1 
2 
3 #include "filmstripcommand.h"
4 #include "tapp.h"
5 #include "toonz/palettecontroller.h"
6 #include "toonz/txshlevelhandle.h"
7 #include "toonz/txsheethandle.h"
8 #include "toonz/tscenehandle.h"
9 #include "toonz/tpalettehandle.h"
10 #include "toonz/tframehandle.h"
11 #include "tinbetween.h"
12 #include "tvectorimage.h"
13 #include "ttoonzimage.h"
14 #include "toonzqt/selection.h"
15 #include "toonzqt/dvdialog.h"
16 #include "drawingdata.h"
17 #include "toonzqt/strokesdata.h"
18 #include "toonzqt/rasterimagedata.h"
19 #include "timagecache.h"
20 #include "tools/toolutils.h"
21 #include "toonzqt/icongenerator.h"
22 
23 #include "tundo.h"
24 #include "toonz/txshsimplelevel.h"
25 #include "toonz/txshchildlevel.h"
26 #include "toonz/txshsoundlevel.h"
27 #include "toonz/txshpalettelevel.h"
28 #include "toonz/txshpalettecolumn.h"
29 #include "toonz/txshsoundcolumn.h"
30 #include "toonz/txsheet.h"
31 #include "toonz/txshcell.h"
32 #include "toonz/toonzscene.h"
33 #include "toonz/levelset.h"
34 #include "toonz/txshleveltypes.h"
35 #include "toonz/toonzimageutils.h"
36 #include "toonz/trasterimageutils.h"
37 #include "toonz/tcamera.h"
38 #include "toonz/preferences.h"
39 #include "trop.h"
40 
41 #include "toonzqt/gutil.h"
42 
43 #include "historytypes.h"
44 
45 #include <QApplication>
46 #include <QClipboard>
47 
48 //=============================================================================
49 
operator +(const TFrameId & fid,int d)50 TFrameId operator+(const TFrameId &fid, int d) {
51   return TFrameId(fid.getNumber() + d, fid.getLetter());
52 }
53 
54 //-----------------------------------------------------------------------------
55 /*
56 void doUpdateXSheet(TXshSimpleLevel *sl, std::vector<TFrameId> oldFids,
57                     std::vector<TFrameId> newFids, TXsheet *xsh,
58                     std::vector<TXshChildLevel *> &childLevels) {
59   for (int c = 0; c < xsh->getColumnCount(); ++c) {
60     int r0, r1;
61     int n = xsh->getCellRange(c, r0, r1);
62     if (n > 0) {
63       bool changed = false;
64       std::vector<TXshCell> cells(n);
65       xsh->getCells(r0, c, n, &cells[0]);
66       for (int i = 0; i < n; i++) {
67         TXshCell currCell = cells[i];
68         // check the sub xsheets too
69         if (!cells[i].isEmpty() &&
70             cells[i].m_level->getType() == CHILD_XSHLEVEL) {
71           TXshChildLevel *level = cells[i].m_level->getChildLevel();
72           // make sure we haven't already checked the level
73           if (level && std::find(childLevels.begin(), childLevels.end(),
74                                  level) == childLevels.end()) {
75             childLevels.push_back(level);
76             TXsheet *subXsh = level->getXsheet();
77             doUpdateXSheet(sl, oldFids, newFids, subXsh, childLevels);
78           }
79         }
80         for (int j = 0; j < oldFids.size(); j++) {
81           if (oldFids.at(j) == newFids.at(j)) continue;
82           TXshCell tempCell(sl, oldFids.at(j));
83           bool sameSl  = tempCell.getSimpleLevel() == currCell.getSimpleLevel();
84           bool sameFid = tempCell.getFrameId() == currCell.getFrameId();
85           if (sameSl && sameFid) {
86             TXshCell newCell(sl, newFids.at(j));
87             cells[i] = newCell;
88             changed  = true;
89             break;
90           }
91         }
92       }
93       if (changed) {
94         xsh->setCells(r0, c, n, &cells[0]);
95         TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
96       }
97     }
98   }
99 }
100 */
101 //-----------------------------------------------------------------------------
102 
updateXSheet(TXshSimpleLevel * sl,std::vector<TFrameId> oldFids,std::vector<TFrameId> newFids)103 static void updateXSheet(TXshSimpleLevel *sl, std::vector<TFrameId> oldFids,
104                          std::vector<TFrameId> newFids) {
105   std::vector<TXshChildLevel *> childLevels;
106   TXsheet *xsh =
107       TApp::instance()->getCurrentScene()->getScene()->getTopXsheet();
108   bool changed =
109       ToolUtils::doUpdateXSheet(sl, oldFids, newFids, xsh, childLevels);
110   if (changed) TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
111 }
112 
113 //=============================================================================
114 // makeSpaceForFid
115 // se necessario renumera gli altri fid del livello in modo che
116 // framesToInsert siano liberi
117 // n.b. modifica
118 //-----------------------------------------------------------------------------
119 
makeSpaceForFids(TXshSimpleLevel * sl,const std::set<TFrameId> & framesToInsert)120 void makeSpaceForFids(TXshSimpleLevel *sl,
121                       const std::set<TFrameId> &framesToInsert) {
122   std::vector<TFrameId> fids;
123   std::vector<TFrameId> oldFids;
124   sl->getFids(fids);
125   sl->getFids(oldFids);
126   std::set<TFrameId>::const_iterator it;
127   std::set<TFrameId> touchedFids;
128   for (it = framesToInsert.begin(); it != framesToInsert.end(); ++it) {
129     // devo inserire fid
130     TFrameId fid(*it);
131     std::vector<TFrameId>::iterator j;
132     // controllo che non ci sia gia'
133     j = fids.begin();
134     while (j = std::find(j, fids.end(), fid), j != fids.end()) {
135       // c'e' gia' un fid. faccio fid -> fid + 1
136       touchedFids.insert(fid);
137       fid = fid + 1;
138       touchedFids.insert(fid);
139       *j = fid;
140       // adesso devo controllare che il nuovo fid non ci sia gia' nella
141       // parte restante dell'array fids
142       ++j;
143     }
144   }
145   if (!touchedFids.empty()) {
146     if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled())
147       updateXSheet(sl, oldFids, fids);
148     sl->renumber(fids);
149     sl->setDirtyFlag(true);
150   }
151 }
152 
153 //=============================================================================
154 namespace {
155 //-----------------------------------------------------------------------------
156 
copyFramesWithoutUndo(TXshSimpleLevel * sl,std::set<TFrameId> & frames)157 void copyFramesWithoutUndo(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
158   QClipboard *clipboard = QApplication::clipboard();
159   TXsheet *xsh          = TApp::instance()->getCurrentXsheet()->getXsheet();
160   DrawingData *data     = new DrawingData();
161   data->setLevelFrames(sl, frames);
162   clipboard->setMimeData(data, QClipboard::Clipboard);
163 }
164 
165 //-----------------------------------------------------------------------------
166 // frames is a selected frames in the film strip
pasteAreasWithoutUndo(const QMimeData * data,TXshSimpleLevel * sl,std::set<TFrameId> & frames,TTileSet ** tileSet,std::map<TFrameId,std::set<int>> & indices)167 bool pasteAreasWithoutUndo(const QMimeData *data, TXshSimpleLevel *sl,
168                            std::set<TFrameId> &frames, TTileSet **tileSet,
169                            std::map<TFrameId, std::set<int>> &indices) {
170   // paste between the same level must keep the palette unchanged
171   // Not emitting PaletteChanged signal can avoid the update of color model
172   bool paletteHasChanged = true;
173 
174   if (const StrokesData *strokesData =
175           dynamic_cast<const StrokesData *>(data)) {
176     std::set<TFrameId>::iterator it;
177     for (it = frames.begin(); it != frames.end(); ++it) {
178       TImageP img = sl->getFrame(*it, true);
179       if (!img) {
180         img = sl->createEmptyFrame();
181         sl->setFrame(*it, img);
182       }
183       TVectorImageP vi = img;
184       TToonzImageP ti  = img;
185       TRasterImageP ri = img;
186       if (vi) {
187         std::set<int> imageIndices;
188         strokesData->getImage(vi, imageIndices, true);
189         indices[*it] = imageIndices;
190       } else if (ti) {
191         ToonzImageData *toonzImageData = strokesData->toToonzImageData(ti);
192         return pasteAreasWithoutUndo(toonzImageData, sl, frames, tileSet,
193                                      indices);
194       } else if (ri) {
195         double dpix, dpiy;
196         ri->getDpi(dpix, dpiy);
197         if (dpix == 0 || dpiy == 0) {
198           TPointD dpi = sl->getScene()->getCurrentCamera()->getDpi();
199           dpix        = dpi.x;
200           dpiy        = dpi.y;
201           ri->setDpi(dpix, dpiy);
202         }
203         FullColorImageData *fullColorImageData =
204             strokesData->toFullColorImageData(ri);
205         return pasteAreasWithoutUndo(fullColorImageData, sl, frames, tileSet,
206                                      indices);
207       }
208     }
209   }
210   // when pasting the copied area selected with the selection tool
211   else if (const RasterImageData *rasterImageData =
212                dynamic_cast<const RasterImageData *>(data)) {
213     std::set<TFrameId>::iterator it;
214     for (it = frames.begin(); it != frames.end(); ++it) {
215       if ((sl->getType() == PLI_XSHLEVEL || sl->getType() == TZP_XSHLEVEL) &&
216           dynamic_cast<const FullColorImageData *>(rasterImageData)) {
217         DVGui::error(QObject::tr(
218             "The copied selection cannot be pasted in the current drawing."));
219         return false;
220       }
221       // obtain the image data to be pasted
222       TImageP img = sl->getFrame(*it, true);
223       if (!img) {
224         img = sl->createEmptyFrame();
225         sl->setFrame(*it, img);
226       }
227       TToonzImageP ti  = img;
228       TRasterImageP ri = img;
229       TVectorImageP vi = img;
230       // pasting TLV
231       if (ti) {
232         TRasterP ras;
233         double dpiX, dpiY;
234         std::vector<TRectD> rects;
235         std::vector<TStroke> strokes;
236         std::vector<TStroke> originalStrokes;
237         TAffine affine;
238 
239         // style will be merged in getData() if the palettes are different
240         int styleCountBeforePasteImage = ti->getPalette()->getStyleCount();
241 
242         rasterImageData->getData(ras, dpiX, dpiY, rects, strokes,
243                                  originalStrokes, affine, ti->getPalette());
244 
245         if (styleCountBeforePasteImage == ti->getPalette()->getStyleCount())
246           paletteHasChanged = false;
247 
248         double imgDpiX, imgDpiY;
249         ti->getDpi(imgDpiX, imgDpiY);
250         TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
251         affine *= sc;
252         int i;
253         TRectD boxD;
254         if (rects.size() > 0) boxD = rects[0];
255         if (strokes.size() > 0) boxD = strokes[0].getBBox();
256         for (i = 0; i < rects.size(); i++) boxD += rects[i];
257         for (i = 0; i < strokes.size(); i++) boxD += strokes[i].getBBox();
258         boxD       = affine * boxD;
259         TRect box  = ToonzImageUtils::convertWorldToRaster(boxD, ti);
260         TPoint pos = box.getP00();
261 
262         if (pos.x < 0) pos.x = 0;
263         if (pos.y < 0) pos.y = 0;
264 
265         if (*tileSet == 0)
266           *tileSet = new TTileSetCM32(ti->getRaster()->getSize());
267         if (box.overlaps(ti->getRaster()->getBounds()))
268           (*tileSet)->add(ti->getRaster(), box);
269         else
270           (*tileSet)->add(0);
271         TRasterCM32P app = ras;
272         if (app) {
273           TRop::over(ti->getRaster(), app, pos, affine);
274           ToolUtils::updateSaveBox(sl, *it);
275         }
276       } else if (ri) {
277         TRasterP ras;
278         double dpiX = 0, dpiY = 0;
279         double imgDpiX = 0, imgDpiY = 0;
280         std::vector<TRectD> rects;
281         std::vector<TStroke> strokes;
282         std::vector<TStroke> originalStrokes;
283         TAffine affine;
284 
285         TPointD cameraDpi;
286 
287         ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
288         if (scene) {
289           TCamera *camera = scene->getCurrentCamera();
290           cameraDpi       = camera->getDpi();
291           if (cameraDpi.x == 0.0 ||
292               cameraDpi.y == 0.0)  // it should never happen. just in case...
293             return false;
294         } else
295           return false;
296 
297         rasterImageData->getData(ras, dpiX, dpiY, rects, strokes,
298                                  originalStrokes, affine, ri->getPalette());
299         if (dpiX == 0 || dpiY == 0) {
300           dpiX = cameraDpi.x;
301           dpiY = cameraDpi.y;
302         }
303 
304         ri->getDpi(imgDpiX, imgDpiY);
305         if (imgDpiX == 0 || imgDpiY == 0) {
306           imgDpiX = cameraDpi.x;
307           imgDpiY = cameraDpi.y;
308         }
309         TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
310         affine *= sc;
311         int i;
312         TRectD boxD;
313         if (rects.size() > 0) boxD = rects[0];
314         if (strokes.size() > 0) boxD = strokes[0].getBBox();
315         for (i = 0; i < rects.size(); i++) boxD += rects[i];
316         for (i = 0; i < strokes.size(); i++) boxD += strokes[i].getBBox();
317         boxD       = affine * boxD;
318         TRect box  = TRasterImageUtils::convertWorldToRaster(boxD, ri);
319         TPoint pos = box.getP00();
320         if (*tileSet == 0)
321           *tileSet = new TTileSetFullColor(ri->getRaster()->getSize());
322         if (box.overlaps(ri->getRaster()->getBounds()))
323           (*tileSet)->add(ri->getRaster(), box);
324         else
325           (*tileSet)->add(0);
326         TRasterCM32P app = ras;
327         if (app)
328           TRop::over(ri->getRaster(), app, ri->getPalette(), pos, affine);
329         else
330           TRop::over(ri->getRaster(), ras, pos, affine);
331       } else if (vi) {
332         StrokesData *strokesData =
333             rasterImageData->toStrokesData(sl->getScene());
334         return pasteAreasWithoutUndo(strokesData, sl, frames, tileSet, indices);
335       }
336     }
337   } else
338     return false;
339 
340   if (paletteHasChanged)
341     TApp::instance()
342         ->getPaletteController()
343         ->getCurrentLevelPalette()
344         ->notifyPaletteChanged();
345 
346   invalidateIcons(sl, frames);
347   sl->setDirtyFlag(true);
348   TApp::instance()->getCurrentLevel()->notifyLevelChange();
349 
350   return true;
351 }
352 
353 //-----------------------------------------------------------------------------
354 // Se insert == true incolla i frames nel livello inserendoli, se necessario
355 // spostando
356 // verso il basso i preesistenti. Altrimenti incolla i frames sostituendoli.
357 // il parametro clone images si mette a false quando questa funzione viene usata
358 // per un undo/redo.
359 
pasteFramesWithoutUndo(const DrawingData * data,TXshSimpleLevel * sl,std::set<TFrameId> & frames,DrawingData::ImageSetType setType,bool cloneImages,bool & keepOriginalPalette,bool isRedo=false)360 bool pasteFramesWithoutUndo(const DrawingData *data, TXshSimpleLevel *sl,
361                             std::set<TFrameId> &frames,
362                             DrawingData::ImageSetType setType, bool cloneImages,
363                             bool &keepOriginalPalette, bool isRedo = false) {
364   if (!data || (frames.empty() && setType != DrawingData::OVER_FRAMEID))
365     return false;
366 
367   bool isPaste = data->getLevelFrames(sl, frames, setType, cloneImages,
368                                       keepOriginalPalette, isRedo);
369   if (!isPaste) return false;
370 
371   if (keepOriginalPalette)
372     invalidateIcons(sl, frames);
373   else {
374     std::vector<TFrameId> sl_fids;
375     sl->getFids(sl_fids);
376     invalidateIcons(sl, sl_fids);
377   }
378   sl->setDirtyFlag(true);
379   TApp::instance()->getCurrentLevel()->notifyLevelChange();
380   TApp::instance()
381       ->getPaletteController()
382       ->getCurrentLevelPalette()
383       ->notifyPaletteChanged();
384 
385   return true;
386 }
387 
388 //-----------------------------------------------------------------------------
389 
390 // "Svuota" i frames: i frames vengono buttati e al loro posto
391 // vengono inseriti frames vuoti.
clearFramesWithoutUndo(const TXshSimpleLevelP & sl,const std::set<TFrameId> & frames)392 std::map<TFrameId, QString> clearFramesWithoutUndo(
393     const TXshSimpleLevelP &sl, const std::set<TFrameId> &frames) {
394   std::map<TFrameId, QString> clearedFrames;
395   if (!sl || frames.empty()) return clearedFrames;
396 
397   std::set<TFrameId>::const_iterator it;
398   for (it = frames.begin(); it != frames.end(); ++it) {
399     TFrameId frameId = *it;
400     /* UINT にキャストしたらだめだろ */
401     // QString id =
402     // "clearFrames"+QString::number((UINT)sl.getPointer())+"-"+QString::number(it->getNumber());
403     QString id = "clearFrames" + QString::number((uintptr_t)sl.getPointer()) +
404                  "-" + QString::number(it->getNumber());
405     TImageCache::instance()->add(id, sl->getFrame(frameId, false));
406     clearedFrames[frameId] = id;
407     // empty frame must be created BEFORE erasing frame or it may initialize
408     // palette.
409     TImageP emptyFrame = sl->createEmptyFrame();
410     sl->eraseFrame(frameId);
411     sl->setFrame(*it, emptyFrame);
412   }
413   invalidateIcons(sl.getPointer(), frames);
414   TApp::instance()->getCurrentLevel()->notifyLevelChange();
415   sl->setDirtyFlag(true);
416   return clearedFrames;
417 }
418 
419 //-----------------------------------------------------------------------------
420 
421 // Rimuove i frames dal livello
removeFramesWithoutUndo(const TXshSimpleLevelP & sl,const std::set<TFrameId> & frames)422 void removeFramesWithoutUndo(const TXshSimpleLevelP &sl,
423                              const std::set<TFrameId> &frames) {
424   if (!sl || frames.empty()) return;
425   std::set<TFrameId>::const_iterator it;
426   for (it = frames.begin(); it != frames.end(); ++it) sl->eraseFrame(*it);
427   removeIcons(sl.getPointer(), frames);
428   sl->setDirtyFlag(true);
429   TApp::instance()->getCurrentLevel()->notifyLevelChange();
430 }
431 
432 //-----------------------------------------------------------------------------
433 
cutFramesWithoutUndo(TXshSimpleLevel * sl,std::set<TFrameId> & frames)434 void cutFramesWithoutUndo(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
435   std::map<TFrameId, QString> imageSet;
436 
437   HookSet *levelHooks   = sl->getHookSet();
438   int currentFrameIndex = TApp::instance()->getCurrentFrame()->getFrameIndex();
439   std::set<TFrameId>::const_iterator it;
440   int i = 0;
441   for (it = frames.begin(); it != frames.end(); ++it, i++) {
442     TFrameId frameId = *it;
443     // QString id =
444     // "cutFrames"+QString::number((UINT)sl)+"-"+QString::number(it->getNumber());
445     QString id = "cutFrames" + QString::number((uintptr_t)sl) + "-" +
446                  QString::number(it->getNumber());
447     TImageCache::instance()->add(id, sl->getFrame(frameId, false));
448     imageSet[frameId] = id;
449   }
450   removeIcons(sl, frames);
451 
452   sl->setDirtyFlag(true);
453 
454   QClipboard *clipboard = QApplication::clipboard();
455   DrawingData *data     = new DrawingData();
456   data->setFrames(imageSet, sl, *levelHooks);
457   clipboard->setMimeData(data, QClipboard::Clipboard);
458 
459   for (it = frames.begin(); it != frames.end(); ++it, i++) {
460     sl->eraseFrame(*it);
461   }
462 
463   std::vector<TFrameId> newFids;
464   sl->getFids(newFids);
465   // Devo settare i nuovi fids al frame handle prima di mandare la notifica di
466   // cambiamento del livello
467   TApp::instance()->getCurrentFrame()->setFrameIds(newFids);
468   TApp::instance()->getCurrentFrame()->setFrameIndex(currentFrameIndex);
469   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
470   sl->setDirtyFlag(true);
471   TApp::instance()->getCurrentLevel()->notifyLevelChange();
472 }
473 
474 //-----------------------------------------------------------------------------
475 
insertNotEmptyframes(const TXshSimpleLevelP & sl,const std::map<TFrameId,QString> & framesToInsert)476 void insertNotEmptyframes(const TXshSimpleLevelP &sl,
477                           const std::map<TFrameId, QString> &framesToInsert) {
478   if (framesToInsert.empty() || !sl) return;
479   std::vector<TFrameId> fids;
480   sl->getFids(fids);
481   std::set<TFrameId> frames;
482   for (auto const &frame : framesToInsert) {
483     frames.insert(frame.first);
484   }
485   makeSpaceForFids(sl.getPointer(), frames);
486 
487   for (auto const &frame : framesToInsert) {
488     TImageP img = TImageCache::instance()->get(frame.second, false);
489     TImageCache::instance()->remove(frame.second);
490     assert(img);
491     sl->setFrame(frame.first, img);
492   }
493   invalidateIcons(sl.getPointer(), frames);
494   sl->setDirtyFlag(true);
495   TApp::instance()->getCurrentLevel()->notifyLevelChange();
496 }
497 
498 //=============================================================================
499 // PasteRasterAreasUndo
500 //-----------------------------------------------------------------------------
501 
502 class PasteRasterAreasUndo final : public TUndo {
503   TXshSimpleLevelP m_level;
504   std::set<TFrameId> m_frames;
505   TTileSet *m_tiles;
506   RasterImageData *m_data;
507   TPaletteP m_oldPalette;
508   TPaletteP m_newPalette;
509   bool m_isFrameInserted;
510 
511 public:
PasteRasterAreasUndo(TXshSimpleLevel * sl,const std::set<TFrameId> & frames,TTileSet * tiles,RasterImageData * data,TPalette * plt,bool isFrameInserted)512   PasteRasterAreasUndo(TXshSimpleLevel *sl, const std::set<TFrameId> &frames,
513                        TTileSet *tiles, RasterImageData *data, TPalette *plt,
514                        bool isFrameInserted)
515       : TUndo()
516       , m_level(sl)
517       , m_frames(frames)
518       , m_tiles(tiles)
519       , m_oldPalette(plt->clone())
520       , m_isFrameInserted(isFrameInserted) {
521     m_data = data->clone();
522     assert(m_tiles->getTileCount() == m_frames.size());
523   }
524 
~PasteRasterAreasUndo()525   ~PasteRasterAreasUndo() {
526     if (m_tiles) delete m_tiles;
527     if (m_data) delete m_data;
528   }
529 
onAdd()530   void onAdd() override { m_newPalette = m_level->getPalette()->clone(); }
531 
undo() const532   void undo() const override {
533     if (!m_level || m_frames.empty()) return;
534     std::set<TFrameId> frames = m_frames;
535 
536     if (m_isFrameInserted) {
537       // Faccio remove dei frame incollati
538       removeFramesWithoutUndo(m_level, frames);
539     } else {
540       std::set<TFrameId>::const_iterator it;
541       int i                        = 0;
542       TTileSetCM32 *tileSetCM      = dynamic_cast<TTileSetCM32 *>(m_tiles);
543       TTileSetFullColor *tileSetFC = dynamic_cast<TTileSetFullColor *>(m_tiles);
544       for (it = m_frames.begin(); it != m_frames.end(); it++, i++) {
545         TImageP image = m_level->getFrame(*it, true);
546         if (!image) continue;
547         TRasterImageP ri(image);
548         TToonzImageP ti(image);
549         if (tileSetCM) {
550           const TTileSetCM32::Tile *tile = tileSetCM->getTile(i);
551           if (!tile) continue;
552           TRasterCM32P tileRas;
553           tile->getRaster(tileRas);
554           assert(ti);
555           ti->getRaster()->copy(tileRas, tile->m_rasterBounds.getP00());
556           ToolUtils::updateSaveBox(m_level, *it);
557         } else if (tileSetFC) {
558           const TTileSetFullColor::Tile *tile = tileSetFC->getTile(i);
559           if (!tile) continue;
560           TRasterP tileRas;
561           tile->getRaster(tileRas);
562           assert(ri);
563           ri->getRaster()->copy(tileRas, tile->m_rasterBounds.getP00());
564         }
565       }
566     }
567     // Setto la vecchia paletta al livello
568     if (m_oldPalette.getPointer()) {
569       m_level->getPalette()->assign(m_oldPalette->clone());
570       TApp::instance()
571           ->getPaletteController()
572           ->getCurrentLevelPalette()
573           ->notifyPaletteChanged();
574     }
575 
576     invalidateIcons(m_level.getPointer(), m_frames);
577     TApp::instance()->getCurrentLevel()->notifyLevelChange();
578   }
579 
redo() const580   void redo() const override {
581     if (!m_level || m_frames.empty()) return;
582     if (m_isFrameInserted) {
583       assert(m_frames.size() == 1);
584       TImageP img = m_level->createEmptyFrame();
585       m_level->setFrame(*m_frames.begin(), img);
586     }
587 
588     std::set<TFrameId> frames = m_frames;
589 
590     std::set<TFrameId>::const_iterator it;
591     for (it = m_frames.begin(); it != m_frames.end(); it++) {
592       TImageP image    = m_level->getFrame(*it, true);
593       TRasterImageP ri = image;
594       TToonzImageP ti  = image;
595       if (ti) {
596         TRasterP ras;
597         double dpiX, dpiY;
598         std::vector<TRectD> rects;
599         std::vector<TStroke> strokes;
600         std::vector<TStroke> originalStrokes;
601         TAffine affine;
602         m_data->getData(ras, dpiX, dpiY, rects, strokes, originalStrokes,
603                         affine, ti->getPalette());
604         double imgDpiX, imgDpiY;
605         ti->getDpi(imgDpiX, imgDpiY);
606         TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
607         affine *= sc;
608 
609         int i;
610         TRectD boxD;
611         if (rects.size() > 0) boxD = rects[0];
612         if (strokes.size() > 0) boxD = strokes[0].getBBox();
613         for (i = 0; i < rects.size(); i++) boxD += rects[i];
614         for (i = 0; i < strokes.size(); i++) boxD += strokes[i].getBBox();
615         boxD             = affine * boxD;
616         TRect box        = ToonzImageUtils::convertWorldToRaster(boxD, ti);
617         TPoint pos       = box.getP00();
618         TRasterCM32P app = ras;
619         TRop::over(ti->getRaster(), app, pos, affine);
620         ToolUtils::updateSaveBox(m_level, *it);
621       } else if (ri) {
622         TRasterP ras;
623         double dpiX, dpiY;
624         std::vector<TRectD> rects;
625         std::vector<TStroke> strokes;
626         std::vector<TStroke> originalStrokes;
627         TAffine affine;
628         m_data->getData(ras, dpiX, dpiY, rects, strokes, originalStrokes,
629                         affine, ri->getPalette());
630         double imgDpiX, imgDpiY;
631         if (dpiX == 0 && dpiY == 0) {
632           ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
633           if (scene) {
634             TCamera *camera = scene->getCurrentCamera();
635             TPointD dpi     = camera->getDpi();
636             dpiX            = dpi.x;
637             dpiY            = dpi.y;
638           } else
639             return;
640         }
641         ri->getDpi(imgDpiX, imgDpiY);
642         TScale sc(imgDpiX / dpiX, imgDpiY / dpiY);
643         affine *= sc;
644         int i;
645         TRectD boxD;
646         if (rects.size() > 0) boxD = rects[0];
647         if (strokes.size() > 0) boxD = strokes[0].getBBox();
648         for (i = 0; i < rects.size(); i++) boxD += rects[i];
649         for (i = 0; i < strokes.size(); i++) boxD += strokes[i].getBBox();
650         boxD             = affine * boxD;
651         TRect box        = TRasterImageUtils::convertWorldToRaster(boxD, ri);
652         TPoint pos       = box.getP00();
653         TRasterCM32P app = ras;
654         if (app)
655           TRop::over(ri->getRaster(), app, ri->getPalette(), pos, affine);
656         else
657           TRop::over(ri->getRaster(), ras, pos, affine);
658       }
659     }
660 
661     if (m_newPalette.getPointer()) {
662       m_level->getPalette()->assign(m_newPalette->clone());
663       TApp::instance()
664           ->getPaletteController()
665           ->getCurrentLevelPalette()
666           ->notifyPaletteChanged();
667     }
668 
669     invalidateIcons(m_level.getPointer(), m_frames);
670     TApp::instance()->getCurrentLevel()->notifyLevelChange();
671   }
672 
getSize() const673   int getSize() const override {
674     return sizeof(*this) + sizeof(*m_data) + sizeof(*m_tiles);
675   }
676 
getHistoryString()677   QString getHistoryString() override {
678     QString str = QObject::tr("Paste  : Level %1 : Frame ")
679                       .arg(QString::fromStdWString(m_level->getName()));
680 
681     std::set<TFrameId>::const_iterator it;
682     for (it = m_frames.begin(); it != m_frames.end(); it++) {
683       if (it != m_frames.begin()) str += QString(", ");
684       str += QString::number((*it).getNumber());
685     }
686 
687     return str;
688   }
getHistoryType()689   int getHistoryType() override { return HistoryType::FilmStrip; }
690 };
691 
692 //=============================================================================
693 // PasteVectorAreasUndo
694 //-----------------------------------------------------------------------------
695 
696 class PasteVectorAreasUndo final : public TUndo {
697   TXshSimpleLevelP m_level;
698   std::set<TFrameId> m_frames;
699   std::map<TFrameId, std::set<int>> m_indices;
700   StrokesData *m_data;
701   TPaletteP m_oldPalette;
702   TPaletteP m_newPalette;
703   bool m_isFrameInserted;
704 
705 public:
PasteVectorAreasUndo(TXshSimpleLevel * sl,const std::set<TFrameId> & frames,std::map<TFrameId,std::set<int>> & indices,StrokesData * data,TPalette * plt,bool isFrameInserted)706   PasteVectorAreasUndo(TXshSimpleLevel *sl, const std::set<TFrameId> &frames,
707                        std::map<TFrameId, std::set<int>> &indices,
708                        StrokesData *data, TPalette *plt, bool isFrameInserted)
709       : TUndo()
710       , m_level(sl)
711       , m_frames(frames)
712       , m_indices(indices)
713       , m_oldPalette(plt->clone())
714       , m_isFrameInserted(isFrameInserted) {
715     m_data = data->clone();
716   }
717 
~PasteVectorAreasUndo()718   ~PasteVectorAreasUndo() {
719     if (m_data) delete m_data;
720   }
721 
onAdd()722   void onAdd() override { m_newPalette = m_level->getPalette()->clone(); }
723 
undo() const724   void undo() const override {
725     if (!m_level || m_frames.empty()) return;
726 
727     std::set<TFrameId> frames = m_frames;
728 
729     if (m_isFrameInserted) {
730       // Faccio remove dei frame incollati
731       removeFramesWithoutUndo(m_level, frames);
732     } else {
733       std::set<TFrameId>::const_iterator it;
734       for (it = m_frames.begin(); it != m_frames.end(); it++) {
735         TVectorImageP img = m_level->getFrame(*it, true);
736         assert(img);
737         if (!img) continue;
738         std::map<TFrameId, std::set<int>>::const_iterator mapIt =
739             m_indices.find(*it);
740         if (mapIt == m_indices.end()) continue;
741         std::set<int> imageIndices = mapIt->second;
742         ;
743         std::set<int>::const_iterator it2 = imageIndices.end();
744         while (it2 != imageIndices.begin()) {
745           it2--;
746           img->removeStroke(*it2);
747         }
748       }
749     }
750 
751     // Setto la vecchia paletta al livello
752     if (m_oldPalette.getPointer()) {
753       m_level->getPalette()->assign(m_oldPalette->clone());
754       TApp::instance()
755           ->getPaletteController()
756           ->getCurrentLevelPalette()
757           ->notifyPaletteChanged();
758     }
759 
760     invalidateIcons(m_level.getPointer(), m_frames);
761     TApp::instance()->getCurrentLevel()->notifyLevelChange();
762   }
763 
redo() const764   void redo() const override {
765     if (!m_level || m_frames.empty()) return;
766 
767     if (m_isFrameInserted) {
768       assert(m_frames.size() == 1);
769       TVectorImageP img = m_level->createEmptyFrame();
770       m_level->setFrame(*m_frames.begin(), img);
771     }
772 
773     std::set<TFrameId> frames = m_frames;
774     std::set<TFrameId>::const_iterator it;
775     for (it = m_frames.begin(); it != m_frames.end(); it++) {
776       TVectorImageP img = m_level->getFrame(*it, true);
777       assert(img);
778       if (!img) continue;
779       std::set<int> app;
780       m_data->getImage(img, app, true);
781     }
782 
783     if (m_newPalette.getPointer()) {
784       m_level->getPalette()->assign(m_newPalette->clone());
785       TApp::instance()
786           ->getPaletteController()
787           ->getCurrentLevelPalette()
788           ->notifyPaletteChanged();
789     }
790 
791     invalidateIcons(m_level.getPointer(), m_frames);
792     TApp::instance()->getCurrentLevel()->notifyLevelChange();
793   }
794 
getSize() const795   int getSize() const override { return sizeof(*this) + sizeof(*m_data); }
796 
getHistoryString()797   QString getHistoryString() override {
798     QString str = QObject::tr("Paste  : Level %1 : Frame ")
799                       .arg(QString::fromStdWString(m_level->getName()));
800 
801     std::set<TFrameId>::const_iterator it;
802     for (it = m_frames.begin(); it != m_frames.end(); it++) {
803       if (it != m_frames.begin()) str += QString(", ");
804       str += QString::number((*it).getNumber());
805     }
806 
807     return str;
808   }
getHistoryType()809   int getHistoryType() override { return HistoryType::FilmStrip; }
810 };
811 
812 /*//=============================================================================
813 // PasteAreasUndo
814 //-----------------------------------------------------------------------------
815 
816 class PasteAreasUndo final : public TUndo
817 {
818   TXshSimpleLevelP m_level;
819   std::set<TFrameId> m_frames;
820         TPaletteP m_oldPalette;
821         TPaletteP m_newPalette;
822         bool m_isFrameInserted;
823 
824 public:
825   PasteAreasUndo(TXshSimpleLevel *sl, const std::set<TFrameId> &frames, bool
826 isFrameInserted)
827       : TUndo()
828       , m_level(sl)
829                         , m_frames(frames)
830                         , m_isFrameInserted(isFrameInserted)
831         {
832                 m_oldPalette = m_level->getPalette()->clone();
833                 if(!m_isFrameInserted)
834                 {
835                         std::set<TFrameId>::iterator it;
836                         for(it=m_frames.begin(); it!=m_frames.end(); it++)
837                         {
838                                 TImageP img = m_level->getFrame(*it,true);
839                                 TImageCache::instance()->add("PasteAreasUndoOld"+QString::number((UINT)this)+QString::number((*it).getNumber()),
840 img->cloneImage());
841                         }
842                 }
843   }
844 
845         void onAdd()
846         {
847                 m_newPalette = m_level->getPalette()->clone();
848                 std::set<TFrameId>::iterator it;
849                 for(it=m_frames.begin(); it!=m_frames.end(); it++)
850                 {
851                         TImageP img = m_level->getFrame(*it,true);
852                         TImageCache::instance()->add("PasteAreasUndoNew"+QString::number((UINT)this)+QString::number((*it).getNumber()),
853 img->cloneImage());
854                 }
855         }
856 
857   ~PasteAreasUndo() {
858                 std::set<TFrameId>::const_iterator it;
859 
860                 if(!m_isFrameInserted)
861                 {
862                         for(it=m_frames.begin(); it!=m_frames.end(); it++)
863                                 TImageCache::instance()->remove("PasteAreasUndoOld"+QString::number((UINT)this)+QString::number((*it).getNumber()));
864                 }
865                 for(it=m_frames.begin(); it!=m_frames.end(); it++)
866                         TImageCache::instance()->remove("PasteAreasUndoNew"+QString::number((UINT)this)+QString::number((*it).getNumber()));
867   }
868 
869         void undo() const
870         {
871                 std::set<TFrameId> frames = m_frames;
872 
873                 if(m_isFrameInserted)
874                 {
875                         // Faccio remove dei frame incollati
876                         removeFramesWithoutUndo(m_level, frames);
877                 }
878                 else
879                 {
880                         std::set<TFrameId>::const_iterator it;
881                         for(it=m_frames.begin(); it!=m_frames.end(); it++)
882                         {
883                                 TImageP img =
884 TImageCache::instance()->get("PasteAreasUndoOld"+QString::number((UINT)this)+QString::number((*it).getNumber()),true);
885                                 assert(img);
886                                 m_level->setFrame(*it,img);
887                         }
888                 }
889 
890                 //Setto la vecchia paletta al livello
891                 if(m_oldPalette.getPointer())
892                 {
893                         m_level->getPalette()->assign(m_oldPalette->clone());
894       TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
895                 }
896 
897     invalidateIcons(m_level.getPointer(), m_frames);
898                 TApp::instance()->getCurrentLevel()->notifyLevelChange();
899   }
900 
901   void redo() const
902         {
903                 if(!m_level || m_frames.empty()) return;
904 
905     std::set<TFrameId> frames = m_frames;
906 
907                 std::set<TFrameId>::const_iterator it;
908                 for(it=m_frames.begin(); it!=m_frames.end(); it++)
909                 {
910                         TImageP img =
911 TImageCache::instance()->get("PasteAreasUndoNew"+QString::number((UINT)this)+QString::number((*it).getNumber()),true);
912                         if(!img) continue;
913                         m_level->setFrame(*it, img, true);
914                 }
915 
916                 if(m_newPalette.getPointer())
917                 {
918       m_level->getPalette()->assign(m_newPalette->clone());
919       TApp::instance()->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged();
920                 }
921 
922                 invalidateIcons(m_level.getPointer(), m_frames);
923                 TApp::instance()->getCurrentLevel()->notifyLevelChange();
924   }
925 
926   int getSize() const{
927     return sizeof(*this);
928   }
929 };*/
930 
931 //=============================================================================
932 // PasteFramesUndo
933 //-----------------------------------------------------------------------------
934 
935 class PasteFramesUndo final : public TUndo {
936   TXshSimpleLevelP m_sl;
937   std::set<TFrameId> m_frames;
938   std::vector<TFrameId> m_oldLevelFrameId;
939   TPaletteP m_oldPalette;
940   DrawingData *m_oldData;
941   DrawingData *m_newData;
942   DrawingData::ImageSetType m_setType;
943   HookSet *m_oldLevelHooks;
944   bool m_updateXSheet;
945   bool m_keepOriginalPalette;
946 
947 public:
PasteFramesUndo(TXshSimpleLevel * sl,const std::set<TFrameId> & frames,const std::vector<TFrameId> & oldLevelFrameId,TPaletteP oldPalette,DrawingData::ImageSetType setType,HookSet * oldLevelHooks,bool keepOriginalPalette,DrawingData * oldData=0)948   PasteFramesUndo(TXshSimpleLevel *sl, const std::set<TFrameId> &frames,
949                   const std::vector<TFrameId> &oldLevelFrameId,
950                   TPaletteP oldPalette, DrawingData::ImageSetType setType,
951                   HookSet *oldLevelHooks, bool keepOriginalPalette,
952                   DrawingData *oldData = 0)
953       : m_sl(sl)
954       , m_frames(frames)
955       , m_oldLevelFrameId(oldLevelFrameId)
956       , m_oldPalette(oldPalette)
957       , m_setType(setType)
958       , m_keepOriginalPalette(keepOriginalPalette)
959       , m_oldData(oldData)
960       , m_oldLevelHooks(oldLevelHooks) {
961     QClipboard *clipboard = QApplication::clipboard();
962     QMimeData *data       = cloneData(clipboard->mimeData());
963     m_newData             = dynamic_cast<DrawingData *>(data);
964     assert(m_newData);
965     m_updateXSheet =
966         Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled();
967   }
968 
~PasteFramesUndo()969   ~PasteFramesUndo() {
970     if (m_oldData) m_oldData->releaseData();
971     if (m_newData) m_newData->releaseData();
972   }
973 
undo() const974   void undo() const override {
975     TSelection *selection = TSelection::getCurrent();
976     if (selection) selection->selectNone();
977     std::set<TFrameId> frames = m_frames;
978 
979     // Faccio remove dei frame incollati
980     if (m_setType != DrawingData::OVER_SELECTION)
981       removeFramesWithoutUndo(m_sl, frames);
982 
983     // Renumero i frame con i vecchi fids
984     if (m_setType == DrawingData::INSERT) {
985       assert(m_sl->getFrameCount() == m_oldLevelFrameId.size());
986       if (m_updateXSheet) {
987         std::vector<TFrameId> newFrames;
988         m_sl->getFids(newFrames);
989         updateXSheet(m_sl.getPointer(), newFrames, m_oldLevelFrameId);
990       }
991       m_sl->renumber(m_oldLevelFrameId);
992       m_sl->setDirtyFlag(true);
993       TApp::instance()
994           ->getPaletteController()
995           ->getCurrentLevelPalette()
996           ->notifyPaletteChanged();
997       TApp::instance()->getCurrentLevel()->notifyLevelChange();
998     }
999 
1000     // Reinserisco i vecchi frame che ho sovrascritto
1001     if (m_setType != DrawingData::INSERT) {
1002       std::set<TFrameId> framesToModify;
1003       m_oldData->getFrames(framesToModify);
1004 
1005       bool dummy = true;
1006       // Incollo i frames sovrascrivendoli
1007       pasteFramesWithoutUndo(m_oldData, m_sl.getPointer(), framesToModify,
1008                              DrawingData::OVER_SELECTION, true, dummy);
1009     }
1010 
1011     // Setto la vecchia paletta al livello
1012     if (m_oldPalette.getPointer()) {
1013       TPalette *levelPalette = m_sl->getPalette();
1014       if (levelPalette) levelPalette->assign(m_oldPalette.getPointer());
1015 
1016       TApp *app = TApp::instance();
1017       if (app->getCurrentLevel()->getLevel() == m_sl.getPointer())
1018         app->getPaletteController()
1019             ->getCurrentLevelPalette()
1020             ->notifyPaletteChanged();
1021     }
1022 
1023     // update all icons
1024     std::vector<TFrameId> sl_fids;
1025     m_sl.getPointer()->getFids(sl_fids);
1026     invalidateIcons(m_sl.getPointer(), sl_fids);
1027 
1028     *m_sl->getHookSet() = *m_oldLevelHooks;
1029   }
1030 
redo() const1031   void redo() const override {
1032     if (!m_sl || m_frames.empty()) return;
1033     TSelection *selection = TSelection::getCurrent();
1034     if (selection) selection->selectNone();
1035     std::set<TFrameId> frames = m_frames;
1036 
1037     bool keepOriginalPalette = m_keepOriginalPalette;
1038     pasteFramesWithoutUndo(m_newData, m_sl.getPointer(), frames, m_setType,
1039                            true, keepOriginalPalette, true);
1040   }
1041 
getSize() const1042   int getSize() const override { return sizeof(*this); }
1043 
getHistoryString()1044   QString getHistoryString() override {
1045     QString str = QObject::tr("Paste  : Level %1 : Frame ")
1046                       .arg(QString::fromStdWString(m_sl->getName()));
1047 
1048     std::set<TFrameId>::const_iterator it;
1049     for (it = m_frames.begin(); it != m_frames.end(); it++) {
1050       if (it != m_frames.begin()) str += QString(", ");
1051       str += QString::number((*it).getNumber());
1052     }
1053 
1054     return str;
1055   }
getHistoryType()1056   int getHistoryType() override { return HistoryType::FilmStrip; }
1057 };
1058 
1059 //=============================================================================
1060 // DeleteFramesUndo
1061 //-----------------------------------------------------------------------------
1062 
1063 class DeleteFramesUndo final : public TUndo {
1064   TXshSimpleLevel *m_sl;
1065   std::set<TFrameId> m_frames;
1066   DrawingData *m_oldData;
1067   DrawingData *m_newData;
1068 
1069 public:
DeleteFramesUndo(TXshSimpleLevel * sl,std::set<TFrameId> & frames,DrawingData * oldData,DrawingData * newData)1070   DeleteFramesUndo(TXshSimpleLevel *sl, std::set<TFrameId> &frames,
1071                    DrawingData *oldData, DrawingData *newData)
1072       : m_sl(sl), m_frames(frames), m_oldData(oldData), m_newData(newData) {}
1073 
~DeleteFramesUndo()1074   ~DeleteFramesUndo() {
1075     if (m_oldData) m_oldData->releaseData();
1076     if (m_newData) m_newData->releaseData();
1077   }
1078 
pasteFramesFromData(const DrawingData * data) const1079   void pasteFramesFromData(const DrawingData *data) const {
1080     std::set<TFrameId> frames = m_frames;
1081 
1082     bool dummy = true;
1083     // Incollo i frames sovrascrivendoli
1084     pasteFramesWithoutUndo(data, m_sl, frames, DrawingData::OVER_SELECTION,
1085                            true, dummy);
1086   }
1087 
undo() const1088   void undo() const override { pasteFramesFromData(m_oldData); }
1089 
1090   // OSS.: Non posso usare il metodo "clearFramesWithoutUndo(...)" perche'
1091   //  genera un NUOVO frame vuoto, perdendo quello precedente e le eventuali
1092   //  modifiche che ad esso possono essere state fatte successivamente.
redo() const1093   void redo() const override { pasteFramesFromData(m_newData); }
1094 
getSize() const1095   int getSize() const override { return sizeof(*this); }
1096 
getHistoryString()1097   QString getHistoryString() override {
1098     QString str = QObject::tr("Delete Frames  : Level %1 : Frame ")
1099                       .arg(QString::fromStdWString(m_sl->getName()));
1100 
1101     std::set<TFrameId>::const_iterator it;
1102     for (it = m_frames.begin(); it != m_frames.end(); it++) {
1103       if (it != m_frames.begin()) str += QString(", ");
1104       str += QString::number((*it).getNumber());
1105     }
1106 
1107     return str;
1108   }
getHistoryType()1109   int getHistoryType() override { return HistoryType::FilmStrip; }
1110 };
1111 
1112 //=============================================================================
1113 
1114 //-----------------------------------------------------------------------------
1115 
1116 class CutFramesUndo final : public TUndo {
1117   TXshSimpleLevel *m_sl;
1118   std::set<TFrameId> m_framesCutted;
1119   std::vector<TFrameId> m_oldFrames;
1120   DrawingData *m_newData;
1121 
1122 public:
CutFramesUndo(TXshSimpleLevel * sl,std::set<TFrameId> & framesCutted,std::vector<TFrameId> & oldFrames)1123   CutFramesUndo(TXshSimpleLevel *sl, std::set<TFrameId> &framesCutted,
1124                 std::vector<TFrameId> &oldFrames)
1125       : m_sl(sl), m_framesCutted(framesCutted), m_oldFrames(oldFrames) {
1126     QClipboard *clipboard = QApplication::clipboard();
1127     QMimeData *data       = cloneData(clipboard->mimeData());
1128     m_newData             = dynamic_cast<DrawingData *>(data);
1129     assert(m_newData);
1130   }
1131 
~CutFramesUndo()1132   ~CutFramesUndo() {
1133     if (m_newData) m_newData->releaseData();
1134   }
1135 
undo() const1136   void undo() const override {
1137     std::set<TFrameId> frames = m_framesCutted;
1138     bool dummy                = true;
1139     pasteFramesWithoutUndo(m_newData, m_sl, frames, DrawingData::OVER_SELECTION,
1140                            true, dummy);
1141     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1142   }
1143 
redo() const1144   void redo() const override {
1145     // Prendo il clipboard corrente.
1146     QClipboard *clipboard  = QApplication::clipboard();
1147     QMimeData *currentData = cloneData(clipboard->mimeData());
1148 
1149     std::set<TFrameId> frames = m_framesCutted;
1150     cutFramesWithoutUndo(m_sl, frames);
1151 
1152     // Setto il clipboard corrente
1153     clipboard->setMimeData(currentData, QClipboard::Clipboard);
1154   }
1155 
getSize() const1156   int getSize() const override { return sizeof(*this); }
1157 
getHistoryString()1158   QString getHistoryString() override {
1159     QString str = QObject::tr("Cut Frames  : Level %1 : Frame ")
1160                       .arg(QString::fromStdWString(m_sl->getName()));
1161 
1162     std::set<TFrameId>::const_iterator it;
1163     for (it = m_framesCutted.begin(); it != m_framesCutted.end(); it++) {
1164       if (it != m_framesCutted.begin()) str += QString(", ");
1165       str += QString::number((*it).getNumber());
1166     }
1167 
1168     return str;
1169   }
getHistoryType()1170   int getHistoryType() override { return HistoryType::FilmStrip; }
1171 };
1172 
1173 }  // namespace
1174 
1175 //=============================================================================
1176 
1177 //-----------------------------------------------------------------------------
1178 
1179 namespace {
1180 //-----------------------------------------------------------------------------
1181 
1182 class AddFramesUndo final : public TUndo {
1183   TXshSimpleLevelP m_level;
1184   std::set<TFrameId> m_insertedFids;
1185   std::vector<TFrameId> m_oldFids;
1186   bool m_updateXSheet;
1187 
1188 public:
AddFramesUndo(const TXshSimpleLevelP & level,const std::set<TFrameId> insertedFids,std::vector<TFrameId> oldFids)1189   AddFramesUndo(const TXshSimpleLevelP &level,
1190                 const std::set<TFrameId> insertedFids,
1191                 std::vector<TFrameId> oldFids)
1192       : m_level(level), m_insertedFids(insertedFids), m_oldFids(oldFids) {
1193     m_updateXSheet =
1194         Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled();
1195   }
1196 
undo() const1197   void undo() const override {
1198     removeFramesWithoutUndo(m_level, m_insertedFids);
1199     if (m_updateXSheet) {
1200       std::vector<TFrameId> newFrames;
1201       m_level->getFids(newFrames);
1202       updateXSheet(m_level.getPointer(), newFrames, m_oldFids);
1203     }
1204     m_level->renumber(m_oldFids);
1205     m_level->setDirtyFlag(true);
1206 
1207     TApp *app = TApp::instance();
1208     app->getCurrentScene()->setDirtyFlag(true);
1209     app->getCurrentLevel()->notifyLevelChange();
1210   }
1211 
redo() const1212   void redo() const override {
1213     makeSpaceForFids(m_level.getPointer(), m_insertedFids);
1214 
1215     for (auto const &fid : m_insertedFids) {
1216       m_level->setFrame(fid, m_level->createEmptyFrame());
1217       IconGenerator::instance()->invalidate(m_level.getPointer(), fid);
1218     }
1219     m_level->setDirtyFlag(true);
1220 
1221     TApp *app = TApp::instance();
1222     app->getCurrentScene()->setDirtyFlag(true);
1223     app->getCurrentLevel()->notifyLevelChange();
1224   }
1225 
getSize() const1226   int getSize() const override { return sizeof(*this); }
1227 
getHistoryString()1228   QString getHistoryString() override {
1229     QString str = QObject::tr("Add Frames  : Level %1 : Frame ")
1230                       .arg(QString::fromStdWString(m_level->getName()));
1231 
1232     std::set<TFrameId>::const_iterator it;
1233     for (it = m_insertedFids.begin(); it != m_insertedFids.end(); it++) {
1234       if (it != m_insertedFids.begin()) str += QString(", ");
1235       str += QString::number((*it).getNumber());
1236     }
1237 
1238     return str;
1239   }
getHistoryType()1240   int getHistoryType() override { return HistoryType::FilmStrip; }
1241 };
1242 
1243 }  // namespace
1244 
1245 //=============================================================================
1246 // AddFrames
1247 //-----------------------------------------------------------------------------
1248 
addFrames(TXshSimpleLevel * sl,int start,int end,int step)1249 void FilmstripCmd::addFrames(TXshSimpleLevel *sl, int start, int end,
1250                              int step) {
1251   if (start < 1 || step < 1 || start > end || !sl || sl->isSubsequence() ||
1252       sl->isReadOnly())
1253     return;
1254 
1255   std::vector<TFrameId> oldFids;
1256   sl->getFids(oldFids);
1257 
1258   std::set<TFrameId> fidsToInsert;
1259   int frame = 0;
1260   for (frame = start; frame <= end; frame += step)
1261     fidsToInsert.insert(TFrameId(frame));
1262 
1263   makeSpaceForFids(sl, fidsToInsert);
1264 
1265   for (auto const &fid : fidsToInsert) {
1266     sl->setFrame(fid, sl->createEmptyFrame());
1267     IconGenerator::instance()->invalidate(sl, fid);
1268   }
1269   sl->setDirtyFlag(true);
1270 
1271   AddFramesUndo *undo = new AddFramesUndo(sl, fidsToInsert, oldFids);
1272   TUndoManager::manager()->add(undo);
1273 
1274   TApp *app = TApp::instance();
1275   app->getCurrentScene()->setDirtyFlag(true);
1276   app->getCurrentLevel()->notifyLevelChange();
1277 }
1278 
1279 //=============================================================================
1280 // RenumberUndo
1281 //-----------------------------------------------------------------------------
1282 
1283 namespace {
1284 
1285 class RenumberUndo final : public TUndo {
1286   TXshSimpleLevelP m_level;
1287   std::vector<TFrameId> m_fids;
1288   std::map<TFrameId, TFrameId> m_mapOldFrameId;
1289   bool m_updateXSheet = false;
1290 
1291 public:
RenumberUndo(const TXshSimpleLevelP & level,const std::vector<TFrameId> & fids,bool forceCallUpdateXSheet=false)1292   RenumberUndo(const TXshSimpleLevelP &level, const std::vector<TFrameId> &fids,
1293                bool forceCallUpdateXSheet = false)
1294       : m_level(level), m_fids(fids) {
1295     assert(m_level);
1296     std::vector<TFrameId> oldFids;
1297     m_level->getFids(oldFids);
1298     assert(oldFids.size() == m_fids.size());
1299     int i;
1300     for (i = 0; i < m_fids.size(); i++) {
1301       if (m_fids[i] != oldFids[i]) m_mapOldFrameId[m_fids[i]] = oldFids[i];
1302     }
1303     m_updateXSheet =
1304         Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled() ||
1305         forceCallUpdateXSheet;
1306   }
renumber(std::vector<TFrameId> fids) const1307   void renumber(std::vector<TFrameId> fids) const {
1308     if (m_updateXSheet) {
1309       std::vector<TFrameId> oldFrames;
1310       m_level->getFids(oldFrames);
1311       updateXSheet(m_level.getPointer(), oldFrames, fids);
1312     }
1313     m_level->renumber(fids);
1314     TSelection *selection = TSelection::getCurrent();
1315     if (selection) selection->selectNone();
1316     m_level->setDirtyFlag(true);
1317     TApp::instance()->getCurrentLevel()->notifyLevelChange();
1318   }
undo() const1319   void undo() const override {
1320     std::vector<TFrameId> fids;
1321     m_level->getFids(fids);
1322     assert(fids.size() == m_fids.size());
1323     int i;
1324     for (i = 0; i < fids.size(); i++) {
1325       if (m_mapOldFrameId.count(fids[i]) > 0) {
1326         std::map<TFrameId, TFrameId>::const_iterator it;
1327         it = m_mapOldFrameId.find(fids[i]);
1328         assert(it != m_mapOldFrameId.end());
1329         if (it != m_mapOldFrameId.end()) fids[i] = TFrameId(it->second);
1330       }
1331     }
1332     renumber(fids);
1333   }
redo() const1334   void redo() const override { renumber(m_fids); }
getSize() const1335   int getSize() const override {
1336     return sizeof(*this) + sizeof(TFrameId) * m_fids.size();
1337   }
1338 
getHistoryString()1339   QString getHistoryString() override {
1340     return QObject::tr("Renumber  : Level %1")
1341         .arg(QString::fromStdWString(m_level->getName()));
1342   }
getHistoryType()1343   int getHistoryType() override { return HistoryType::FilmStrip; }
1344 };
1345 
1346 }  // namespace
1347 
1348 //=============================================================================
1349 // Renumber
1350 //-----------------------------------------------------------------------------
1351 
renumber(TXshSimpleLevel * sl,const std::vector<std::pair<TFrameId,TFrameId>> & table,bool forceCallUpdateXSheet)1352 void FilmstripCmd::renumber(
1353     TXshSimpleLevel *sl,
1354     const std::vector<std::pair<TFrameId, TFrameId>> &table,
1355     bool forceCallUpdateXSheet) {
1356   if (!sl || sl->isSubsequence() || sl->isReadOnly()) return;
1357   if (table.empty()) return;
1358 
1359   // table:src->dst; check that src is a fid of the level
1360   std::vector<std::pair<TFrameId, TFrameId>>::const_iterator it;
1361   for (it = table.begin(); it != table.end(); ++it) {
1362     TFrameId srcFid = it->first;
1363     if (!sl->isFid(srcFid)) {
1364       // todo: error messages
1365       return;
1366     }
1367   }
1368 
1369   // tmp contains all the level fids that are not affected by the renumbering
1370   std::vector<TFrameId> fids, rfids, oldFrames;
1371   sl->getFids(fids);
1372   sl->getFids(oldFrames);
1373   std::set<TFrameId> tmp;
1374   for (int i = 0; i < (int)fids.size(); i++) tmp.insert(fids[i]);
1375   for (it = table.begin(); it != table.end(); ++it) tmp.erase(it->first);
1376 
1377   // fids contain the new numbering of all the level drawings
1378   // (note: fids can be not ordered)
1379   for (int i = 0; i < (int)fids.size(); i++) {
1380     TFrameId srcFid = fids[i];
1381     for (it = table.begin(); it != table.end() && it->first != srcFid; ++it) {
1382     }
1383     if (it != table.end()) {
1384       // srcFid is affected by the renumbering
1385       TFrameId tarFid = it->second;
1386       // make sure that srcFid has not been used. add a letter if this is needed
1387       if (tmp.count(tarFid) > 0) {
1388         do {
1389           char letter = tarFid.getLetter();
1390           tarFid = TFrameId(tarFid.getNumber(), letter == 0 ? 'a' : letter + 1);
1391         } while (tarFid.getLetter() <= 'z' && tmp.count(tarFid) > 0);
1392         if (tarFid.getLetter() > 'z') {
1393           // todo: error message
1394           return;
1395         }
1396       }
1397       tmp.insert(tarFid);
1398       fids[i] = tarFid;
1399     }
1400   }
1401 
1402   TUndoManager::manager()->add(
1403       new RenumberUndo(sl, fids, forceCallUpdateXSheet));
1404   if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled() ||
1405       forceCallUpdateXSheet) {
1406     updateXSheet(sl, oldFrames, fids);
1407   }
1408   sl->renumber(fids);
1409   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1410   TApp::instance()->getCurrentLevel()->notifyLevelChange();
1411 
1412   /*
1413 int i;
1414 std::set<TFrameId>::iterator it2;
1415 it2=frames.begin();
1416 std::set<TFrameId> newFrames;
1417 for (i=0; i<frames.size(); i++, it2++)
1418 newFrames.insert(TFrameId(startFrame+(i*stepFrame),it2->getLetter()));
1419 assert(frames.size()==newFrames.size());
1420 frames.swap(newFrames);
1421 */
1422 }
1423 
1424 //-----------------------------------------------------------------------------
1425 
renumber(TXshSimpleLevel * sl,std::set<TFrameId> & frames,int startFrame,int stepFrame)1426 void FilmstripCmd::renumber(TXshSimpleLevel *sl, std::set<TFrameId> &frames,
1427                             int startFrame, int stepFrame) {
1428   if (!sl || sl->isSubsequence() || sl->isReadOnly()) return;
1429   assert(startFrame > 0 && stepFrame > 0);
1430   if (startFrame <= 0 || stepFrame <= 0 || frames.empty()) return;
1431 
1432   std::vector<TFrameId> fids;
1433   std::vector<TFrameId> oldFrames;
1434   sl->getFids(oldFrames);
1435   sl->getFids(fids);
1436 
1437   std::set<TFrameId> modifiedFids;
1438 
1439   // tmp contiene i frames del livello, meno quelli da renumerare
1440   std::set<TFrameId> tmp(fids.begin(), fids.end());
1441   std::set<TFrameId>::const_iterator it;
1442   for (it = frames.begin(); it != frames.end(); ++it) tmp.erase(*it);
1443 
1444   int frame                         = startFrame;
1445   std::vector<TFrameId>::iterator j = fids.begin();
1446   for (it = frames.begin(); it != frames.end(); ++it) {
1447     TFrameId srcFid(*it);
1448     TFrameId dstFid(frame);
1449     frame += stepFrame;
1450     // faccio il controllo su tmp e non su fids. considera:
1451     // fids = [1,2,3,4], renumber = [2->3,3->5]
1452     if (tmp.count(dstFid) > 0) {
1453       DVGui::error(("can't renumber: frame conflict"));
1454       return;
1455     }
1456     j = std::find(j, fids.end(), srcFid);
1457     // i frames selezionati fanno parte del livello sl. Quindi:
1458     assert(j != fids.end());
1459     assert(srcFid == *j);
1460     if (j == fids.end()) continue;  // per sicurezza
1461     int k = std::distance(fids.begin(), j);
1462     if (srcFid != dstFid) {
1463       modifiedFids.insert(srcFid);
1464       modifiedFids.insert(dstFid);
1465       *j = dstFid;
1466       ++j;
1467     }
1468   }
1469   TUndoManager::manager()->add(new RenumberUndo(sl, fids));
1470   if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) {
1471     updateXSheet(sl, oldFrames, fids);
1472   }
1473   sl->renumber(fids);
1474   sl->setDirtyFlag(true);
1475   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1476   int i;
1477   std::set<TFrameId>::iterator it2;
1478   it2 = frames.begin();
1479   std::set<TFrameId> newFrames;
1480   for (i = 0; i < frames.size(); i++, it2++)
1481     newFrames.insert(TFrameId(startFrame + (i * stepFrame), it2->getLetter()));
1482   assert(frames.size() == newFrames.size());
1483   frames.swap(newFrames);
1484 
1485   TApp::instance()->getCurrentLevel()->notifyLevelChange();
1486 }
1487 
1488 //=============================================================================
1489 // copy
1490 //-----------------------------------------------------------------------------
1491 
copy(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1492 void FilmstripCmd::copy(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1493   if (!sl || frames.empty()) return;
1494   copyFramesWithoutUndo(sl, frames);
1495 }
1496 
1497 //=============================================================================
1498 // paste
1499 //-----------------------------------------------------------------------------
1500 
paste(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1501 void FilmstripCmd::paste(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1502   if (!sl || sl->isReadOnly() || frames.empty()) return;
1503 
1504   std::vector<TFrameId> oldLevelFrameId;
1505   sl->getFids(oldLevelFrameId);
1506 
1507   TPaletteP oldPalette;
1508   if (TPalette *pal = sl->getPalette()) oldPalette = pal->clone();
1509 
1510   QClipboard *clipboard = QApplication::clipboard();
1511   QMimeData *data       = cloneData(clipboard->mimeData());
1512   // when pasting the filmstrip frames
1513   DrawingData *drawingData = dynamic_cast<DrawingData *>(data);
1514   if (drawingData) {
1515     if (sl->isSubsequence()) return;
1516 
1517     // keep the choosed option of "Keep Original Palette" and reproduce it in
1518     // undo
1519     bool keepOriginalPalette;
1520 
1521     HookSet *oldLevelHooks = new HookSet();
1522     *oldLevelHooks         = *sl->getHookSet();
1523 
1524     bool isPaste =
1525         pasteFramesWithoutUndo(drawingData, sl, frames, DrawingData::INSERT,
1526                                true, keepOriginalPalette);
1527     if (!isPaste) return;
1528     TUndoManager::manager()->add(new PasteFramesUndo(
1529         sl, frames, oldLevelFrameId, oldPalette, DrawingData::INSERT,
1530         oldLevelHooks, keepOriginalPalette));
1531   }
1532   // when pasting the copied part of the image which is selected with the
1533   // selection tool
1534   else {
1535     bool isFrameToInsert =
1536         (frames.size() == 1) ? !sl->isFid((*frames.begin())) : false;
1537     TTileSet *tileSet = 0;
1538     std::map<TFrameId, std::set<int>> indices;
1539     TUndo *undo   = 0;
1540     TPaletteP plt = sl->getPalette()->clone();
1541     bool isPaste  = pasteAreasWithoutUndo(data, sl, frames, &tileSet, indices);
1542     RasterImageData *rasterImageData = dynamic_cast<RasterImageData *>(data);
1543     StrokesData *strokesData         = dynamic_cast<StrokesData *>(data);
1544     if (rasterImageData && tileSet)
1545       undo = new PasteRasterAreasUndo(sl, frames, tileSet, rasterImageData,
1546                                       plt.getPointer(), isFrameToInsert);
1547     if (strokesData && tileSet) {
1548       TImageP img      = sl->getFrame(*frames.begin(), false);
1549       TRasterImageP ri = img;
1550       TToonzImageP ti  = img;
1551       assert(img);
1552       if (ti)
1553         undo = new PasteRasterAreasUndo(sl, frames, tileSet,
1554                                         strokesData->toToonzImageData(ti),
1555                                         plt.getPointer(), isFrameToInsert);
1556       else if (ri) {
1557         double dpix, dpiy;
1558         ri->getDpi(dpix, dpiy);
1559         if (dpix == 0 || dpiy == 0) {
1560           TPointD dpi = sl->getScene()->getCurrentCamera()->getDpi();
1561           dpix        = dpi.x;
1562           dpiy        = dpi.y;
1563           ri->setDpi(dpix, dpiy);
1564         }
1565         undo = new PasteRasterAreasUndo(sl, frames, tileSet,
1566                                         strokesData->toFullColorImageData(ri),
1567                                         plt.getPointer(), isFrameToInsert);
1568       }
1569     }
1570     if (strokesData && !indices.empty())
1571       undo = new PasteVectorAreasUndo(sl, frames, indices, strokesData,
1572                                       plt.getPointer(), isFrameToInsert);
1573     if (rasterImageData && !indices.empty())
1574       undo = new PasteVectorAreasUndo(
1575           sl, frames, indices, rasterImageData->toStrokesData(sl->getScene()),
1576           plt.getPointer(), isFrameToInsert);
1577     if (!isPaste) return;
1578     if (undo) TUndoManager::manager()->add(undo);
1579   }
1580 }
1581 
1582 //=============================================================================
1583 // merge
1584 //-----------------------------------------------------------------------------
1585 
merge(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1586 void FilmstripCmd::merge(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1587   if (!sl || sl->isReadOnly() || sl->isSubsequence()) return;
1588 
1589   std::vector<TFrameId> oldLevelFrameId;
1590   sl->getFids(oldLevelFrameId);
1591   TPaletteP oldPalette = sl->getPalette()->clone();
1592   std::set<TFrameId> frameIdToChange;
1593 
1594   QClipboard *clipboard = QApplication::clipboard();
1595   if (const DrawingData *drawingData =
1596           dynamic_cast<const DrawingData *>(clipboard->mimeData())) {
1597     drawingData->getFrames(frameIdToChange);
1598     DrawingData *data = new DrawingData();
1599     data->setLevelFrames(sl, frameIdToChange);
1600     HookSet *oldLevelHooks = new HookSet();
1601     *oldLevelHooks         = *sl->getHookSet();
1602 
1603     bool keepOriginalPalette = true;
1604 
1605     bool isPaste = pasteFramesWithoutUndo(drawingData, sl, frames,
1606                                           DrawingData::OVER_FRAMEID, true,
1607                                           keepOriginalPalette);
1608     if (!isPaste) return;
1609     TUndoManager::manager()->add(new PasteFramesUndo(
1610         sl, frames, oldLevelFrameId, oldPalette, DrawingData::OVER_FRAMEID,
1611         oldLevelHooks, keepOriginalPalette, data));
1612   }
1613 }
1614 
1615 //=============================================================================
1616 // pasteInto
1617 //-----------------------------------------------------------------------------
1618 
pasteInto(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1619 void FilmstripCmd::pasteInto(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1620   if (!sl || sl->isReadOnly() || sl->isSubsequence()) return;
1621 
1622   std::vector<TFrameId> oldLevelFrameId;
1623   sl->getFids(oldLevelFrameId);
1624 
1625   TPaletteP oldPalette;
1626   if (TPalette *pal = sl->getPalette()) oldPalette = pal->clone();
1627 
1628   QClipboard *clipboard = QApplication::clipboard();
1629   if (const DrawingData *drawingData =
1630           dynamic_cast<const DrawingData *>(clipboard->mimeData())) {
1631     DrawingData *data = new DrawingData();
1632     data->setLevelFrames(sl, frames);
1633 
1634     HookSet *oldLevelHooks = new HookSet();
1635     *oldLevelHooks         = *sl->getHookSet();
1636 
1637     bool keepOriginalPalette = true;
1638     bool isPaste             = pasteFramesWithoutUndo(drawingData, sl, frames,
1639                                           DrawingData::OVER_SELECTION, true,
1640                                           keepOriginalPalette);
1641     if (!isPaste) return;
1642 
1643     TUndoManager::manager()->add(new PasteFramesUndo(
1644         sl, frames, oldLevelFrameId, oldPalette, DrawingData::OVER_SELECTION,
1645         oldLevelHooks, keepOriginalPalette, data));
1646   }
1647 }
1648 
1649 //=============================================================================
1650 // cut
1651 //-----------------------------------------------------------------------------
1652 
cut(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1653 void FilmstripCmd::cut(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1654   if (!sl || frames.empty() || sl->isSubsequence() || sl->isReadOnly()) return;
1655 
1656   std::set<TFrameId> framesToCut = frames;
1657   std::vector<TFrameId> oldFrames;
1658   sl->getFids(oldFrames);
1659   cutFramesWithoutUndo(sl, frames);
1660   TUndoManager::manager()->add(new CutFramesUndo(sl, framesToCut, oldFrames));
1661 }
1662 
1663 //=============================================================================
1664 // clear
1665 //-----------------------------------------------------------------------------
1666 
clear(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1667 void FilmstripCmd::clear(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1668   if (!sl || frames.empty()) return;
1669 
1670   if (sl->isReadOnly()) {
1671     std::set<TFrameId> editableFrames = sl->getEditableRange();
1672     if (editableFrames.empty()) return;
1673 
1674     // Browser all the frames and return if some frames are not editable
1675     std::set<TFrameId>::const_iterator it;
1676     for (it = frames.begin(); it != frames.end(); ++it) {
1677       TFrameId frameId = *it;
1678       if (editableFrames.count(frameId) == 0) return;
1679     }
1680   }
1681 
1682   HookSet *levelHooks          = sl->getHookSet();
1683   std::set<TFrameId> oldFrames = frames;
1684   std::map<TFrameId, QString> clearedFrames =
1685       clearFramesWithoutUndo(sl, frames);
1686   DrawingData *oldData = new DrawingData();
1687   oldData->setFrames(clearedFrames, sl, *levelHooks);
1688   DrawingData *newData = new DrawingData();
1689   newData->setLevelFrames(sl, frames);
1690   frames.clear();
1691   TUndoManager::manager()->add(
1692       new DeleteFramesUndo(sl, oldFrames, oldData, newData));
1693 }
1694 
1695 //-----------------------------------------------------------------------------
1696 namespace {
1697 //-----------------------------------------------------------------------------
1698 
insertEmptyFilmstripFrames(const TXshSimpleLevelP & sl,const std::set<TFrameId> & frames)1699 void insertEmptyFilmstripFrames(const TXshSimpleLevelP &sl,
1700                                 const std::set<TFrameId> &frames) {
1701   if (!sl || frames.empty()) return;
1702   makeSpaceForFids(sl.getPointer(), frames);
1703   std::set<TFrameId>::const_iterator it;
1704   for (it = frames.begin(); it != frames.end(); ++it)
1705     sl->setFrame(*it, sl->createEmptyFrame());
1706   invalidateIcons(sl.getPointer(), frames);
1707   sl->setDirtyFlag(true);
1708   //  TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1709   TApp::instance()->getCurrentLevel()->notifyLevelChange();
1710 }
1711 
1712 //-----------------------------------------------------------------------------
1713 
1714 class UndoInsertEmptyFrames final : public TUndo {
1715   TXshSimpleLevelP m_level;
1716   std::vector<TFrameId> m_oldFrames;
1717   std::set<TFrameId> m_frames;
1718   bool m_updateXSheet;
1719 
1720 public:
UndoInsertEmptyFrames(const TXshSimpleLevelP & level,std::set<TFrameId> frames,std::vector<TFrameId> oldFrames)1721   UndoInsertEmptyFrames(const TXshSimpleLevelP &level,
1722                         std::set<TFrameId> frames,
1723                         std::vector<TFrameId> oldFrames)
1724       : m_level(level), m_frames(frames), m_oldFrames(oldFrames) {
1725     if (m_level->getType() == TZP_XSHLEVEL) {
1726       std::set<TFrameId>::iterator it;
1727       for (it = m_frames.begin(); it != m_frames.end(); it++) {
1728         TToonzImageP img = m_level->getFrame(*it, true);
1729         // TImageCache::instance()->add("UndoInsertEmptyFrames"+QString::number((UINT)this),
1730         // img);
1731         TImageCache::instance()->add(
1732             "UndoInsertEmptyFrames" + QString::number((uintptr_t)this), img);
1733       }
1734     }
1735     m_updateXSheet =
1736         Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled();
1737   }
1738 
~UndoInsertEmptyFrames()1739   ~UndoInsertEmptyFrames() {
1740     // TImageCache::instance()->remove("UndoInsertEmptyFrames"+QString::number((UINT)this));
1741     TImageCache::instance()->remove("UndoInsertEmptyFrames" +
1742                                     QString::number((uintptr_t)this));
1743   }
1744 
undo() const1745   void undo() const override {
1746     removeFramesWithoutUndo(m_level, m_frames);
1747     assert(m_oldFrames.size() == m_level->getFrameCount());
1748     if (m_updateXSheet) {
1749       std::vector<TFrameId> newFrames;
1750       m_level->getFids(newFrames);
1751       updateXSheet(m_level.getPointer(), newFrames, m_oldFrames);
1752     }
1753     m_level->renumber(m_oldFrames);
1754     m_level->setDirtyFlag(true);
1755     TApp::instance()->getCurrentLevel()->notifyLevelChange();
1756   }
1757 
redo() const1758   void redo() const override {
1759     if (!m_level || m_frames.empty()) return;
1760     if (m_level->getType() == PLI_XSHLEVEL)
1761       FilmstripCmd::insert(m_level.getPointer(), m_frames, false);
1762     else if (m_level->getType() == TZP_XSHLEVEL) {
1763       makeSpaceForFids(m_level.getPointer(), m_frames);
1764       std::set<TFrameId>::const_iterator it;
1765       // TToonzImageP image =
1766       // (TToonzImageP)TImageCache::instance()->get("UndoInsertEmptyFrames"+QString::number((UINT)this),
1767       // true);
1768       TToonzImageP image = (TToonzImageP)TImageCache::instance()->get(
1769           "UndoInsertEmptyFrames" + QString::number((uintptr_t)this), true);
1770       if (!image) return;
1771       for (it = m_frames.begin(); it != m_frames.end(); ++it)
1772         m_level->setFrame(*it, image);
1773       invalidateIcons(m_level.getPointer(), m_frames);
1774       m_level->setDirtyFlag(true);
1775       TApp::instance()->getCurrentLevel()->notifyLevelChange();
1776     }
1777   }
1778 
getSize() const1779   int getSize() const override { return sizeof(*this); }
1780 
getHistoryString()1781   QString getHistoryString() override {
1782     return QObject::tr("Insert  : Level %1")
1783         .arg(QString::fromStdWString(m_level->getName()));
1784   }
getHistoryType()1785   int getHistoryType() override { return HistoryType::FilmStrip; }
1786 };
1787 
1788 }  // namespace
1789 
1790 //=============================================================================
1791 // insert
1792 //-----------------------------------------------------------------------------
1793 
insert(TXshSimpleLevel * sl,const std::set<TFrameId> & frames,bool withUndo)1794 void FilmstripCmd::insert(TXshSimpleLevel *sl, const std::set<TFrameId> &frames,
1795                           bool withUndo) {
1796   if (frames.empty() || !sl || sl->isSubsequence() || sl->isReadOnly()) return;
1797   std::vector<TFrameId> oldFrames;
1798   if (withUndo) sl->getFids(oldFrames);
1799 
1800   insertEmptyFilmstripFrames(sl, frames);
1801   if (withUndo)
1802     TUndoManager::manager()->add(
1803         new UndoInsertEmptyFrames(sl, frames, oldFrames));
1804   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1805 }
1806 
1807 //=============================================================================
1808 
1809 //-----------------------------------------------------------------------------
1810 namespace {
1811 //-----------------------------------------------------------------------------
1812 
performReverse(const TXshSimpleLevelP & sl,const std::set<TFrameId> & frames)1813 void performReverse(const TXshSimpleLevelP &sl,
1814                     const std::set<TFrameId> &frames) {
1815   if (!sl || frames.empty()) return;
1816 
1817   std::vector<TFrameId> fids;
1818   std::vector<TFrameId> oldFrames;
1819   sl->getFids(oldFrames);
1820   sl->getFids(fids);
1821   int i = 0, j = (int)fids.size() - 1;
1822   for (;;) {
1823     while (i < j && frames.count(fids[i]) == 0) i++;
1824     while (i < j && frames.count(fids[j]) == 0) j--;
1825     if (i >= j) break;
1826     std::swap(fids[i], fids[j]);
1827     i++;
1828     j--;
1829   }
1830   if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) {
1831     updateXSheet(sl.getPointer(), oldFrames, fids);
1832   }
1833   sl->renumber(fids);
1834   sl->setDirtyFlag(true);
1835   //  TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1836   TApp::instance()->getCurrentLevel()->notifyLevelChange();
1837 }
1838 
1839 //-----------------------------------------------------------------------------
1840 
1841 class FilmstripReverseUndo final : public TUndo {
1842   TXshSimpleLevelP m_level;
1843   std::set<TFrameId> m_frames;
1844 
1845 public:
FilmstripReverseUndo(TXshSimpleLevelP level,std::set<TFrameId> frames)1846   FilmstripReverseUndo(TXshSimpleLevelP level, std::set<TFrameId> frames)
1847       : m_level(level), m_frames(frames) {}
1848 
undo() const1849   void undo() const override { performReverse(m_level, m_frames); }
redo() const1850   void redo() const override { performReverse(m_level, m_frames); }
getSize() const1851   int getSize() const override { return sizeof *this; }
1852 
getHistoryString()1853   QString getHistoryString() override {
1854     return QObject::tr("Reverse  : Level %1")
1855         .arg(QString::fromStdWString(m_level->getName()));
1856   }
getHistoryType()1857   int getHistoryType() override { return HistoryType::FilmStrip; }
1858 };
1859 
1860 }  // namespace
1861 
1862 //=============================================================================
1863 // reverse
1864 //-----------------------------------------------------------------------------
1865 
reverse(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1866 void FilmstripCmd::reverse(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1867   if (!sl || sl->isSubsequence() || sl->isReadOnly()) return;
1868   performReverse(sl, frames);
1869   TUndoManager::manager()->add(new FilmstripReverseUndo(sl, frames));
1870   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1871 }
1872 
1873 //=============================================================================
1874 
1875 //-----------------------------------------------------------------------------
1876 namespace {
1877 //-----------------------------------------------------------------------------
1878 
performSwing(const TXshSimpleLevelP & sl,const std::set<TFrameId> & frames)1879 void performSwing(const TXshSimpleLevelP &sl,
1880                   const std::set<TFrameId> &frames) {
1881   if (!sl) return;
1882   int count = frames.size() - 1;
1883   if (count <= 0) return;  // niente swing con un solo frame
1884   TFrameId lastFid     = *frames.rbegin();
1885   TFrameId insertPoint = lastFid + 1;
1886   std::set<TFrameId> framesToInsert;
1887   int i;
1888   for (i = 0; i < count; i++) framesToInsert.insert(insertPoint + i);
1889 
1890   std::vector<TImage *> clonedImages;
1891   std::set<TFrameId>::const_reverse_iterator k;
1892   k = frames.rbegin();
1893   for (++k; k != frames.rend(); ++k) {
1894     TImageP img = sl->getFrame(*k, false);
1895     clonedImages.push_back(img ? img->cloneImage() : 0);
1896   }
1897 
1898   makeSpaceForFids(sl.getPointer(), framesToInsert);
1899   assert(count == (int)clonedImages.size());
1900   for (i = 0; i < count && (int)i < (int)clonedImages.size(); i++) {
1901     TImage *img = clonedImages[i];
1902     if (img) sl->setFrame(insertPoint + i, img);
1903   }
1904   invalidateIcons(sl.getPointer(), framesToInsert);
1905   sl->setDirtyFlag(true);
1906   //  TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1907   TApp::instance()->getCurrentLevel()->notifyLevelChange();
1908 }
1909 
1910 //-----------------------------------------------------------------------------
1911 
1912 class FilmstripSwingUndo final : public TUndo {
1913   TXshSimpleLevelP m_level;
1914   std::set<TFrameId> m_frames;
1915   std::set<TFrameId> m_newFrames;
1916 
1917 public:
FilmstripSwingUndo(const TXshSimpleLevelP & level,const std::set<TFrameId> & frames)1918   FilmstripSwingUndo(const TXshSimpleLevelP &level,
1919                      const std::set<TFrameId> &frames)
1920       : m_level(level), m_frames(frames) {
1921     int count = frames.size() - 1;
1922     if (count <= 0) return;  // niente swing con un solo frame
1923     TFrameId lastFid     = *frames.rbegin();
1924     TFrameId insertPoint = lastFid + 1;
1925     std::set<TFrameId> framesToInsert;
1926     int i;
1927     for (i = 0; i < count; i++) m_newFrames.insert(insertPoint + i);
1928   }
1929 
undo() const1930   void undo() const override {
1931     TSelection *selection = TSelection::getCurrent();
1932     if (selection) selection->selectNone();
1933     removeFramesWithoutUndo(m_level, m_newFrames);
1934   }
redo() const1935   void redo() const override {
1936     TSelection *selection = TSelection::getCurrent();
1937     if (selection) selection->selectNone();
1938     performSwing(m_level, m_frames);
1939   }
getSize() const1940   int getSize() const override { return sizeof *this; }
1941 
getHistoryString()1942   QString getHistoryString() override {
1943     return QObject::tr("Swing  : Level %1")
1944         .arg(QString::fromStdWString(m_level->getName()));
1945   }
getHistoryType()1946   int getHistoryType() override { return HistoryType::FilmStrip; }
1947 };
1948 
1949 }  // namespace
1950 
1951 //=============================================================================
1952 // swing
1953 //-----------------------------------------------------------------------------
1954 
swing(TXshSimpleLevel * sl,std::set<TFrameId> & frames)1955 void FilmstripCmd::swing(TXshSimpleLevel *sl, std::set<TFrameId> &frames) {
1956   if (!sl || sl->isSubsequence() || sl->isReadOnly()) return;
1957   performSwing(sl, frames);
1958   TUndoManager::manager()->add(new FilmstripSwingUndo(sl, frames));
1959   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
1960 }
1961 
1962 //=============================================================================
1963 
1964 //-----------------------------------------------------------------------------
1965 namespace {
1966 //-----------------------------------------------------------------------------
1967 
stepFilmstripFrames(const TXshSimpleLevelP & sl,const std::set<TFrameId> & frames,int step=2)1968 void stepFilmstripFrames(const TXshSimpleLevelP &sl,
1969                          const std::set<TFrameId> &frames, int step = 2) {
1970   if (!sl || frames.empty() || step < 2) return;
1971   std::vector<TFrameId> fids;
1972   std::set<TFrameId> changedFids;
1973   std::vector<int> insertIndices;
1974   std::vector<TFrameId> oldFrames;
1975   sl->getFids(oldFrames);
1976   sl->getFids(fids);
1977   int i, offset = 0;
1978   for (i = 0; i < (int)fids.size(); i++) {
1979     bool frameToStep = (frames.count(fids[i]) > 0);
1980     if (offset > 0) {
1981       changedFids.insert(fids[i]);
1982       fids[i] = fids[i] + offset;
1983       changedFids.insert(fids[i]);
1984     }
1985     if (frameToStep) {
1986       insertIndices.push_back(i);
1987       offset += step - 1;
1988     }
1989   }
1990   if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) {
1991     updateXSheet(sl.getPointer(), oldFrames, fids);
1992   }
1993   sl->renumber(fids);
1994   for (i = 0; i < (int)insertIndices.size(); i++) {
1995     int j        = insertIndices[i];
1996     TFrameId fid = fids[j];
1997     TImageP img  = sl->getFrame(fid, false);
1998     if (img) {
1999       int h;
2000       for (h = 1; h < step; h++) {
2001         sl->setFrame(fid + h, img->cloneImage());
2002         changedFids.insert(fid + h);
2003       }
2004     }
2005   }
2006   invalidateIcons(sl.getPointer(), changedFids);
2007   sl->setDirtyFlag(true);
2008   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2009 }
2010 
2011 //-----------------------------------------------------------------------------
2012 
2013 class StepFilmstripUndo final : public TUndo {
2014   TXshSimpleLevelP m_level;
2015   std::set<TFrameId> m_insertedFrames;
2016   std::set<TFrameId> m_frames;
2017   std::vector<TFrameId> m_oldFrames;
2018   int m_step;
2019   bool m_updateXSheet;
2020 
2021 public:
StepFilmstripUndo(const TXshSimpleLevelP & level,const std::set<TFrameId> & frames,int step)2022   StepFilmstripUndo(const TXshSimpleLevelP &level,
2023                     const std::set<TFrameId> &frames, int step)
2024       : m_level(level), m_frames(frames), m_step(step) {
2025     assert(m_level);
2026     m_level->getFids(m_oldFrames);
2027     int d = 0;
2028     std::set<TFrameId>::const_iterator it;
2029     for (it = frames.begin(); it != frames.end(); ++it)
2030       for (int j = 1; j < step; j++) m_insertedFrames.insert(*it + (++d));
2031     m_updateXSheet =
2032         Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled();
2033   }
2034 
undo() const2035   void undo() const override {
2036     removeFramesWithoutUndo(m_level, m_insertedFrames);
2037     std::set<TFrameId>::const_iterator it = m_frames.begin();
2038     if (m_updateXSheet) {
2039       std::vector<TFrameId> newFrames;
2040       m_level->getFids(newFrames);
2041       updateXSheet(m_level.getPointer(), newFrames, m_oldFrames);
2042     }
2043     m_level->renumber(m_oldFrames);
2044     TSelection *selection = TSelection::getCurrent();
2045     if (selection) selection->selectNone();
2046     m_level->setDirtyFlag(true);
2047     TApp::instance()->getCurrentLevel()->notifyLevelChange();
2048   }
redo() const2049   void redo() const override {
2050     TSelection *selection = TSelection::getCurrent();
2051     if (selection) selection->selectNone();
2052     stepFilmstripFrames(m_level, m_frames, m_step);
2053   }
getSize() const2054   int getSize() const override { return sizeof *this; }
2055 
getHistoryString()2056   QString getHistoryString() override {
2057     return QObject::tr("Step %1  : Level %2")
2058         .arg(QString::number(m_step))
2059         .arg(QString::fromStdWString(m_level->getName()));
2060   }
getHistoryType()2061   int getHistoryType() override { return HistoryType::FilmStrip; }
2062 };
2063 
2064 }  // namespace
2065 
2066 //=============================================================================
2067 // step
2068 //-----------------------------------------------------------------------------
2069 
step(TXshSimpleLevel * sl,std::set<TFrameId> & frames,int step)2070 void FilmstripCmd::step(TXshSimpleLevel *sl, std::set<TFrameId> &frames,
2071                         int step) {
2072   if (!sl || sl->isSubsequence() || sl->isReadOnly()) return;
2073   QApplication::setOverrideCursor(Qt::WaitCursor);
2074   StepFilmstripUndo *undo = new StepFilmstripUndo(sl, frames, step);
2075   stepFilmstripFrames(sl, frames, step);
2076   TUndoManager::manager()->add(undo);
2077   frames.clear();
2078   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2079   QApplication::restoreOverrideCursor();
2080 }
2081 
2082 //=============================================================================
2083 
2084 //-----------------------------------------------------------------------------
2085 namespace {
2086 //-----------------------------------------------------------------------------
2087 
eachFilmstripFrames(const TXshSimpleLevelP & sl,const std::set<TFrameId> & frames,int each)2088 std::map<TFrameId, QString> eachFilmstripFrames(
2089     const TXshSimpleLevelP &sl, const std::set<TFrameId> &frames, int each) {
2090   if (frames.empty() || !sl || each < 2) return std::map<TFrameId, QString>();
2091   std::vector<TFrameId> framesToDelete;
2092   std::set<TFrameId>::const_iterator it;
2093   int k = 0;
2094   for (it = frames.begin(); it != frames.end(); ++it, ++k)
2095     if ((k % each) > 0) framesToDelete.push_back(*it);
2096   int i = 0;
2097   std::vector<TFrameId>::reverse_iterator fit;
2098   std::map<TFrameId, QString> cutFrames;
2099 
2100   for (fit = framesToDelete.rbegin(); fit != framesToDelete.rend(); ++fit) {
2101     TImageP img = sl->getFrame(*fit, false);
2102     if (img) {
2103       // QString id =
2104       // "eachFrames"+QString::number((UINT)sl.getPointer())+"-"+QString::number(fit->getNumber());
2105       QString id = "eachFrames" + QString::number((uintptr_t)sl.getPointer()) +
2106                    "-" + QString::number(fit->getNumber());
2107       TImageCache::instance()->add(id, img);
2108 
2109       cutFrames[*fit] = id;
2110     }
2111     sl->eraseFrame(*fit);  // toglie da cache?
2112     IconGenerator::instance()->remove(sl.getPointer(), *fit);
2113   }
2114 
2115   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2116   return cutFrames;
2117 }
2118 
2119 //-----------------------------------------------------------------------------
2120 
2121 class EachFilmstripUndo final : public TUndo {
2122   TXshSimpleLevelP m_level;
2123   std::set<TFrameId> m_frames;
2124   std::map<TFrameId, QString> m_cutFrames;
2125   int m_each;
2126 
2127 public:
EachFilmstripUndo(const TXshSimpleLevelP & level,int each,const std::set<TFrameId> & frames,std::map<TFrameId,QString> deletedFrames)2128   EachFilmstripUndo(const TXshSimpleLevelP &level, int each,
2129                     const std::set<TFrameId> &frames,
2130                     std::map<TFrameId, QString> deletedFrames)
2131       : m_level(level)
2132       , m_cutFrames(deletedFrames)
2133       , m_each(each)
2134       , m_frames(frames) {}
~EachFilmstripUndo()2135   ~EachFilmstripUndo() {
2136     std::map<TFrameId, QString>::iterator it = m_cutFrames.begin();
2137     for (; it != m_cutFrames.end(); ++it)
2138       TImageCache::instance()->remove(it->second);
2139   }
undo() const2140   void undo() const override {
2141     TSelection *selection = TSelection::getCurrent();
2142     if (selection) selection->selectNone();
2143     insertNotEmptyframes(m_level, m_cutFrames);
2144   }
redo() const2145   void redo() const override {
2146     TSelection *selection = TSelection::getCurrent();
2147     if (selection) selection->selectNone();
2148     eachFilmstripFrames(m_level, m_frames, m_each);
2149   }
getSize() const2150   int getSize() const override { return sizeof *this; }
2151 
getHistoryString()2152   QString getHistoryString() override {
2153     return QObject::tr("Each %1  : Level %2")
2154         .arg(QString::number(m_each))
2155         .arg(QString::fromStdWString(m_level->getName()));
2156   }
getHistoryType()2157   int getHistoryType() override { return HistoryType::FilmStrip; }
2158 };
2159 
2160 }  // namespace
2161 
2162 //=============================================================================
2163 // each
2164 //-----------------------------------------------------------------------------
2165 
each(TXshSimpleLevel * sl,std::set<TFrameId> & frames,int each)2166 void FilmstripCmd::each(TXshSimpleLevel *sl, std::set<TFrameId> &frames,
2167                         int each) {
2168   if (!sl || sl->isSubsequence() || sl->isReadOnly()) return;
2169   std::map<TFrameId, QString> deletedFrames =
2170       eachFilmstripFrames(sl, frames, each);
2171   TUndoManager::manager()->add(
2172       new EachFilmstripUndo(sl, each, frames, deletedFrames));
2173   frames.clear();
2174   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2175 }
2176 
2177 //=============================================================================
2178 
2179 //-----------------------------------------------------------------------------
2180 namespace {
2181 //-----------------------------------------------------------------------------
2182 
2183 class UndoDuplicateDrawing final : public TUndo {
2184   TXshSimpleLevelP m_level;
2185   std::set<TFrameId> m_frameInserted;
2186   std::vector<TFrameId> m_oldFrames;
2187   std::set<TFrameId> m_framesForRedo;
2188   bool m_updateXSheet;
2189 
2190 public:
UndoDuplicateDrawing(const TXshSimpleLevelP & level,const std::vector<TFrameId> oldFrames,std::set<TFrameId> frameInserted,std::set<TFrameId> framesForRedo)2191   UndoDuplicateDrawing(const TXshSimpleLevelP &level,
2192                        const std::vector<TFrameId> oldFrames,
2193                        std::set<TFrameId> frameInserted,
2194                        std::set<TFrameId> framesForRedo)
2195       : m_level(level)
2196       , m_oldFrames(oldFrames)
2197       , m_frameInserted(frameInserted)
2198       , m_framesForRedo(framesForRedo) {
2199     m_updateXSheet =
2200         Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled();
2201   }
2202 
undo() const2203   void undo() const override {
2204     assert(m_level);
2205     removeFramesWithoutUndo(m_level, m_frameInserted);
2206     if (m_updateXSheet) {
2207       std::vector<TFrameId> newFrames;
2208       m_level->getFids(newFrames);
2209       updateXSheet(m_level.getPointer(), newFrames, m_oldFrames);
2210     }
2211     m_level->renumber(m_oldFrames);
2212     m_level->setDirtyFlag(true);
2213     TApp::instance()->getCurrentLevel()->notifyLevelChange();
2214   }
redo() const2215   void redo() const override {
2216     std::set<TFrameId> framesForRedo = m_framesForRedo;
2217     FilmstripCmd::duplicate(m_level.getPointer(), framesForRedo, false);
2218   }
getSize() const2219   int getSize() const override { return sizeof(*this); }
2220 
getHistoryString()2221   QString getHistoryString() override {
2222     return QObject::tr("Duplicate  : Level %1")
2223         .arg(QString::fromStdWString(m_level->getName()));
2224   }
getHistoryType()2225   int getHistoryType() override { return HistoryType::FilmStrip; }
2226 };
2227 
2228 }  // namespace
2229 
2230 //=============================================================================
2231 // duplicate
2232 //-----------------------------------------------------------------------------
2233 
duplicateFrameWithoutUndo(TXshSimpleLevel * sl,TFrameId srcFrame,TFrameId targetFrame)2234 void FilmstripCmd::duplicateFrameWithoutUndo(TXshSimpleLevel *sl,
2235                                              TFrameId srcFrame,
2236                                              TFrameId targetFrame) {
2237   if (srcFrame.isNoFrame() || targetFrame.isNoFrame()) return;
2238   if (srcFrame.isEmptyFrame()) return;
2239 
2240   std::set<TFrameId> frames;
2241 
2242   frames.insert(srcFrame);
2243   DrawingData *data = new DrawingData();
2244   data->setLevelFrames(sl, frames);
2245 
2246   frames.clear();
2247   frames.insert(targetFrame);
2248 
2249   bool keepOriginalPalette = true;
2250 
2251   pasteFramesWithoutUndo(data, sl, frames, DrawingData::OVER_SELECTION, true,
2252                          keepOriginalPalette);
2253 }
2254 
duplicate(TXshSimpleLevel * sl,std::set<TFrameId> & frames,bool withUndo)2255 void FilmstripCmd::duplicate(TXshSimpleLevel *sl, std::set<TFrameId> &frames,
2256                              bool withUndo) {
2257   if (frames.empty() || !sl || sl->isSubsequence() || sl->isReadOnly()) return;
2258 
2259   TFrameId insertPoint = (*frames.rbegin()) + 1;
2260 
2261   std::map<TFrameId, QString> framesToInsert;
2262   std::set<TFrameId> newFrames;
2263   int i = 0;
2264   for (auto const &fid : frames) {
2265     TImageP img      = sl->getFrame(fid, false);
2266     TImageP imgClone = (img) ? img->cloneImage() : 0;
2267     QString id       = "dupFrames" + QString::number((uintptr_t)sl) + "-" +
2268                  QString::number(fid.getNumber());
2269     TImageCache::instance()->add(id, imgClone);
2270     framesToInsert[insertPoint + i] = id;
2271     newFrames.insert(insertPoint + i);
2272     i++;
2273   }
2274   std::vector<TFrameId> oldFrames;
2275   sl->getFids(oldFrames);
2276   insertNotEmptyframes(sl, framesToInsert);
2277   if (withUndo)
2278     TUndoManager::manager()->add(
2279         new UndoDuplicateDrawing(sl, oldFrames, newFrames, frames));
2280   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2281 }
2282 
2283 //=============================================================================
2284 
2285 //-----------------------------------------------------------------------------
2286 namespace {
2287 //-----------------------------------------------------------------------------
2288 
moveToSceneFrames(TXshLevel * level,const std::set<TFrameId> & frames)2289 void moveToSceneFrames(TXshLevel *level, const std::set<TFrameId> &frames) {
2290   if (frames.empty() || !level) return;
2291 
2292   TXsheetHandle *xh = TApp::instance()->getCurrentXsheet();
2293   TXsheet *xsh      = xh->getXsheet();
2294   int row           = 0;
2295   int col           = xsh->getFirstFreeColumnIndex();
2296   std::set<TFrameId>::const_iterator it;
2297   if (level->getPaletteLevel()) {
2298     TXshPaletteColumn *column = new TXshPaletteColumn;
2299     xsh->insertColumn(col, column);
2300   }
2301   for (it = frames.begin(); it != frames.end(); ++it) {
2302     xsh->setCell(row, col, TXshCell(level, *it));
2303     ++row;
2304   }
2305   xh->notifyXsheetChanged();
2306 }
2307 
2308 //-----------------------------------------------------------------------------
2309 
2310 class MoveLevelToSceneUndo final : public TUndo {
2311   std::wstring m_levelName;
2312   int m_col;
2313   std::set<TFrameId> m_fids;
2314 
2315 public:
MoveLevelToSceneUndo(std::wstring levelName,int col,std::set<TFrameId> fids)2316   MoveLevelToSceneUndo(std::wstring levelName, int col, std::set<TFrameId> fids)
2317       : m_levelName(levelName), m_col(col), m_fids(fids) {}
2318 
undo() const2319   void undo() const override {
2320     TApp *app         = TApp::instance();
2321     TXsheet *xsh      = app->getCurrentXsheet()->getXsheet();
2322     ToonzScene *scene = app->getCurrentScene()->getScene();
2323     TXshLevel *xl     = scene->getLevelSet()->getLevel(m_levelName);
2324     if (xl->getPaletteLevel()) xsh->removeColumn(m_col);
2325     xsh->clearCells(0, m_col, m_fids.size());
2326     app->getCurrentXsheet()->notifyXsheetChanged();
2327   }
redo() const2328   void redo() const override {
2329     TApp *app         = TApp::instance();
2330     ToonzScene *scene = app->getCurrentScene()->getScene();
2331     TXshLevel *xl     = scene->getLevelSet()->getLevel(m_levelName);
2332     if (!xl) return;
2333     moveToSceneFrames(xl, m_fids);
2334   }
getSize() const2335   int getSize() const override { return sizeof *this; }
2336 
getHistoryString()2337   QString getHistoryString() override {
2338     return QObject::tr("Move Level to Scene  : Level %1")
2339         .arg(QString::fromStdWString(m_levelName));
2340   }
getHistoryType()2341   int getHistoryType() override { return HistoryType::FilmStrip; }
2342 };
2343 
2344 }  // namespace
2345 
2346 //=============================================================================
2347 // moveToScene
2348 //-----------------------------------------------------------------------------
2349 
moveToScene(TXshLevel * sl,std::set<TFrameId> & frames)2350 void FilmstripCmd::moveToScene(TXshLevel *sl, std::set<TFrameId> &frames) {
2351   if (frames.empty() || !sl) return;
2352 
2353   TXsheetHandle *xh = TApp::instance()->getCurrentXsheet();
2354   TXsheet *xsh      = xh->getXsheet();
2355   int row           = 0;
2356   int col           = xsh->getFirstFreeColumnIndex();
2357   std::set<TFrameId>::const_iterator it;
2358   for (it = frames.begin(); it != frames.end(); ++it) {
2359     xsh->setCell(row, col, TXshCell(sl, *it));
2360     ++row;
2361   }
2362   xh->notifyXsheetChanged();
2363   TUndoManager::manager()->add(
2364       new MoveLevelToSceneUndo(sl->getName(), col, frames));
2365   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2366 }
2367 
2368 //-----------------------------------------------------------------------------
2369 
moveToScene(TXshSimpleLevel * sl)2370 void FilmstripCmd::moveToScene(TXshSimpleLevel *sl) {
2371   std::vector<TFrameId> fids;
2372   sl->getFids(fids);
2373   std::set<TFrameId> fidsSet(fids.begin(), fids.end());
2374   moveToScene(sl, fidsSet);
2375 }
2376 
2377 //-----------------------------------------------------------------------------
2378 
moveToScene(TXshPaletteLevel * pl)2379 void FilmstripCmd::moveToScene(TXshPaletteLevel *pl) {
2380   if (!pl) return;
2381   std::set<TFrameId> fidsSet;
2382   fidsSet.insert(TFrameId(1));
2383 
2384   TXsheetHandle *xh         = TApp::instance()->getCurrentXsheet();
2385   TXsheet *xsh              = xh->getXsheet();
2386   int row                   = 0;
2387   int col                   = xsh->getFirstFreeColumnIndex();
2388   TXshPaletteColumn *column = new TXshPaletteColumn;
2389   xsh->insertColumn(col, column);
2390   std::set<TFrameId>::const_iterator it;
2391   for (it = fidsSet.begin(); it != fidsSet.end(); ++it) {
2392     xsh->setCell(row, col, TXshCell(pl, *it));
2393     ++row;
2394   }
2395   xh->notifyXsheetChanged();
2396   TUndoManager::manager()->add(
2397       new MoveLevelToSceneUndo(pl->getName(), col, fidsSet));
2398   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2399 }
2400 
2401 //-----------------------------------------------------------------------------
2402 
moveToScene(TXshSoundLevel * sl)2403 void FilmstripCmd::moveToScene(TXshSoundLevel *sl) {
2404   std::vector<TFrameId> fids;
2405   sl->getFids(fids);
2406   std::set<TFrameId> fidsSet(fids.begin(), fids.end());
2407   moveToScene(sl, fidsSet);
2408 }
2409 
2410 //=============================================================================
2411 // UndoInbetween
2412 //-----------------------------------------------------------------------------
2413 
2414 namespace {
2415 
2416 class UndoInbetween final : public TUndo {
2417   TXshSimpleLevelP m_level;
2418   std::vector<TFrameId> m_fids;
2419   std::vector<TVectorImageP> m_images;
2420   FilmstripCmd::InbetweenInterpolation m_interpolation;
2421 
2422 public:
UndoInbetween(TXshSimpleLevel * xl,std::vector<TFrameId> fids,FilmstripCmd::InbetweenInterpolation interpolation)2423   UndoInbetween(TXshSimpleLevel *xl, std::vector<TFrameId> fids,
2424                 FilmstripCmd::InbetweenInterpolation interpolation)
2425       : m_level(xl), m_fids(fids), m_interpolation(interpolation) {
2426     std::vector<TFrameId>::iterator it = fids.begin();
2427     // mi salvo tutte le immagine
2428     for (; it != fids.end(); ++it)
2429       m_images.push_back(m_level->getFrame(
2430           *it, false));  // non si fa clone perche' il livello subito dopo
2431                          // rilascia queste immagini a causa dell'inbetweener
2432   }
2433 
undo() const2434   void undo() const override {
2435     UINT levelSize = m_fids.size() - 1;
2436     for (UINT count = 1; count != levelSize; count++) {
2437       TVectorImageP vImage = m_images[count];
2438       m_level->setFrame(m_fids[count], vImage);
2439       IconGenerator::instance()->invalidate(m_level.getPointer(),
2440                                             m_fids[count]);
2441     }
2442 
2443     TApp::instance()->getCurrentLevel()->notifyLevelChange();
2444   }
2445 
redo() const2446   void redo() const override {
2447     TFrameId fid0 = *m_fids.begin();
2448     TFrameId fid1 = *(--m_fids.end());
2449     FilmstripCmd::inbetweenWithoutUndo(m_level.getPointer(), fid0, fid1,
2450                                        m_interpolation);
2451   }
2452 
getSize() const2453   int getSize() const override {
2454     assert(!m_images.empty());
2455     return m_images.size() * m_images.front()->getStrokeCount() * 100;
2456   }
2457 
getHistoryString()2458   QString getHistoryString() override {
2459     QString str = QObject::tr("Inbetween  : Level %1,  ")
2460                       .arg(QString::fromStdWString(m_level->getName()));
2461     switch (m_interpolation) {
2462     case FilmstripCmd::II_Linear:
2463       str += QString("Linear Interpolation");
2464       break;
2465     case FilmstripCmd::II_EaseIn:
2466       str += QString("Ease In Interpolation");
2467       break;
2468     case FilmstripCmd::II_EaseOut:
2469       str += QString("Ease Out Interpolation");
2470       break;
2471     case FilmstripCmd::II_EaseInOut:
2472       str += QString("Ease In-Out Interpolation");
2473       break;
2474     }
2475     return str;
2476   }
getHistoryType()2477   int getHistoryType() override { return HistoryType::FilmStrip; }
2478 };
2479 
2480 }  // namespace
2481 
2482 //=============================================================================
2483 // inbetween
2484 //-----------------------------------------------------------------------------
2485 
inbetweenWithoutUndo(TXshSimpleLevel * sl,const TFrameId & fid0,const TFrameId & fid1,FilmstripCmd::InbetweenInterpolation interpolation)2486 void FilmstripCmd::inbetweenWithoutUndo(
2487     TXshSimpleLevel *sl, const TFrameId &fid0, const TFrameId &fid1,
2488     FilmstripCmd::InbetweenInterpolation interpolation) {
2489   if (!sl) return;
2490   std::vector<TFrameId> fids;
2491   sl->getFids(fids);
2492   std::vector<TFrameId>::iterator it;
2493   it = std::find(fids.begin(), fids.end(), fid0);
2494   if (it == fids.end()) return;
2495   int ia = std::distance(fids.begin(), it);
2496   it     = std::find(fids.begin(), fids.end(), fid1);
2497   if (it == fids.end()) return;
2498   int ib = std::distance(fids.begin(), it);
2499   if (ib - ia < 2) return;
2500 
2501   TVectorImageP img0 = sl->getFrame(fid0, false);
2502   TVectorImageP img1 = sl->getFrame(fid1, false);
2503   if (!img0 || !img1) return;
2504 
2505   enum TInbetween::TweenAlgorithm algorithm;
2506   switch (interpolation) {
2507   case II_Linear:
2508     algorithm = TInbetween::LinearInterpolation;
2509     break;
2510   case II_EaseIn:
2511     algorithm = TInbetween::EaseInInterpolation;
2512     break;
2513   case II_EaseOut:
2514     algorithm = TInbetween::EaseOutInterpolation;
2515     break;
2516   case II_EaseInOut:
2517     algorithm = TInbetween::EaseInOutInterpolation;
2518     break;
2519   }
2520 
2521   TInbetween inbetween(img0, img1);
2522   int i;
2523   for (i = ia + 1; i < ib; i++) {
2524     double t = (double)(i - ia) / (double)(ib - ia);
2525     double s = TInbetween::interpolation(t, algorithm);
2526 
2527     TVectorImageP vi = inbetween.tween(s);
2528     sl->setFrame(fids[i], vi);
2529     IconGenerator::instance()->invalidate(sl, fids[i]);
2530   }
2531   sl->setDirtyFlag(true);
2532   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2533 }
2534 
2535 //-----------------------------------------------------------------------------
2536 
inbetween(TXshSimpleLevel * sl,const TFrameId & fid0,const TFrameId & fid1,FilmstripCmd::InbetweenInterpolation interpolation)2537 void FilmstripCmd::inbetween(
2538     TXshSimpleLevel *sl, const TFrameId &fid0, const TFrameId &fid1,
2539     FilmstripCmd::InbetweenInterpolation interpolation) {
2540   std::vector<TFrameId> fids;
2541   std::vector<TFrameId> levelFids;
2542   sl->getFids(levelFids);
2543   for (auto const &fid : levelFids) {
2544     int curFid = fid.getNumber();
2545     if (fid0.getNumber() <= curFid && curFid <= fid1.getNumber())
2546       fids.push_back(fid);
2547   }
2548 
2549   TUndoManager::manager()->add(new UndoInbetween(sl, fids, interpolation));
2550 
2551   inbetweenWithoutUndo(sl, fid0, fid1, interpolation);
2552   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2553 }
2554 
2555 //-----------------------------------------------------------------------------
2556 
renumberDrawing(TXshSimpleLevel * sl,const TFrameId & oldFid,const TFrameId & desiredNewFid)2557 void FilmstripCmd::renumberDrawing(TXshSimpleLevel *sl, const TFrameId &oldFid,
2558                                    const TFrameId &desiredNewFid) {
2559   if (oldFid == desiredNewFid) return;
2560   std::vector<TFrameId> fids;
2561   std::vector<TFrameId> oldFrames;
2562   sl->getFids(oldFrames);
2563   sl->getFids(fids);
2564   std::vector<TFrameId>::iterator it =
2565       std::find(fids.begin(), fids.end(), oldFid);
2566   if (it == fids.end()) return;
2567   TFrameId newFid = desiredNewFid;
2568   while (std::find(fids.begin(), fids.end(), newFid) != fids.end()) {
2569     char letter = newFid.getLetter();
2570     if (letter == 'z') return;
2571     if (letter == 0)
2572       letter = 'a';
2573     else
2574       letter++;
2575     newFid = TFrameId(newFid.getNumber(), letter);
2576   }
2577   *it = newFid;
2578   if (Preferences::instance()->isSyncLevelRenumberWithXsheetEnabled()) {
2579     updateXSheet(sl, oldFrames, fids);
2580   }
2581   sl->renumber(fids);
2582   sl->setDirtyFlag(true);
2583 
2584   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
2585   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2586   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
2587 }
2588