1 #include "boardsettingspopup.h"
2 
3 // Tnz6 includes
4 #include "tapp.h"
5 #include "filebrowser.h"
6 
7 // TnzQt includes
8 #include "toonzqt/gutil.h"
9 #include "toonzqt/filefield.h"
10 #include "toonzqt/colorfield.h"
11 #include "toonzqt/intfield.h"
12 
13 // TnzLib includes
14 #include "toutputproperties.h"
15 #include "toonz/tscenehandle.h"
16 #include "toonz/toonzscene.h"
17 #include "toonz/sceneproperties.h"
18 #include "toonz/tcamera.h"
19 #include "toonz/boardsettings.h"
20 #include "toonz/toonzfolders.h"
21 
22 // TnzBase includes
23 #include "trasterfx.h"
24 
25 // TnzCore includes
26 #include "tsystem.h"
27 #include "tlevel_io.h"
28 
29 #include <QLineEdit>
30 #include <QLabel>
31 #include <QPushButton>
32 #include <QTextEdit>
33 #include <QComboBox>
34 #include <QFontComboBox>
35 #include <QHBoxLayout>
36 #include <QVBoxLayout>
37 #include <QGridLayout>
38 #include <QPainter>
39 #include <QRectF>
40 #include <QListWidget>
41 #include <QMap>
42 #include <QPixmap>
43 #include <QImageReader>
44 #include <QMouseEvent>
45 
46 BoardItem* BoardSettingsPopup::currentBoardItem = nullptr;
47 
48 namespace {
49 QMap<BoardItem::Type, QString> stringByItemType;
50 
currentBoardItem()51 BoardItem* currentBoardItem() { return BoardSettingsPopup::currentBoardItem; }
setCurrentBoardItem(BoardItem * item)52 void setCurrentBoardItem(BoardItem* item) {
53   BoardSettingsPopup::currentBoardItem = item;
54 }
55 
editListWidgetItem(QListWidgetItem * listItem,BoardItem * item)56 void editListWidgetItem(QListWidgetItem* listItem, BoardItem* item) {
57   QString itemText = item->getName() + "\n(" +
58                      stringByItemType.value(item->getType(), "Unknown type") +
59                      ")";
60   listItem->setText(itemText);
61 
62   if (item->getType() == BoardItem::Image) {
63     ToonzScene* scene        = TApp::instance()->getCurrentScene()->getScene();
64     TFilePath decodedImgPath = scene->decodeFilePath(item->getImgPath());
65     QPixmap iconPm           = QPixmap::fromImage(
66         QImage(decodedImgPath.getQString()).scaled(QSize(20, 20)));
67     listItem->setIcon(QIcon(iconPm));
68   } else {
69     QPixmap iconPm(20, 20);
70     iconPm.fill(item->getColor());
71     listItem->setIcon(QIcon(iconPm));
72   }
73 }
74 }  // namespace
75 
76 //=============================================================================
77 
BoardView(QWidget * parent)78 BoardView::BoardView(QWidget* parent) : QWidget(parent) {
79   setMouseTracking(true);
80 }
81 
paintEvent(QPaintEvent * event)82 void BoardView::paintEvent(QPaintEvent* event) {
83   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
84   if (!scene) return;
85   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
86   BoardSettings* boardSettings = outProp->getBoardSettings();
87 
88   QPainter p(this);
89 
90   p.fillRect(rect(), Qt::black);
91 
92   // Duration must be more than 0 to save the clapperboard.
93   // Scene file can maintain compatibility with older versions if you set the
94   // duration to 0.
95   if (boardSettings->getDuration() == 0) {
96     p.setPen(Qt::white);
97     QFont font = p.font();
98     font.setPixelSize(30);
99     p.setFont(font);
100     p.drawText(
101         rect().adjusted(5, 5, -5, -5), Qt::AlignCenter | Qt::TextWordWrap,
102         tr("Please set the duration more than 0 frame first, or the "
103            "clapperboard settings will not be saved in the scene at all!"));
104 
105     p.setPen(QPen(Qt::red, 2));
106     p.drawLine(5, 5, 150, 5);
107     p.drawLine(5, 10, 150, 10);
108 
109     return;
110   }
111 
112   if (!m_valid) {
113     int shrinkX          = outProp->getRenderSettings().m_shrinkX,
114         shrinkY          = outProp->getRenderSettings().m_shrinkY;
115     TDimension frameSize = scene->getCurrentCamera()->getRes();
116     TDimension cameraRes(frameSize.lx / shrinkX, frameSize.ly / shrinkY);
117 
118     m_boardImg = boardSettings->getBoardImage(cameraRes, shrinkX, scene);
119     m_valid    = true;
120   }
121 
122   p.drawImage(m_boardImgRect, m_boardImg);
123 
124   p.translate(m_boardImgRect.topLeft());
125 
126   p.setBrush(Qt::NoBrush);
127   for (int i = 0; i < boardSettings->getItemCount(); i++) {
128     BoardItem* item = &(boardSettings->getItem(i));
129     if (item == currentBoardItem()) {
130       p.setPen(QPen(QColor(255, 100, 0), 2));
131     } else {
132       p.setPen(QPen(QColor(64, 64, 255), 1, Qt::DashLine));
133     }
134     p.drawRect(item->getItemRect(m_boardImgRect.size().toSize()));
135   }
136 }
137 
resizeEvent(QResizeEvent * event)138 void BoardView::resizeEvent(QResizeEvent* event) {
139   QSize boardRes;
140   if (m_valid) {
141     boardRes = m_boardImg.size();
142   } else {
143     ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
144     TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
145     int shrinkX                = outProp->getRenderSettings().m_shrinkX,
146         shrinkY                = outProp->getRenderSettings().m_shrinkY;
147     TDimension frameSize       = scene->getCurrentCamera()->getRes();
148     boardRes = QSize(frameSize.lx / shrinkX, frameSize.ly / shrinkY);
149   }
150 
151   float ratio = std::min((float)width() / (float)boardRes.width(),
152                          (float)height() / (float)boardRes.height());
153   QSizeF imgSize = QSizeF(boardRes) * ratio;
154   QPointF imgTopLeft(((float)width() - imgSize.width()) * 0.5,
155                      ((float)height() - imgSize.height()) * 0.5);
156 
157   m_boardImgRect = QRectF(imgTopLeft, imgSize);
158 }
159 
mouseMoveEvent(QMouseEvent * event)160 void BoardView::mouseMoveEvent(QMouseEvent* event) {
161   // mouse move - change drag item
162   if (!(event->buttons() & Qt::LeftButton)) {
163     if (!currentBoardItem()) {
164       m_dragItem = None;
165       setCursor(Qt::ArrowCursor);
166       return;
167     }
168 
169     QPointF posOnImg = QPointF(event->pos()) - m_boardImgRect.topLeft();
170 
171     QRectF itemRect =
172         currentBoardItem()->getItemRect(m_boardImgRect.size().toSize());
173 
174     qreal distance = 10.0;
175 
176     if ((itemRect.topLeft() - posOnImg).manhattanLength() < distance) {
177       m_dragItem = TopLeftCorner;
178       setCursor(Qt::SizeFDiagCursor);
179     } else if ((itemRect.topRight() - posOnImg).manhattanLength() < distance) {
180       m_dragItem = TopRightCorner;
181       setCursor(Qt::SizeBDiagCursor);
182     } else if ((itemRect.bottomRight() - posOnImg).manhattanLength() <
183                distance) {
184       m_dragItem = BottomRightCorner;
185       setCursor(Qt::SizeFDiagCursor);
186     } else if ((itemRect.bottomLeft() - posOnImg).manhattanLength() <
187                distance) {
188       m_dragItem = BottomLeftCorner;
189       setCursor(Qt::SizeBDiagCursor);
190     } else if (std::abs(itemRect.top() - posOnImg.y()) < distance &&
191                itemRect.left() < posOnImg.x() &&
192                itemRect.right() > posOnImg.x()) {
193       m_dragItem = TopEdge;
194       setCursor(Qt::SizeVerCursor);
195     } else if (std::abs(itemRect.right() - posOnImg.x()) < distance &&
196                itemRect.top() < posOnImg.y() &&
197                itemRect.bottom() > posOnImg.y()) {
198       m_dragItem = RightEdge;
199       setCursor(Qt::SizeHorCursor);
200     } else if (std::abs(itemRect.bottom() - posOnImg.y()) < distance &&
201                itemRect.left() < posOnImg.x() &&
202                itemRect.right() > posOnImg.x()) {
203       m_dragItem = BottomEdge;
204       setCursor(Qt::SizeVerCursor);
205     } else if (std::abs(itemRect.left() - posOnImg.x()) < distance &&
206                itemRect.top() < posOnImg.y() &&
207                itemRect.bottom() > posOnImg.y()) {
208       m_dragItem = LeftEdge;
209       setCursor(Qt::SizeHorCursor);
210     } else if (itemRect.contains(posOnImg)) {
211       m_dragItem = Translate;
212       setCursor(Qt::SizeAllCursor);
213     } else {
214       m_dragItem = None;
215       setCursor(Qt::ArrowCursor);
216     }
217 
218     return;
219   }
220 
221   // left mouse drag
222   if (m_dragItem == None) return;
223 
224   if (m_dragStartItemRect.isNull()) return;
225 
226   QPointF posOnImg = QPointF(event->pos()) - m_boardImgRect.topLeft();
227   QPointF ratioPos = QPointF(posOnImg.x() / m_boardImgRect.width(),
228                              posOnImg.y() / m_boardImgRect.height());
229 
230   // with alt : center resize
231   bool altPressed = event->modifiers() & Qt::AltModifier;
232   // with shift : align to discrete position
233   bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
234 
235   auto adjVal = [&](double p) {
236     double step = 0.05;
237     if (!shiftPressed) return p;
238     return std::round(p / step) * step;
239   };
240   auto adjPos = [&](QPointF p) {
241     if (!shiftPressed) return p;
242     return QPointF(adjVal(p.x()), adjVal(p.y()));
243   };
244 
245   QRectF newRect = m_dragStartItemRect;
246   QPointF d      = ratioPos - m_dragStartPos;
247   switch (m_dragItem) {
248   case Translate:
249     newRect.translate(ratioPos - m_dragStartPos);
250     if (shiftPressed) {
251       newRect.setTopLeft(adjPos(newRect.topLeft()));
252       newRect.setBottomRight(adjPos(newRect.bottomRight()));
253     }
254     break;
255 
256   case TopLeftCorner:
257     newRect.setTopLeft(adjPos(newRect.topLeft() + d));
258     if (altPressed) newRect.setBottomRight(adjPos(newRect.bottomRight() - d));
259     break;
260 
261   case TopRightCorner:
262     newRect.setTopRight(adjPos(newRect.topRight() + d));
263     if (altPressed) newRect.setBottomLeft(adjPos(newRect.bottomLeft() - d));
264     break;
265 
266   case BottomRightCorner:
267     newRect.setBottomRight(adjPos(newRect.bottomRight() + d));
268     if (altPressed) newRect.setTopLeft(adjPos(newRect.topLeft() - d));
269     break;
270 
271   case BottomLeftCorner:
272     newRect.setBottomLeft(adjPos(newRect.bottomLeft() + d));
273     if (altPressed) newRect.setTopRight(adjPos(newRect.topRight() - d));
274     break;
275 
276   case TopEdge:
277     newRect.setTop(adjVal(newRect.top() + d.y()));
278     if (altPressed) newRect.setBottom(adjVal(newRect.bottom() - d.y()));
279     break;
280 
281   case RightEdge:
282     newRect.setRight(adjVal(newRect.right() + d.x()));
283     if (altPressed) newRect.setLeft(adjVal(newRect.left() - d.x()));
284     break;
285 
286   case BottomEdge:
287     newRect.setBottom(adjVal(newRect.bottom() + d.y()));
288     if (altPressed) newRect.setTop(adjVal(newRect.top() - d.y()));
289     break;
290 
291   case LeftEdge:
292     newRect.setLeft(adjVal(newRect.left() + d.x()));
293     if (altPressed) newRect.setRight(adjVal(newRect.right() - d.x()));
294     break;
295   default:
296     break;
297   }
298 
299   currentBoardItem()->setRatioRect(newRect.normalized());
300   invalidate();
301   update();
302 }
303 
mousePressEvent(QMouseEvent * event)304 void BoardView::mousePressEvent(QMouseEvent* event) {
305   // only the left button counts
306   if (event->button() != Qt::LeftButton) return;
307   // store the current item rect and mouse pos, relative to the image size
308   m_dragStartItemRect = currentBoardItem()->getRatioRect();
309   QPointF posOnImg    = QPointF(event->pos()) - m_boardImgRect.topLeft();
310   m_dragStartPos      = QPointF(posOnImg.x() / m_boardImgRect.width(),
311                            posOnImg.y() / m_boardImgRect.height());
312 }
313 
mouseReleaseEvent(QMouseEvent * event)314 void BoardView::mouseReleaseEvent(QMouseEvent* event) {
315   m_dragStartItemRect = QRectF();
316   m_dragStartPos      = QPointF();
317 }
318 
319 //=============================================================================
320 
ItemInfoView(QWidget * parent)321 ItemInfoView::ItemInfoView(QWidget* parent) : QStackedWidget(parent) {
322   m_nameEdit        = new QLineEdit(this);
323   m_maxFontSizeEdit = new DVGui::IntLineEdit(this, 1, 1);
324   m_typeCombo       = new QComboBox(this);
325   m_textEdit        = new QTextEdit(this);
326   m_imgPathField    = new DVGui::FileField(this);
327   m_fontCombo       = new QFontComboBox(this);
328   m_boldButton      = new QPushButton(createQIcon("bold"), tr("Bold"), this);
329   m_italicButton = new QPushButton(createQIcon("italic"), tr("Italic"), this);
330   m_fontColorField =
331       new DVGui::ColorField(this, true, TPixel32(0, 0, 0, 255), 25, false, 54);
332   m_imgARModeCombo = new QComboBox(this);
333 
334   m_fontPropBox = new QWidget(this);
335   m_imgPropBox  = new QWidget(this);
336 
337   for (int i = 0; i < BoardItem::TypeCount; i++) {
338     m_typeCombo->addItem(stringByItemType[(BoardItem::Type)i]);
339   }
340 
341   m_textEdit->setAcceptRichText(false);
342   m_textEdit->setStyleSheet(
343       "background:white;\ncolor:black;\nborder:1 solid black;");
344 
345   m_boldButton->setIconSize(QSize(16, 16));
346   m_boldButton->setFixedHeight(25);
347   m_boldButton->setCheckable(true);
348   m_italicButton->setIconSize(QSize(16, 16));
349   m_italicButton->setFixedHeight(25);
350   m_italicButton->setCheckable(true);
351 
352   QStringList filters;
353   for (QByteArray& format : QImageReader::supportedImageFormats())
354     filters += format;
355   m_imgPathField->setFilters(filters);
356   m_imgPathField->setFileMode(QFileDialog::ExistingFile);
357 
358   m_imgARModeCombo->addItem(tr("Ignore"), Qt::IgnoreAspectRatio);
359   m_imgARModeCombo->addItem(tr("Keep"), Qt::KeepAspectRatio);
360 
361   //----- layout
362 
363   QGridLayout* mainLay = new QGridLayout();
364   mainLay->setMargin(5);
365   mainLay->setHorizontalSpacing(3);
366   mainLay->setVerticalSpacing(10);
367   {
368     mainLay->addWidget(new QLabel(tr("Name:"), this), 0, 0,
369                        Qt::AlignRight | Qt::AlignVCenter);
370     mainLay->addWidget(m_nameEdit, 0, 1);
371 
372     mainLay->addWidget(new QLabel(tr("Type:"), this), 1, 0,
373                        Qt::AlignRight | Qt::AlignVCenter);
374     mainLay->addWidget(m_typeCombo, 1, 1);
375 
376     QVBoxLayout* extraInfoLay = new QVBoxLayout();
377     extraInfoLay->setMargin(0);
378     extraInfoLay->setSpacing(0);
379     {
380       extraInfoLay->addWidget(m_textEdit, 1);
381 
382       QGridLayout* imgPropLay = new QGridLayout();
383       imgPropLay->setMargin(0);
384       imgPropLay->setHorizontalSpacing(3);
385       imgPropLay->setVerticalSpacing(10);
386       {
387         imgPropLay->addWidget(new QLabel(tr("Path:"), this), 0, 0,
388                               Qt::AlignRight | Qt::AlignVCenter);
389         imgPropLay->addWidget(m_imgPathField, 0, 1, 1, 2);
390 
391         imgPropLay->addWidget(new QLabel(tr("Aspect Ratio:"), this), 1, 0,
392                               Qt::AlignRight | Qt::AlignVCenter);
393         imgPropLay->addWidget(m_imgARModeCombo, 1, 1);
394       }
395       imgPropLay->setColumnStretch(0, 0);
396       imgPropLay->setColumnStretch(1, 0);
397       imgPropLay->setColumnStretch(2, 1);
398       m_imgPropBox->setLayout(imgPropLay);
399       extraInfoLay->addWidget(m_imgPropBox, 0);
400 
401       extraInfoLay->addSpacing(5);
402 
403       QGridLayout* fontPropLay = new QGridLayout();
404       fontPropLay->setMargin(0);
405       fontPropLay->setHorizontalSpacing(3);
406       fontPropLay->setVerticalSpacing(10);
407       {
408         fontPropLay->addWidget(new QLabel(tr("Font:"), this), 0, 0,
409                                Qt::AlignRight | Qt::AlignVCenter);
410         fontPropLay->addWidget(m_fontCombo, 0, 1, 1, 4);
411 
412         fontPropLay->addWidget(new QLabel(tr("Max Size:"), this), 1, 0,
413                                Qt::AlignRight | Qt::AlignVCenter);
414         fontPropLay->addWidget(m_maxFontSizeEdit, 1, 1);
415 
416         fontPropLay->addWidget(m_boldButton, 1, 2);
417         fontPropLay->addWidget(m_italicButton, 1, 3);
418 
419         fontPropLay->addWidget(m_fontColorField, 2, 0, 1, 5);
420       }
421       fontPropLay->setColumnStretch(0, 0);
422       fontPropLay->setColumnStretch(1, 0);
423       fontPropLay->setColumnStretch(2, 0);
424       fontPropLay->setColumnStretch(3, 0);
425       fontPropLay->setColumnStretch(4, 1);
426       m_fontPropBox->setLayout(fontPropLay);
427       extraInfoLay->addWidget(m_fontPropBox, 0);
428     }
429     mainLay->addLayout(extraInfoLay, 2, 0, 1, 2);
430   }
431   mainLay->setColumnStretch(0, 0);
432   mainLay->setColumnStretch(1, 1);
433   mainLay->setRowStretch(0, 0);
434   mainLay->setRowStretch(1, 0);
435   mainLay->setRowStretch(2, 1);
436 
437   QWidget* mainWidget = new QWidget(this);
438   mainWidget->setLayout(mainLay);
439   addWidget(mainWidget);
440 
441   addWidget(new QLabel(tr("No item selected."), this));
442 
443   bool ret = true;
444   ret      = ret && connect(m_nameEdit, SIGNAL(editingFinished()), this,
445                        SLOT(onNameEdited()));
446   ret = ret && connect(m_maxFontSizeEdit, SIGNAL(editingFinished()), this,
447                        SLOT(onMaxFontSizeEdited()));
448   ret = ret && connect(m_typeCombo, SIGNAL(activated(int)), this,
449                        SLOT(onTypeComboActivated(int)));
450   ret = ret && connect(m_textEdit, SIGNAL(textChanged()), this,
451                        SLOT(onFreeTextChanged()));
452   ret = ret && connect(m_imgPathField, SIGNAL(pathChanged()), this,
453                        SLOT(onImgPathChanged()));
454   ret = ret && connect(m_fontCombo, SIGNAL(currentFontChanged(const QFont&)),
455                        this, SLOT(onFontComboChanged(const QFont&)));
456   ret = ret && connect(m_boldButton, SIGNAL(clicked(bool)), this,
457                        SLOT(onBoldButtonClicked(bool)));
458   ret = ret && connect(m_italicButton, SIGNAL(clicked(bool)), this,
459                        SLOT(onItalicButtonClicked(bool)));
460   ret = ret &&
461         connect(m_fontColorField, SIGNAL(colorChanged(const TPixel32&, bool)),
462                 this, SLOT(onFontColorChanged(const TPixel32&, bool)));
463   ret = ret && connect(m_imgARModeCombo, SIGNAL(activated(int)), this,
464                        SLOT(onImgARModeComboActivated()));
465   assert(ret);
466 }
467 
468 // called on switching the current item
setCurrentItem(int index)469 void ItemInfoView::setCurrentItem(int index) {
470   if (index == -1) {
471     if (!currentBoardItem()) return;
472     setCurrentBoardItem(nullptr);
473     setCurrentIndex(1);  // set to "no item seleceted" page.
474     return;
475   }
476 
477   setCurrentIndex(0);  // set to normal page.
478 
479   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
480   if (!scene) return;
481   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
482   BoardSettings* boardSettings = outProp->getBoardSettings();
483 
484   if (index >= boardSettings->getItemCount()) return;
485 
486   BoardItem* newItem = &(boardSettings->getItem(index));
487   if (currentBoardItem() == newItem) return;
488 
489   setCurrentBoardItem(newItem);
490 
491   // sync
492   m_nameEdit->setText(newItem->getName());
493   m_maxFontSizeEdit->setValue(newItem->getMaximumFontSize());
494   m_typeCombo->setCurrentIndex((int)newItem->getType());
495   m_textEdit->setText(newItem->getFreeText());
496   m_imgPathField->setPath(newItem->getImgPath().getQString());
497   m_fontCombo->setCurrentFont(newItem->font());
498   m_boldButton->setChecked(newItem->font().bold());
499   m_italicButton->setChecked(newItem->font().italic());
500   QColor col = newItem->getColor();
501   m_fontColorField->setColor(
502       TPixel32(col.red(), col.green(), col.blue(), col.alpha()));
503   int ARModeIndex = m_imgARModeCombo->findData(newItem->getImgARMode());
504   if (ARModeIndex >= 0) m_imgARModeCombo->setCurrentIndex(ARModeIndex);
505 
506   switch (newItem->getType()) {
507   case BoardItem::FreeText:
508     m_textEdit->show();
509     m_imgPropBox->hide();
510     m_fontPropBox->show();
511     break;
512   case BoardItem::Image:
513     m_textEdit->hide();
514     m_imgPropBox->show();
515     m_fontPropBox->hide();
516     break;
517   default:
518     m_textEdit->hide();
519     m_imgPropBox->hide();
520     m_fontPropBox->show();
521     break;
522   }
523 }
524 
onNameEdited()525 void ItemInfoView::onNameEdited() {
526   assert(currentBoardItem());
527 
528   QString newName = m_nameEdit->text();
529 
530   if (newName.isEmpty()) {
531     newName = tr("Item");
532     m_nameEdit->setText(newName);
533   }
534 
535   if (currentBoardItem()->getName() == newName) return;
536 
537   currentBoardItem()->setName(newName);
538 
539   emit itemPropertyChanged(true);
540 }
541 
onMaxFontSizeEdited()542 void ItemInfoView::onMaxFontSizeEdited() {
543   assert(currentBoardItem());
544 
545   int maxFontSize = m_maxFontSizeEdit->getValue();
546 
547   if (currentBoardItem()->getMaximumFontSize() == maxFontSize) return;
548 
549   currentBoardItem()->setMaximumFontSize(maxFontSize);
550 
551   emit itemPropertyChanged(false);
552 }
553 
onTypeComboActivated(int)554 void ItemInfoView::onTypeComboActivated(int) {
555   assert(currentBoardItem());
556 
557   BoardItem::Type newType = (BoardItem::Type)m_typeCombo->currentIndex();
558 
559   if (currentBoardItem()->getType() == newType) return;
560 
561   currentBoardItem()->setType(newType);
562 
563   switch (newType) {
564   case BoardItem::FreeText:
565     m_textEdit->show();
566     m_imgPropBox->hide();
567     m_fontPropBox->show();
568     break;
569   case BoardItem::Image:
570     m_textEdit->hide();
571     m_imgPropBox->show();
572     m_fontPropBox->hide();
573     break;
574   default:
575     m_textEdit->hide();
576     m_imgPropBox->hide();
577     m_fontPropBox->show();
578     break;
579   }
580 
581   emit itemPropertyChanged(true);
582 }
583 
onFreeTextChanged()584 void ItemInfoView::onFreeTextChanged() {
585   assert(currentBoardItem());
586 
587   currentBoardItem()->setFreeText(m_textEdit->toPlainText());
588 
589   emit itemPropertyChanged(false);
590 }
591 
onImgPathChanged()592 void ItemInfoView::onImgPathChanged() {
593   assert(currentBoardItem());
594   TFilePath fp(m_imgPathField->getPath());
595   if (fp.isLevelName()) {
596     ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
597     TLevelReaderP lr(scene->decodeFilePath(fp));
598     TLevelP level;
599     if (lr) level = lr->loadInfo();
600     if (level.getPointer() && level->getTable()->size() > 0) {
601       TFrameId firstFrame = level->begin()->first;
602       fp                  = fp.withFrame(firstFrame);
603       m_imgPathField->setPath(fp.getQString());
604     }
605   }
606   currentBoardItem()->setImgPath(fp);
607 
608   emit itemPropertyChanged(true);
609 }
610 
onFontComboChanged(const QFont & newFont)611 void ItemInfoView::onFontComboChanged(const QFont& newFont) {
612   assert(currentBoardItem());
613 
614   currentBoardItem()->font().setFamily(newFont.family());
615 
616   emit itemPropertyChanged(false);
617 }
618 
onBoldButtonClicked(bool on)619 void ItemInfoView::onBoldButtonClicked(bool on) {
620   assert(currentBoardItem());
621 
622   currentBoardItem()->font().setBold(on);
623 
624   emit itemPropertyChanged(false);
625 }
626 
onItalicButtonClicked(bool on)627 void ItemInfoView::onItalicButtonClicked(bool on) {
628   assert(currentBoardItem());
629 
630   currentBoardItem()->font().setItalic(on);
631 
632   emit itemPropertyChanged(false);
633 }
634 
onFontColorChanged(const TPixel32 & color,bool isDragging)635 void ItemInfoView::onFontColorChanged(const TPixel32& color, bool isDragging) {
636   assert(currentBoardItem());
637 
638   if (isDragging) return;
639 
640   QColor newColor((int)color.r, (int)color.g, (int)color.b, (int)color.m);
641   if (currentBoardItem()->getColor() == newColor) return;
642   currentBoardItem()->setColor(newColor);
643 
644   emit itemPropertyChanged(true);
645 }
646 
onImgARModeComboActivated()647 void ItemInfoView::onImgARModeComboActivated() {
648   assert(currentBoardItem());
649 
650   currentBoardItem()->setImgARMode(
651       (Qt::AspectRatioMode)m_imgARModeCombo->currentData().toInt());
652 
653   emit itemPropertyChanged(false);
654 }
655 
656 //=============================================================================
657 
ItemListView(QWidget * parent)658 ItemListView::ItemListView(QWidget* parent) : QWidget(parent) {
659   QPushButton* newItemBtn =
660       new QPushButton(createQIcon("plus"), tr("Add"), this);
661   m_deleteItemBtn = new QPushButton(createQIcon("delete"), tr("Remove"), this);
662   m_moveUpBtn     = new QPushButton(createQIcon("fb_up"), tr("Move Up"), this);
663   m_moveDownBtn =
664       new QPushButton(createQIcon("fb_down"), tr("Move Down"), this);
665 
666   m_list = new QListWidget(this);
667 
668   QSize iconSize(16, 16);
669 
670   newItemBtn->setIconSize(iconSize);
671   m_deleteItemBtn->setIconSize(iconSize);
672   m_moveUpBtn->setIconSize(iconSize);
673   m_moveDownBtn->setIconSize(iconSize);
674 
675   m_list->setIconSize(QSize(20, 20));
676   m_list->setAlternatingRowColors(true);
677   m_list->setMaximumWidth(225);
678 
679   QHBoxLayout* mainLay = new QHBoxLayout();
680   mainLay->setMargin(5);
681   mainLay->setSpacing(5);
682   {
683     QVBoxLayout* buttonsLay = new QVBoxLayout();
684     buttonsLay->setMargin(0);
685     buttonsLay->setSpacing(3);
686     {
687       buttonsLay->addWidget(newItemBtn, 0);
688       buttonsLay->addWidget(m_deleteItemBtn, 0);
689       buttonsLay->addWidget(m_moveUpBtn, 0);
690       buttonsLay->addWidget(m_moveDownBtn, 0);
691       buttonsLay->addStretch(1);
692     }
693     mainLay->addLayout(buttonsLay, 0);
694 
695     mainLay->addWidget(m_list, 1);
696   }
697   setLayout(mainLay);
698 
699   bool ret = true;
700   ret      = ret && connect(m_list, SIGNAL(currentRowChanged(int)), this,
701                        SLOT(onCurrentItemSwitched(int)));
702   ret = ret && connect(newItemBtn, SIGNAL(clicked(bool)), this,
703                        SLOT(onNewItemButtonClicked()));
704   ret = ret && connect(m_deleteItemBtn, SIGNAL(clicked(bool)), this,
705                        SLOT(onDeleteItemButtonClicked()));
706   ret = ret && connect(m_moveUpBtn, SIGNAL(clicked(bool)), this,
707                        SLOT(onMoveUpButtonClicked()));
708   ret = ret && connect(m_moveDownBtn, SIGNAL(clicked(bool)), this,
709                        SLOT(onMoveDownButtonClicked()));
710   assert(ret);
711 }
712 
initialize()713 void ItemListView::initialize() {
714   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
715   if (!scene) return;
716   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
717   BoardSettings* boardSettings = outProp->getBoardSettings();
718 
719   // first, clear all the list items
720   m_list->clear();
721 
722   if (boardSettings->getItemCount() == 0) return;
723 
724   for (int i = 0; i < boardSettings->getItemCount(); i++) {
725     BoardItem* item           = &(boardSettings->getItem(i));
726     QListWidgetItem* listItem = new QListWidgetItem(m_list);
727 
728     editListWidgetItem(listItem, item);
729   }
730 
731   // this will update information view
732   m_list->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
733 }
734 
735 // called when the infoView is edited
updateCurrentItem()736 void ItemListView::updateCurrentItem() {
737   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
738   if (!scene) return;
739   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
740   BoardSettings* boardSettings = outProp->getBoardSettings();
741 
742   int currentItemIndex = m_list->currentRow();
743 
744   if (currentItemIndex < 0 || currentItemIndex >= boardSettings->getItemCount())
745     return;
746 
747   BoardItem* item = &(boardSettings->getItem(currentItemIndex));
748   editListWidgetItem(m_list->item(currentItemIndex), item);
749 }
750 
onCurrentItemSwitched(int currentRow)751 void ItemListView::onCurrentItemSwitched(int currentRow) {
752   if (currentRow == -1) {
753     m_moveUpBtn->setEnabled(false);
754     m_moveDownBtn->setEnabled(false);
755   } else {
756     m_moveUpBtn->setEnabled(currentRow != 0);
757     m_moveDownBtn->setEnabled(currentRow != m_list->count() - 1);
758   }
759   emit currentItemSwitched(currentRow);
760 }
761 
onNewItemButtonClicked()762 void ItemListView::onNewItemButtonClicked() {
763   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
764   if (!scene) return;
765   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
766   BoardSettings* boardSettings = outProp->getBoardSettings();
767 
768   boardSettings->addNewItem();
769 
770   QListWidgetItem* listItem = new QListWidgetItem();
771   BoardItem* item           = &(boardSettings->getItem(0));
772 
773   editListWidgetItem(listItem, item);
774 
775   m_list->insertItem(0, listItem);
776   m_list->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
777 
778   m_deleteItemBtn->setEnabled(true);
779 
780   emit itemAddedOrDeleted();
781 }
782 
onDeleteItemButtonClicked()783 void ItemListView::onDeleteItemButtonClicked() {
784   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
785   if (!scene) return;
786   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
787   BoardSettings* boardSettings = outProp->getBoardSettings();
788 
789   int currentRow = m_list->currentRow();
790 
791   assert(currentRow != -1);
792 
793   boardSettings->removeItem(currentRow);
794 
795   if (m_list->count() == 1) {
796     m_deleteItemBtn->setEnabled(false);
797     m_list->setCurrentRow(-1, QItemSelectionModel::ClearAndSelect);
798   } else if (currentRow == m_list->count() - 1)
799     m_list->setCurrentRow(m_list->count() - 2,
800                           QItemSelectionModel::ClearAndSelect);
801 
802   delete m_list->takeItem(currentRow);
803 
804   emit itemAddedOrDeleted();
805 }
806 
onMoveUpButtonClicked()807 void ItemListView::onMoveUpButtonClicked() {
808   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
809   if (!scene) return;
810   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
811   BoardSettings* boardSettings = outProp->getBoardSettings();
812 
813   int currentRow = m_list->currentRow();
814   assert(currentRow > 0);
815 
816   boardSettings->swapItems(currentRow - 1, currentRow);
817 
818   m_list->insertItem(currentRow - 1, m_list->takeItem(currentRow));
819   m_list->setCurrentRow(currentRow - 1);
820 
821   emit itemAddedOrDeleted();
822 }
823 
onMoveDownButtonClicked()824 void ItemListView::onMoveDownButtonClicked() {
825   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
826   if (!scene) return;
827   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
828   BoardSettings* boardSettings = outProp->getBoardSettings();
829 
830   int currentRow = m_list->currentRow();
831   assert(currentRow < m_list->count() - 1);
832 
833   boardSettings->swapItems(currentRow, currentRow + 1);
834 
835   QListWidgetItem* item = m_list->takeItem(currentRow);
836   m_list->insertItem(currentRow + 1, item);
837   m_list->setCurrentRow(currentRow + 1);
838 
839   emit itemAddedOrDeleted();
840 }
841 
842 //=============================================================================
843 
BoardSettingsPopup(QWidget * parent)844 BoardSettingsPopup::BoardSettingsPopup(QWidget* parent)
845     : DVGui::Dialog(parent, true, false, "ClapperboardSettings") {
846   setWindowTitle(tr("Clapperboard Settings"));
847 
848   initializeItemTypeString();
849 
850   m_boardView    = new BoardView(this);
851   m_itemInfoView = new ItemInfoView(this);
852   m_itemListView = new ItemListView(this);
853 
854   m_durationEdit = new DVGui::IntLineEdit(this, 1, 0);
855 
856   QPushButton* loadPresetBtn =
857       new QPushButton(createQIcon("load"), tr("Load Preset"), this);
858   QPushButton* savePresetBtn =
859       new QPushButton(createQIcon("saveas"), tr("Save as Preset"), this);
860 
861   QPushButton* closeButton = new QPushButton(tr("Close"), this);
862 
863   //--- layout
864 
865   QHBoxLayout* mainLay = new QHBoxLayout();
866   mainLay->setMargin(0);
867   mainLay->setSpacing(10);
868   {
869     QVBoxLayout* leftLay = new QVBoxLayout();
870     leftLay->setMargin(0);
871     leftLay->setSpacing(0);
872     {
873       QHBoxLayout* leftTopLay = new QHBoxLayout();
874       leftTopLay->setMargin(5);
875       leftTopLay->setSpacing(3);
876       {
877         leftTopLay->addWidget(new QLabel(tr("Duration (frames):"), this), 0);
878         leftTopLay->addWidget(m_durationEdit, 0);
879 
880         leftTopLay->addSpacing(10);
881 
882         leftTopLay->addWidget(loadPresetBtn, 0);
883         leftTopLay->addWidget(savePresetBtn, 0);
884 
885         leftTopLay->addStretch(1);
886       }
887       leftLay->addLayout(leftTopLay, 0);
888 
889       leftLay->addWidget(m_boardView, 1);
890     }
891     mainLay->addLayout(leftLay, 1);
892 
893     QVBoxLayout* rightLay = new QVBoxLayout();
894     rightLay->setMargin(0);
895     rightLay->setSpacing(15);
896     {
897       rightLay->addWidget(m_itemInfoView, 0);
898       rightLay->addWidget(m_itemListView, 1);
899     }
900     mainLay->addLayout(rightLay, 0);
901   }
902   m_topLayout->addLayout(mainLay, 1);
903 
904   addButtonBarWidget(closeButton);
905 
906   bool ret = true;
907   ret = ret && connect(m_itemListView, SIGNAL(currentItemSwitched(int)), this,
908                        SLOT(onCurrentItemSwitched(int)));
909   ret = ret && connect(m_itemListView, SIGNAL(itemAddedOrDeleted()), this,
910                        SLOT(onItemAddedOrDeleted()));
911   ret = ret && connect(m_itemInfoView, SIGNAL(itemPropertyChanged(bool)), this,
912                        SLOT(onItemPropertyChanged(bool)));
913   ret = ret && connect(m_durationEdit, SIGNAL(editingFinished()), this,
914                        SLOT(onDurationEdited()));
915   ret = ret && connect(closeButton, SIGNAL(pressed()), this, SLOT(close()));
916   ret = ret &&
917         connect(loadPresetBtn, SIGNAL(pressed()), this, SLOT(onLoadPreset()));
918   ret = ret &&
919         connect(savePresetBtn, SIGNAL(pressed()), this, SLOT(onSavePreset()));
920 
921   assert(ret);
922 }
923 
initialize()924 void BoardSettingsPopup::initialize() {
925   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
926   if (!scene) return;
927   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
928   BoardSettings* boardSettings = outProp->getBoardSettings();
929 
930   m_durationEdit->setValue(boardSettings->getDuration());
931 
932   m_itemListView->initialize();
933 
934   m_boardView->invalidate();
935   m_boardView->update();
936 }
937 
initializeItemTypeString()938 void BoardSettingsPopup::initializeItemTypeString() {
939   if (!stringByItemType.isEmpty()) return;
940 
941   stringByItemType[BoardItem::FreeText]    = tr("Text");  // free text (m_text)
942   stringByItemType[BoardItem::ProjectName] = tr("Project name");
943   stringByItemType[BoardItem::SceneName]   = tr("Scene name");
944   stringByItemType[BoardItem::Duration_Frame]    = tr("Duration : Frame");
945   stringByItemType[BoardItem::Duration_SecFrame] = tr("Duration : Sec + Frame");
946   stringByItemType[BoardItem::Duration_HHMMSSFF] = tr("Duration : HH:MM:SS:FF");
947   stringByItemType[BoardItem::CurrentDate]       = tr("Current date");
948   stringByItemType[BoardItem::CurrentDateTime]   = tr("Current date and time");
949   stringByItemType[BoardItem::UserName]          = tr("User name");
950   stringByItemType[BoardItem::ScenePath_Aliased] =
951       tr("Scene location : Aliased path");
952   stringByItemType[BoardItem::ScenePath_Full] =
953       tr("Scene location : Full path");
954   stringByItemType[BoardItem::MoviePath_Aliased] =
955       tr("Output location : Aliased path");
956   stringByItemType[BoardItem::MoviePath_Full] =
957       tr("Output location : Full path");
958   stringByItemType[BoardItem::Image] = tr("Image");  // m_imgPath
959 }
960 
hideEvent(QHideEvent * event)961 void BoardSettingsPopup::hideEvent(QHideEvent* event) {
962   setCurrentBoardItem(nullptr);
963 }
964 
965 // called on changing the current row of ItemListView
onCurrentItemSwitched(int index)966 void BoardSettingsPopup::onCurrentItemSwitched(int index) {
967   // updat Info
968   m_itemInfoView->setCurrentItem(index);
969 
970   m_boardView->update();
971 }
972 
onItemAddedOrDeleted()973 void BoardSettingsPopup::onItemAddedOrDeleted() {
974   m_boardView->invalidate();
975   m_boardView->update();
976 }
977 
978 // called on modifying the infoView
onItemPropertyChanged(bool updateListView)979 void BoardSettingsPopup::onItemPropertyChanged(bool updateListView) {
980   m_boardView->invalidate();
981   m_boardView->update();
982 
983   if (updateListView) m_itemListView->updateCurrentItem();
984 }
985 
onDurationEdited()986 void BoardSettingsPopup::onDurationEdited() {
987   ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
988   if (!scene) return;
989   TOutputProperties* outProp   = scene->getProperties()->getOutputProperties();
990   BoardSettings* boardSettings = outProp->getBoardSettings();
991 
992   boardSettings->setDuration(m_durationEdit->getValue());
993 
994   m_boardView->update();
995 }
996 
onLoadPreset()997 void BoardSettingsPopup::onLoadPreset() {
998   LoadBoardPresetFilePopup popup;
999 
1000   TFilePath fp = popup.getPath();
1001   if (fp.isEmpty()) return;
1002 
1003   TIStream is(fp);
1004   if (!is) throw TException(fp.getWideString() + L": Can't open file");
1005   try {
1006     std::string tagName = "";
1007     if (!is.matchTag(tagName)) throw TException("Bad file format");
1008     if (tagName == "clapperboardSettingsPreset") {
1009       ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
1010       if (!scene) return;
1011       TOutputProperties* outProp =
1012           scene->getProperties()->getOutputProperties();
1013       BoardSettings* boardSettings = outProp->getBoardSettings();
1014 
1015       boardSettings->loadData(is);
1016     } else
1017       throw TException("Bad file format");
1018   } catch (const TSystemException& se) {
1019     DVGui::warning(QString::fromStdWString(se.getMessage()));
1020   } catch (...) {
1021     DVGui::error(QObject::tr("Couldn't load %1").arg(fp.getQString()));
1022   }
1023 
1024   initialize();
1025 }
1026 
onSavePreset()1027 void BoardSettingsPopup::onSavePreset() {
1028   SaveBoardPresetFilePopup popup;
1029 
1030   TFilePath fp = popup.getPath();
1031   if (fp.isEmpty()) return;
1032 
1033   try {
1034     TFileStatus fs(fp);
1035     if (fs.doesExist() && !fs.isWritable()) {
1036       throw TSystemException(
1037           fp, "The preset cannot be saved: it is a read only file.");
1038     }
1039 
1040     TOStream os(fp, false);
1041     if (!os.checkStatus()) throw TException("Could not open file");
1042 
1043     ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
1044     if (!scene) return;
1045     TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
1046     BoardSettings* boardSettings = outProp->getBoardSettings();
1047 
1048     os.openChild("clapperboardSettingsPreset");
1049     boardSettings->saveData(os, true);
1050     os.closeChild();
1051 
1052     bool status = os.checkStatus();
1053     if (!status) throw TException("Could not complete the save preset");
1054 
1055   } catch (const TSystemException& se) {
1056     DVGui::warning(QString::fromStdWString(se.getMessage()));
1057   } catch (...) {
1058     DVGui::error(QObject::tr("Couldn't save %1").arg(fp.getQString()));
1059   }
1060 }
1061 
1062 //=============================================================================
1063 
SaveBoardPresetFilePopup()1064 SaveBoardPresetFilePopup::SaveBoardPresetFilePopup()
1065     : GenericSaveFilePopup(tr("Save Clapperboard Settings As Preset")) {
1066   m_browser->enableGlobalSelection(false);
1067   setFilterTypes(QStringList("clapperboard"));
1068 }
1069 
showEvent(QShowEvent * e)1070 void SaveBoardPresetFilePopup::showEvent(QShowEvent* e) {
1071   FileBrowserPopup::showEvent(e);
1072   setFolder(ToonzFolder::getLibraryFolder() + "clapperboards");
1073 }
1074 
1075 //=============================================================================
1076 
LoadBoardPresetFilePopup()1077 LoadBoardPresetFilePopup::LoadBoardPresetFilePopup()
1078     : GenericLoadFilePopup(tr("Load Clapperboard Settings Preset")) {
1079   m_browser->enableGlobalSelection(false);
1080   setFilterTypes(QStringList("clapperboard"));
1081 }
1082 
showEvent(QShowEvent * e)1083 void LoadBoardPresetFilePopup::showEvent(QShowEvent* e) {
1084   FileBrowserPopup::showEvent(e);
1085   setFolder(ToonzFolder::getLibraryFolder() + "clapperboards");
1086 }