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