1 #include "tapp.h"
2 #include "tpalette.h"
3 #include "toonz/txsheet.h"
4 #include "toonz/toonzscene.h"
5 #include "toonz/levelset.h"
6 #include "toonz/txshsimplelevel.h"
7 #include "toonz/txshlevelcolumn.h"
8 #include "toonz/txshcell.h"
9 //#include "tw/action.h"
10 #include "tropcm.h"
11 #include "ttoonzimage.h"
12 #include "matchline.h"
13 #include "toonz/scenefx.h"
14 #include "toonz/dpiscale.h"
15 #include "toonz/txsheethandle.h"
16 #include "toonz/palettecontroller.h"
17 #include "toonz/tpalettehandle.h"
18 #include "toonz/txshlevelhandle.h"
19 #include "toonz/txshleveltypes.h"
20 #include "toonz/tscenehandle.h"
21 #include "toonz/tframehandle.h"
22 #include "toonz/preferences.h"
23 #include "toonzqt/icongenerator.h"
24 #include <map>
25 #include <QRadioButton>
26 #include <QPushButton>
27 #include <QApplication>
28 #include "tundo.h"
29 #include "tools/toolutils.h"
30 #include "timagecache.h"
31 #include "tcolorstyles.h"
32 #include "toonz/levelproperties.h"
33 #include "toonz/childstack.h"
34 #include "toonz/toonzimageutils.h"
35 #include "tpaletteutil.h"
36 
37 #include <algorithm>
38 
39 using namespace DVGui;
40 
41 namespace {
42 
43 class MergeCmappedPair {
44 public:
45   const TXshCell *m_cell;
46   TAffine m_imgAff;
47   const TXshCell *m_mcell;
48   TAffine m_matchAff;
49 
MergeCmappedPair(const TXshCell & cell,const TAffine & imgAff,const TXshCell & mcell,const TAffine & matchAff)50   MergeCmappedPair(const TXshCell &cell, const TAffine &imgAff,
51                    const TXshCell &mcell, const TAffine &matchAff)
52       : m_cell(&cell)
53       , m_imgAff(imgAff)
54       , m_mcell(&mcell)
55       , m_matchAff(matchAff){};
56 };
57 
mergeCmapped(const std::vector<MergeCmappedPair> & matchingLevels)58 void mergeCmapped(const std::vector<MergeCmappedPair> &matchingLevels) {
59   if (matchingLevels.empty()) return;
60 
61   TPalette *palette = matchingLevels[0].m_cell->getImage(false)->getPalette();
62   TPalette *matchPalette =
63       matchingLevels[0].m_mcell->getImage(false)->getPalette();
64 
65   TPalette::Page *page;
66 
67   // upInkId -> downInkId
68   std::map<int, int> usedColors;
69 
70   int i = 0;
71   for (i = 0; i < (int)matchingLevels.size(); i++) {
72     TToonzImageP img = (TToonzImageP)matchingLevels[i].m_cell->getImage(true);
73     TToonzImageP match =
74         (TToonzImageP)matchingLevels[i].m_mcell->getImage(false);
75     if (!img || !match)
76       throw TRopException("Can merge only cmapped raster images!");
77 
78     std::set<int> usedStyles;
79     ToonzImageUtils::getUsedStyles(usedStyles, match);
80     std::map<int, int> indexTable;
81     mergePalette(palette, indexTable, matchPalette, usedStyles);
82     ToonzImageUtils::scrambleStyles(match, indexTable);
83     match->setPalette(palette);
84     matchPalette = palette;
85 
86     // img->lock();
87     TRasterCM32P ras      = img->getRaster();    // img->getCMapped(false);
88     TRasterCM32P matchRas = match->getRaster();  // match->getCMapped(true);
89     if (!ras || !matchRas)
90       throw TRopException("Can merge only cmapped images!");
91 
92     TAffine aff =
93         matchingLevels[i].m_imgAff.inv() * matchingLevels[i].m_matchAff;
94     int mlx = matchRas->getLx();
95     int mly = matchRas->getLy();
96     int rlx = ras->getLx();
97     int rly = ras->getLy();
98 
99     TRectD in = convert(matchRas->getBounds()) - matchRas->getCenterD();
100 
101     TRectD out = aff * in;
102 
103     TPoint offs((rlx - mlx) / 2 + tround(out.getP00().x - in.getP00().x),
104                 (rly - mly) / 2 + tround(out.getP00().y - in.getP00().y));
105 
106     int lxout = tround(out.getLx()) + 1 + ((offs.x < 0) ? offs.x : 0);
107     int lyout = tround(out.getLy()) + 1 + ((offs.y < 0) ? offs.y : 0);
108 
109     if (lxout <= 0 || lyout <= 0 || offs.x >= rlx || offs.y >= rly) {
110       // tmsg_error("no intersections between matchline and level");
111       continue;
112     }
113 
114     aff = aff.place((double)(in.getLx() / 2.0), (double)(in.getLy() / 2.0),
115                     (out.getLx()) / 2.0 + ((offs.x < 0) ? offs.x : 0),
116                     (out.getLy()) / 2.0 + ((offs.y < 0) ? offs.y : 0));
117 
118     if (offs.x < 0) offs.x = 0;
119     if (offs.y < 0) offs.y = 0;
120 
121     if (lxout + offs.x > rlx || lyout + offs.y > rly) {
122       // PRINTF("TAGLIO L'IMMAGINE\n");
123       lxout = (lxout + offs.x > rlx) ? (rlx - offs.x) : lxout;
124       lyout = (lyout + offs.y > rly) ? (rly - offs.y) : lyout;
125     }
126 
127     if (!aff.isIdentity(1e-4)) {
128       TRasterCM32P aux(lxout, lyout);
129       TRop::resample(aux, matchRas, aff);
130       matchRas = aux;
131     }
132     ras->lock();
133     matchRas->lock();
134     TRect raux = matchRas->getBounds() + offs;
135     TRasterP r = ras->extract(raux);
136     TRop::overlayCmapped(r, matchRas, palette, matchPalette, usedColors);
137     ras->unlock();
138     matchRas->unlock();
139 
140     img->setSavebox(img->getSavebox() + (matchRas->getBounds() + offs));
141   }
142   /*
143     std::map<int, int>::iterator it = usedColors.begin();
144     for (; it != usedColors.end(); ++it)
145       if (it->first != it->second) break;
146 
147     if (it == usedColors.end())  // this means that the merged palette does not
148                                  // differ from source palette.(all usedColors
149                                  // are not new color )
150       return;
151 
152     std::wstring pageName = L"merged palettes";
153 
154     for (i = 0; i < palette->getPageCount(); i++)
155       if (palette->getPage(i)->getName() == pageName) {
156         page = palette->getPage(i);
157         break;
158       }
159     if (i == palette->getPageCount()) page = palette->addPage(pageName);
160 
161     it        = usedColors.begin();
162     int count = 0;
163     for (; it != usedColors.end(); ++it) {
164       if (it->first == it->second) continue;
165       while (palette->getStyleCount() <= it->second)
166         palette->addStyle(TPixel32::Red);
167       palette->setStyle(it->second, matchPalette->getStyle(it->first)->clone());
168       page->addStyle(it->second);
169     }
170     if (usedColors.size() > 0) palette->setDirtyFlag(true);
171   */
172 }
173 
174 /*------------------------------------------------------------------------*/
175 
applyDeleteMatchline(TXshSimpleLevel * sl,const std::vector<TFrameId> & fids,const std::vector<int> & _inkIndexes)176 void applyDeleteMatchline(TXshSimpleLevel *sl,
177                           const std::vector<TFrameId> &fids,
178                           const std::vector<int> &_inkIndexes) {
179   TPalette::Page *page = 0;
180   int i, j, pageIndex = 0;
181   std::vector<int> inkIndexes = _inkIndexes;
182 
183   if (fids.empty()) return;
184 
185   TPalette *palette = 0;
186 
187   if (inkIndexes.empty()) {
188     palette = sl->getFrame(fids[0], true)->getPalette();
189 
190     for (i = 0; i < palette->getPageCount(); i++)
191       if (palette->getPage(i)->getName() == L"match lines") {
192         page = palette->getPage(i);
193         break;
194       }
195 
196     if (!page) return;
197     pageIndex = i;
198   }
199 
200   for (i = 0; i < (int)fids.size(); i++) {
201     // level[i]->lock();
202     TToonzImageP image = sl->getFrame(fids[i], true);
203     assert(image);
204     TRasterCM32P ras = image->getRaster();  // level[i]->getCMapped(false);
205     ras->lock();
206     if (inkIndexes.empty())
207       for (j = 0; j < page->getStyleCount(); j++)
208         inkIndexes.push_back(page->getStyleId(j));
209 
210     TRop::eraseColors(ras, &inkIndexes, true);
211 
212     ras->unlock();
213     TRect savebox;
214     TRop::computeBBox(ras, savebox);
215     image->setSavebox(savebox);
216     // level[i]->unlock();
217   }
218 
219   // if (page)
220   //  {
221   //  while (page->getStyleCount())
222   //   page->removeStyle(0);
223   // palette->erasePage(pageIndex);
224   // }
225 }
226 
227 /*------------------------------------------------------------------------*/
228 }  // namespace
229 //-----------------------------------------------------------------------------
230 
231 class DeleteMatchlineUndo final : public TUndo {
232 public:
233   TXshLevel *m_xl;
234   TXshSimpleLevel *m_sl;
235   std::vector<TFrameId> m_fids;
236   std::vector<int> m_indexes;
237   TPaletteP m_matchlinePalette;
238 
DeleteMatchlineUndo(TXshLevel * xl,TXshSimpleLevel * sl,const std::vector<TFrameId> & fids,const std::vector<int> & indexes)239   DeleteMatchlineUndo(
240       TXshLevel *xl, TXshSimpleLevel *sl, const std::vector<TFrameId> &fids,
241       const std::vector<int> &indexes)  //, TPalette*matchPalette)
242       : TUndo(),
243         m_xl(xl),
244         m_sl(sl),
245         m_fids(fids),
246         m_indexes(indexes)
247   //, m_matchlinePalette(matchPalette->clone())
248   {
249     // assert(matchPalette);
250     int i;
251     for (i = 0; i < fids.size(); i++) {
252       QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t) this) +
253                    "-" + QString::number(i);
254       TToonzImageP image = sl->getFrame(fids[i], false);
255       assert(image);
256       TImageCache::instance()->add(id, image->clone());
257     }
258   }
259 
undo() const260   void undo() const override {
261     int i;
262     // TPalette *palette = m_matchlinePalette->clone();
263     // m_sl->setPalette(palette);
264     for (i = 0; i < m_fids.size(); i++) {
265       QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t) this) +
266                    "-" + QString::number(i);
267       TImageP img = TImageCache::instance()->get(id, false)->cloneImage();
268 
269       m_sl->setFrame(m_fids[i], img);
270       ToolUtils::updateSaveBox(m_sl, m_fids[i]);
271     }
272     // TApp::instance()->getPaletteController()->getCurrentLevelPalette()->setPalette(palette);
273 
274     if (m_xl) invalidateIcons(m_xl, m_fids);
275     m_sl->setDirtyFlag(true);
276     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
277   }
278 
redo() const279   void redo() const override {
280     int i;
281 
282     // for (i=0; i<m_fids.size(); i++)
283     //  images.push_back(m_sl->getFrame(m_fids[i], true));
284 
285     applyDeleteMatchline(m_sl, m_fids, m_indexes);
286     for (i = 0; i < m_fids.size(); i++) {
287       ToolUtils::updateSaveBox(m_sl, m_fids[i]);
288     }
289     if (m_xl) invalidateIcons(m_xl, m_fids);
290     m_sl->setDirtyFlag(true);
291     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
292   }
293 
getSize() const294   int getSize() const override { return sizeof(*this); }
295 
~DeleteMatchlineUndo()296   ~DeleteMatchlineUndo() {
297     int i;
298     for (i = 0; i < m_fids.size(); i++)
299       TImageCache::instance()->remove("DeleteMatchlineUndo" +
300                                       QString::number((uintptr_t) this) + "-" +
301                                       QString::number(i));
302   }
303 };
304 
305 //-----------------------------------------------------------------------------
306 /*
307 namespace {
308 
309 class DeleteLevelUndo final : public TUndo {
310   TXshLevelP m_xl;
311 public:
312   DeleteLevelUndo(TXshLevel *xl) : m_xl(xl) {}
313 
314   void undo() const {
315                 ToonzScene *scene =
316 TApp::instance()->getCurrentScene()->getScene();
317     scene->getLevelSet()->insertLevel(m_xl.getPointer());
318                 TApp::instance()->getCurrentScene()->notifyCastChange();
319   }
320   void redo() const {
321                 ToonzScene *scene =
322 TApp::instance()->getCurrentScene()->getScene();
323     scene->getLevelSet()->removeLevel(m_xl.getPointer());
324     TApp::instance()->getCurrentScene()->notifyCastChange();
325   }
326 
327   int getSize() const {
328     return sizeof *this + 100;
329   }
330 };
331 
332 } //namespace
333 */
334 
removeLevel(TXshLevel * level)335 static bool removeLevel(TXshLevel *level) {
336   TApp *app         = TApp::instance();
337   ToonzScene *scene = app->getCurrentScene()->getScene();
338   // if(scene->getChildStack()->getTopXsheet()->isLevelUsed(level))
339   //	DVGui::error(QObject::tr("It is not possible to delete the used level
340   //%1.").arg(QString::fromStdWString(level->getName())));//"E_CantDeleteUsedLevel_%1"
341   // else
342   {
343     // TUndoManager *um = TUndoManager::manager();
344     // um->add(new DeleteLevelUndo(level));
345     scene->getLevelSet()->removeLevel(level, false);
346     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
347     TApp::instance()->getCurrentScene()->notifyCastChange();
348   }
349   return true;
350 }
351 
352 class MergeCmappedUndo final : public TUndo {
353   TXshLevel *m_xl;
354   int m_mergeCmappedSessionId;
355   std::map<TFrameId, QString> m_images;
356   TXshSimpleLevel *m_level;
357   TPalette *m_palette;
358   int m_column, m_mColumn;
359   std::wstring m_fullpath;
360 
361 public:
MergeCmappedUndo(TXshLevel * xl,int mergeCmappedSessionId,int column,TXshSimpleLevel * level,const std::map<TFrameId,QString> & images,int mColumn,TPalette * palette)362   MergeCmappedUndo(TXshLevel *xl, int mergeCmappedSessionId, int column,
363                    TXshSimpleLevel *level,
364                    const std::map<TFrameId, QString> &images, int mColumn,
365                    TPalette *palette)
366       : TUndo()
367       , m_xl(xl)
368       , m_mergeCmappedSessionId(mergeCmappedSessionId)
369       , m_palette(palette->clone())
370       , m_level(level)
371       , m_column(column)
372       , m_mColumn(mColumn)
373       , m_images(images)
374 
375   {
376     m_fullpath = m_xl->getPath().getWideString();
377   }
378 
undo() const379   void undo() const override {
380     std::map<TFrameId, QString>::const_iterator it = m_images.begin();
381     TPalette *palette = m_palette->clone();
382     m_level->setPalette(palette);
383     std::vector<TFrameId> fids;
384     for (; it != m_images.end(); ++it)  //, ++mit)
385     {
386       QString id = "MergeCmappedUndo" +
387                    QString::number(m_mergeCmappedSessionId) + "-" +
388                    QString::number(it->first.getNumber());
389       TImageP img = TImageCache::instance()->get(id, false)->cloneImage();
390       img->setPalette(palette);
391       m_level->setFrame(it->first, img);
392       fids.push_back(it->first);
393     }
394 
395     removeLevel(m_xl);
396 
397     TApp::instance()
398         ->getPaletteController()
399         ->getCurrentLevelPalette()
400         ->setPalette(palette);
401     m_level->setDirtyFlag(true);
402     TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
403   }
404 
redo() const405   void redo() const override {
406     mergeCmapped(m_column, m_mColumn, QString::fromStdWString(m_fullpath),
407                  true);
408   }
409 
getSize() const410   int getSize() const override { return sizeof(*this); }
411 
~MergeCmappedUndo()412   ~MergeCmappedUndo() {
413     std::map<TFrameId, QString>::const_iterator it = m_images.begin();
414     for (; it != m_images.end(); ++it)  //, ++mit)
415     {
416       QString id = "MergeCmappedUndo" + QString::number((uintptr_t) this) +
417                    "-" + QString::number(it->first.getNumber());
418       TImageCache::instance()->remove(id);
419     }
420     delete m_palette;
421   }
422 };
423 
424 //-----------------------------------------------------------------------------
425 
426 static int LastMatchlineIndex = -1;
427 
428 class MergedPair {
429 public:
430   TFrameId m_f0, m_f1;
431   TAffine m_aff;
MergedPair(const TFrameId & f0,const TFrameId & f1,const TAffine & aff)432   MergedPair(const TFrameId &f0, const TFrameId &f1, const TAffine &aff)
433       : m_f0(f0), m_f1(f1), m_aff(aff) {}
434 
operator <(const MergedPair & p) const435   bool operator<(const MergedPair &p) const {
436     if (m_f0 != p.m_f0) return m_f0 < p.m_f0;
437     if (m_f1 != p.m_f1) return m_f1 < p.m_f1;
438     if (m_aff.a11 != p.m_aff.a11) return m_aff.a11 < p.m_aff.a11;
439     if (m_aff.a12 != p.m_aff.a12) return m_aff.a12 < p.m_aff.a12;
440     if (m_aff.a13 != p.m_aff.a13) return m_aff.a13 < p.m_aff.a13;
441     if (m_aff.a21 != p.m_aff.a21) return m_aff.a21 < p.m_aff.a21;
442     if (m_aff.a22 != p.m_aff.a22) return m_aff.a22 < p.m_aff.a22;
443     if (m_aff.a23 != p.m_aff.a23) return m_aff.a23 < p.m_aff.a23;
444     return false;
445   }
446 };
447 
448 //--------------------------------------------------------------------
449 
mergeCmapped(int column,int mColumn,const QString & fullpath,bool isRedo)450 void mergeCmapped(int column, int mColumn, const QString &fullpath,
451                   bool isRedo) {
452   static int MergeCmappedSessionId = 0;
453   MergeCmappedSessionId++;
454 
455   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
456   int start, end;
457   int mStart, mEnd;
458   xsh->getCellRange(column, start, end);
459   xsh->getCellRange(mColumn, mStart, mEnd);
460 
461   if (start > end) return;
462   std::vector<TXshCell> cell(std::max(end, mEnd) - std::min(start, mStart) + 1);
463   std::vector<TXshCell> mCell(cell.size());
464 
465   xsh->getCells(std::min(start, mStart), column, cell.size(), &(cell[0]));
466 
467   if (mColumn != -1)
468     xsh->getCells(std::min(start, mStart), mColumn, cell.size(), &(mCell[0]));
469 
470   TXshColumn *col  = xsh->getColumn(column);
471   TXshColumn *mcol = xsh->getColumn(mColumn);
472 
473   std::vector<MergeCmappedPair> matchingLevels;
474 
475   std::map<MergedPair, TFrameId> computedMergedMap;
476 
477   TXshSimpleLevel *level = 0, *mLevel = 0;
478   TXshLevel *xl;
479 
480   std::map<TFrameId, QString> images;
481   double dpix = 0, dpiy = 0;
482   for (int i = 0; i < (int)cell.size(); i++) {
483     if (!cell[i].isEmpty() && dpix == 0)
484       ((TToonzImageP)(cell[i].getImage(false)))->getDpi(dpix, dpiy);
485 
486     if (!level) {
487       level = cell[i].getSimpleLevel();
488       xl    = cell[i].m_level.getPointer();
489     }
490     if (!mLevel) mLevel = mCell[i].getSimpleLevel();
491   }
492 
493   if (!level || !mLevel) return;
494 
495   TFilePath fp(fullpath.toStdString());
496 
497   TXshLevel *txl = level->getScene()->createNewLevel(
498       level->getType(), fp.getWideName(), level->getResolution());
499   TXshSimpleLevel *newLevel = txl->getSimpleLevel();
500   newLevel->setPath(fp);
501   newLevel->setPalette(level->getPalette());
502   newLevel->clonePropertiesFrom(level);
503   //  newLevel->setPath(fp);
504 
505   TApp::instance()->getCurrentScene()->notifySceneChanged();
506   TApp::instance()->getCurrentScene()->notifyCastChange();
507   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
508 
509   int count = 0;
510   for (int i = 0; i < (int)cell.size(); i++) {
511     if (cell[i].isEmpty() && mCell[i].isEmpty()) continue;
512 
513     TAffine imgAff, matchAff;
514 
515     getColumnPlacement(imgAff, xsh, std::min(start, mStart) + i, column, false);
516     getColumnPlacement(matchAff, xsh, std::min(start, mStart) + i, mColumn,
517                        false);
518 
519     // std::map<TFrameId, TFrameId>::iterator it;
520     MergedPair mp(cell[i].isEmpty() ? TFrameId() : cell[i].getFrameId(),
521                   mCell[i].isEmpty() ? TFrameId() : mCell[i].getFrameId(),
522                   imgAff.inv() * matchAff);
523 
524     std::map<MergedPair, TFrameId>::iterator computedMergedIt =
525         computedMergedMap.find(mp);
526 
527     if (computedMergedIt != computedMergedMap.end()) {
528       TXshCell newCell(newLevel, computedMergedIt->second);
529       xsh->setCell(i, column, newCell);
530       cell[i] = newCell;
531       continue;
532     }
533 
534     TFrameId newFid(++count);  // level->getLastFid().getNumber()+1);
535     TDimension dim = level->getResolution();
536     TToonzImageP newImage;
537     if (cell[i].isEmpty()) {
538       newImage =
539           TToonzImageP(TRasterCM32P(dim), TRect(0, 0, dim.lx - 1, dim.ly - 1));
540       newImage->setDpi(dpix, dpiy);
541     } else
542       newImage = (TToonzImageP)(cell[i].getImage(false)->cloneImage());
543 
544     newImage->setPalette(level->getPalette());
545 
546     newLevel->setFrame(newFid, newImage);
547     TXshCell newCell(newLevel, newFid);
548     xsh->setCell(i, column, newCell);
549     computedMergedMap[mp] = newCell.getFrameId();
550 
551     cell[i] = newCell;
552 
553     TImageP img   = cell[i].getImage(true);
554     TImageP match = mCell[i].getImage(true);
555     TFrameId fid  = cell[i].m_frameId;
556     TFrameId mFid = mCell[i].m_frameId;
557 
558     if (!img || !match) continue;
559 
560     TToonzImageP timg   = (TToonzImageP)img;
561     TToonzImageP tmatch = (TToonzImageP)match;
562 
563     QString id = "MergeCmappedUndo" + QString::number(MergeCmappedSessionId) +
564                  "-" + QString::number(fid.getNumber());
565     TImageCache::instance()->add(id, timg->clone());
566     images[fid] = id;
567 
568     TAffine dpiAff  = getDpiAffine(level, fid);
569     TAffine mdpiAff = getDpiAffine(mLevel, mFid);
570     matchingLevels.push_back(MergeCmappedPair(cell[i], imgAff * dpiAff,
571                                               mCell[i], matchAff * mdpiAff));
572   }
573 
574   if (!isRedo) {
575     TPalette *plt = level->getPalette();
576 
577     TPaletteHandle *pltHandle = new TPaletteHandle();
578     pltHandle->setPalette(plt);
579     int styleCount = plt->getStyleCount();
580 
581     TUndoManager::manager()->add(new MergeCmappedUndo(
582         txl, MergeCmappedSessionId, column, level, images, mColumn, plt));
583   }
584 
585   removeLevel(xl);
586   QApplication::setOverrideCursor(Qt::WaitCursor);
587   mergeCmapped(matchingLevels);
588   QApplication::restoreOverrideCursor();
589 
590   for (int i = 0; i < (int)cell.size(); i++)  // the saveboxes must be updated
591   {
592     if (cell[i].isEmpty() || mCell[i].isEmpty()) continue;
593 
594     if (!cell[i].getImage(false) || !mCell[i].getImage(false)) continue;
595 
596     TXshSimpleLevel *sl = cell[i].getSimpleLevel();
597     const TFrameId &fid = cell[i].m_frameId;
598 
599     ToolUtils::updateSaveBox(sl, fid);
600     IconGenerator::instance()->invalidate(sl, fid);
601     sl->setDirtyFlag(true);
602   }
603 
604   newLevel->setDirtyFlag(true);
605   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
606 }
607 
608 namespace {
609 
findCell(int column,const TFrameId & fid)610 const TXshCell *findCell(int column, const TFrameId &fid) {
611   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
612   int i;
613   for (i = 0; i < xsh->getColumn(column)->getMaxFrame(); i++)
614     if (xsh->getCell(i, column).getFrameId() == fid)
615       return &(xsh->getCell(i, column));
616   return 0;
617 }
618 
contains(const std::vector<TFrameId> & v,const TFrameId & val)619 bool contains(const std::vector<TFrameId> &v, const TFrameId &val) {
620   int i;
621   for (i = 0; i < (int)v.size(); i++)
622     if (v[i] == val) return true;
623   return false;
624 }
625 
626 //-----------------------------------------------------------------------------
627 
indexes2string(const std::set<TFrameId> fids)628 QString indexes2string(const std::set<TFrameId> fids) {
629   if (fids.empty()) return "";
630 
631   QString str;
632 
633   std::set<TFrameId>::const_iterator it = fids.begin();
634 
635   str = QString::number(it->getNumber());
636 
637   while (it != fids.end()) {
638     std::set<TFrameId>::const_iterator it1 = it;
639     it1++;
640 
641     int lastVal = it->getNumber();
642     while (it1 != fids.end() && it1->getNumber() == lastVal + 1) {
643       lastVal = it1->getNumber();
644       it1++;
645     }
646 
647     if (lastVal != it->getNumber()) str += "-" + QString::number(lastVal);
648     if (it1 == fids.end()) return str;
649 
650     str += ", " + QString::number(it1->getNumber());
651 
652     it = it1;
653   }
654 
655   return str;
656 }
657 
658 //-----------------------------------------------------------------------------
659 
string2Indexes(const QString & values)660 std::vector<int> string2Indexes(const QString &values) {
661   std::vector<int> ret;
662   int i, j;
663   bool ok;
664   QStringList vals = values.split(',', QString::SkipEmptyParts);
665   for (i = 0; i < vals.size(); i++) {
666     if (vals.at(i).contains('-')) {
667       QStringList vals1 = vals.at(i).split('-', QString::SkipEmptyParts);
668       if (vals1.size() != 2) return std::vector<int>();
669       int from = vals1.at(0).toInt(&ok);
670       if (!ok) return std::vector<int>();
671       int to = vals1.at(1).toInt(&ok);
672       if (!ok) return std::vector<int>();
673 
674       for (j = std::min(from, to); j <= std::max(from, to); j++)
675         ret.push_back(j);
676     } else {
677       int val = vals.at(i).toInt(&ok);
678       if (!ok) return std::vector<int>();
679       ret.push_back(val);
680     }
681   }
682   std::sort(ret.begin(), ret.end());
683   return ret;
684 }
685 
686 }  // namespace
687 
688 //-----------------------------------------------------------------------------
689