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 }