diff --git a/toonz/sources/include/orientation.h b/toonz/sources/include/orientation.h index 6d5069b..a9207b9 100644 --- a/toonz/sources/include/orientation.h +++ b/toonz/sources/include/orientation.h @@ -67,6 +67,7 @@ enum class PredefinedRect { BEGIN_EXTENDER, //! top / left extender KEYFRAME_AREA, //! part of cell dedicated to key frames DRAG_AREA, //! draggable side bar + CELL_MARK_AREA, //! cell mark SOUND_TRACK, //! area dedicated to waveform display PREVIEW_TRACK, //! sound preview area BEGIN_SOUND_EDIT, //! top sound resize diff --git a/toonz/sources/include/toonz/sceneproperties.h b/toonz/sources/include/toonz/sceneproperties.h index 88c5d42..06b1b9d 100644 --- a/toonz/sources/include/toonz/sceneproperties.h +++ b/toonz/sources/include/toonz/sceneproperties.h @@ -50,6 +50,11 @@ class DVAPI TSceneProperties { public: typedef std::vector Guides; + struct CellMark { + QString name; + TPixel32 color; + }; + private: Guides m_hGuides, m_vGuides; @@ -75,6 +80,9 @@ private: //! Xsheet Note Color, color number = 7. QList m_notesColor; + // Cell Mark colors and names + QList m_cellMarks; + bool m_columnColorFilterOnRender; TFilePath m_camCapSaveInPath; @@ -281,6 +289,12 @@ and height. TPixel32 getNoteColor(int colorIndex) const; void setNoteColor(TPixel32 color, int colorIndex); + QList getCellMarks() const; + CellMark getCellMark(int index) const; + void setCellMark(const CellMark &mark, int index); + bool hasDefaultCellMarks() + const; // check if the cell mark settings are modified + private: // not implemented TSceneProperties(const TSceneProperties &); diff --git a/toonz/sources/include/toonz/txshcolumn.h b/toonz/sources/include/toonz/txshcolumn.h index 526e08b..480ed33 100644 --- a/toonz/sources/include/toonz/txshcolumn.h +++ b/toonz/sources/include/toonz/txshcolumn.h @@ -9,6 +9,7 @@ #include #include +#include #undef DVAPI #undef DVVAR @@ -304,6 +305,9 @@ protected: std::vector m_cells; int m_first; + // cell marks information key:frame value:id + QMap m_cellMarkIds; + public: /*! Constructs a TXshCellColumn with default value. @@ -398,6 +402,13 @@ last row with not empty cell of same level. bool getLevelRange(int row, int &r0, int &r1) const override; // virtual void updateIcon() = 0; + + void saveCellMarks(TOStream &os); + bool loadCellMarks(std::string tagName, TIStream &is); + void setCellMark(int frame, int id); + int getCellMark(int frame) const; + QMap getCellMarks() const; + void clearCellMarks(); }; #endif diff --git a/toonz/sources/include/toonzqt/menubarcommand.h b/toonz/sources/include/toonzqt/menubarcommand.h index 0839e23..c70e892 100644 --- a/toonz/sources/include/toonzqt/menubarcommand.h +++ b/toonz/sources/include/toonzqt/menubarcommand.h @@ -65,7 +65,8 @@ enum CommandType { MiscCommandType, MenuCommandType, VisualizationButtonCommandType, - StopMotionCommandType + StopMotionCommandType, + CellMarkCommandType }; //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/exportxsheetpdf.cpp b/toonz/sources/toonz/exportxsheetpdf.cpp index 4c8f55d..04d312f 100644 --- a/toonz/sources/toonz/exportxsheetpdf.cpp +++ b/toonz/sources/toonz/exportxsheetpdf.cpp @@ -90,6 +90,12 @@ TEnv::StringVar XShPdfExportImgPath("XShPdfExportImgPath", ""); TEnv::IntVar XShPdfExportContinuousLineThres("XShPdfExportContinuousLineThres", 0); +TEnv::IntVar XShPdfExportTick1Id("XShPdfExportTick1Id", -1); +TEnv::IntVar XShPdfExportTick2Id("XShPdfExportTick2Id", -1); +TEnv::IntVar XShPdfExportKeyId("XShPdfExportKeyId", -1); +TEnv::IntVar XShPdfExportTick1Type("XShPdfExportTick1Type", TickMark_Dot); +TEnv::IntVar XShPdfExportTick2Type("XShPdfExportTick2Type", TickMark_Dot); + using namespace XSheetPDFTemplateParamIDs; namespace { @@ -287,6 +293,86 @@ XSheetPDFDataType dataStr2Type(const QString& str) { return map.value(str, Data_Invalid); } +QIcon getColorChipIcon(TPixel32 color) { + QPixmap pm(15, 15); + pm.fill(QColor(color.r, color.g, color.b)); + return QIcon(pm); +} + +void refreshCellMarkComboItems(QComboBox* combo) { + int current = -1; + if (combo->count()) current = combo->currentData().toInt(); + + combo->clear(); + QList marks = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMarks(); + combo->addItem(QObject::tr("None", "XSheetPDF CellMark"), -1); + int curId = 0; + for (auto mark : marks) { + QString label = QString("%1: %2").arg(curId).arg(mark.name); + combo->addItem(getColorChipIcon(mark.color), label, curId); + curId++; + } + + if (current >= 0) combo->setCurrentIndex(combo->findData(current)); +} + +QPixmap tickMarkPm(TickMarkType type, int size, bool withBG = false) { + QPixmap pm(size, size); + QPointF center(double(size) * 0.5, double(size) * 0.5); + + pm.fill((withBG) ? Qt::white : Qt::transparent); + QPainter p(&pm); + QPen pen(Qt::black); + pen.setWidthF(double(size) / 15.0); + p.setPen(pen); + switch (type) { + case TickMark_Dot: { + p.setBrush(Qt::black); + double dotR = double(size) * 0.1; + p.drawEllipse(center, dotR, dotR); + break; + } + case TickMark_Circle: { + double circleR = double(size) * 0.4; + p.drawEllipse(center, circleR, circleR); + break; + } + case TickMark_Filled: { + p.setBrush(Qt::black); + double circleR = double(size) * 0.4; + p.drawEllipse(center, circleR, circleR); + break; + } + case TickMark_Asterisk: { + QFont font = p.font(); + font.setPixelSize(size); + p.setFont(font); + p.drawText(0, 0, size, size, Qt::AlignCenter, "*"); + break; + } + } + return pm; +} + +QComboBox* createTickMarkCombo(QWidget* parent) { + QComboBox* combo = new QComboBox(parent); + combo->addItem(QIcon(tickMarkPm(TickMark_Dot, 15, true)), + QObject::tr("Dot", "XSheetPDF CellMark"), TickMark_Dot); + combo->addItem(QIcon(tickMarkPm(TickMark_Circle, 15, true)), + QObject::tr("Circle", "XSheetPDF CellMark"), TickMark_Circle); + combo->addItem(QIcon(tickMarkPm(TickMark_Filled, 15, true)), + QObject::tr("Filled circle", "XSheetPDF CellMark"), + TickMark_Filled); + combo->addItem(QIcon(tickMarkPm(TickMark_Asterisk, 15, true)), + QObject::tr("Asterisk", "XSheetPDF CellMark"), + TickMark_Asterisk); + return combo; +} + } // namespace //--------------------------------------------------------- @@ -859,7 +945,7 @@ void XSheetPDFTemplate::drawContinuousLine(QPainter& painter, QRect rect, } void XSheetPDFTemplate::drawCellNumber(QPainter& painter, QRect rect, - TXshCell& cell) { + TXshCell& cell, bool isKey) { QFont font = painter.font(); font.setPixelSize(param(RowHeight) - mm2px(1)); font.setLetterSpacing(QFont::PercentageSpacing, 100); @@ -878,9 +964,29 @@ void XSheetPDFTemplate::drawCellNumber(QPainter& painter, QRect rect, str += cell.m_frameId.getLetter(); } painter.drawText(rect, Qt::AlignCenter, str); + if (isKey) { + QPen keep(painter.pen()); + QPen circlePen(keep); + circlePen.setWidth(mm2px(0.3)); + painter.setPen(circlePen); + QFontMetrics fm(font); + int keyR_width = + std::max(param(RowHeight), fm.horizontalAdvance(str) + mm2px(1)); + QRect keyR(0, 0, keyR_width, param(RowHeight)); + keyR.moveCenter(rect.center()); + painter.drawEllipse(keyR); + painter.setPen(keep); + } } } +void XSheetPDFTemplate::drawTickMark(QPainter& painter, QRect rect, + TickMarkType type) { + QRect tickR(0, 0, rect.height(), rect.height()); + tickR.moveCenter(rect.center()); + painter.drawPixmap(tickR, tickMarkPm(type, rect.height())); +} + void XSheetPDFTemplate::drawEndMark(QPainter& painter, QRect upperRect) { QRect rect = upperRect.translated(0, upperRect.height()); @@ -1136,14 +1242,23 @@ void XSheetPDFTemplate::drawXsheetContents(QPainter& painter, int framePage, TXshCell cell = column->getCell(f); if (cell.m_level != level) cell.m_level = nullptr; + int markId = column->getCellMark(r); + // cotinuous line if (r != 0 && r != 72 && prevCell == cell) { - if (drawCLFlag) + // draw tick mark + if (markId >= 0 && m_info.tick1MarkId == markId) + drawTickMark(painter, m_cellRects[c][r], m_info.tick1MarkType); + else if (markId >= 0 && m_info.tick2MarkId == markId) + drawTickMark(painter, m_cellRects[c][r], m_info.tick2MarkType); + + else if (drawCLFlag) drawContinuousLine(painter, m_cellRects[c][r], cell.isEmpty()); } // draw cell else { - drawCellNumber(painter, m_cellRects[c][r], cell); + bool drawKeyMark = (markId >= 0 && m_info.keyMarkId == markId); + drawCellNumber(painter, m_cellRects[c][r], cell, drawKeyMark); drawCLFlag = (m_info.continuousLineMode == Line_Always) ? true : (m_info.continuousLineMode == Line_None) @@ -1621,6 +1736,15 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() QPushButton* exportPngBtn = new QPushButton(tr("Export PNG"), this); QPushButton* cancelBtn = new QPushButton(tr("Cancel"), this); + m_tick1IdCombo = new QComboBox(this); + m_tick2IdCombo = new QComboBox(this); + m_keyIdCombo = new QComboBox(this); + refreshCellMarkComboItems(m_tick1IdCombo); + refreshCellMarkComboItems(m_tick2IdCombo); + refreshCellMarkComboItems(m_keyIdCombo); + m_tick1MarkCombo = createTickMarkCombo(this); + m_tick2MarkCombo = createTickMarkCombo(this); + //------ QStringList pdfFileTypes = {"pdf"}; m_pathFld->setFilters(pdfFileTypes); @@ -1748,22 +1872,41 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() exportLay->addWidget(m_continuousLineCombo, 2, 1, 1, 2, Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(m_addDateTimeCB, 3, 0, 1, 3, - Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(m_addScenePathCB, 4, 0, 1, 3, - Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(m_drawSoundCB, 5, 0, 1, 3, - Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(m_addSceneNameCB, 6, 0, 1, 2, - Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(m_sceneNameEdit, 6, 2, + QGridLayout* checksLay = new QGridLayout(); + checksLay->setMargin(0); + checksLay->setHorizontalSpacing(10); + checksLay->setVerticalSpacing(10); + { + checksLay->addWidget(m_addDateTimeCB, 0, 0); + checksLay->addWidget(m_addScenePathCB, 0, 1, 1, 2); + checksLay->addWidget(m_drawSoundCB, 1, 0); + checksLay->addWidget(m_addSceneNameCB, 1, 1); + checksLay->addWidget(m_sceneNameEdit, 1, 2, + Qt::AlignLeft | Qt::AlignVCenter); + checksLay->addWidget(m_levelNameOnBottomCB, 2, 0, 1, 3); + } + checksLay->setColumnStretch(0, 2); + checksLay->setColumnStretch(1, 1); + checksLay->setColumnStretch(2, 1); + exportLay->addLayout(checksLay, 3, 0, 1, 3); + + exportLay->addWidget(new QLabel(tr("Inbetween mark:"), this), 4, 0, + Qt::AlignRight | Qt::AlignVCenter); + exportLay->addWidget(m_tick1IdCombo, 4, 1); + exportLay->addWidget(m_tick1MarkCombo, 4, 2, Qt::AlignLeft | Qt::AlignVCenter); - exportLay->addWidget(m_levelNameOnBottomCB, 7, 0, 1, 3, + exportLay->addWidget(new QLabel(tr("Reverse sheet mark:"), this), 5, + 0, Qt::AlignRight | Qt::AlignVCenter); + exportLay->addWidget(m_tick2IdCombo, 5, 1); + exportLay->addWidget(m_tick2MarkCombo, 5, 2, Qt::AlignLeft | Qt::AlignVCenter); + exportLay->addWidget(new QLabel(tr("Keyframe mark:"), this), 6, 0, + Qt::AlignRight | Qt::AlignVCenter); + exportLay->addWidget(m_keyIdCombo, 6, 1); - exportLay->addWidget(new QLabel(tr("Memo:"), this), 8, 0, + exportLay->addWidget(new QLabel(tr("Memo:"), this), 7, 0, Qt::AlignRight | Qt::AlignTop); - exportLay->addWidget(m_memoEdit, 8, 1, 1, 2); + exportLay->addWidget(m_memoEdit, 7, 1, 1, 2); } exportLay->setColumnStretch(2, 1); exportGBox->setLayout(exportLay); @@ -1850,6 +1993,16 @@ ExportXsheetPdfPopup::ExportXsheetPdfPopup() connect(m_prev, SIGNAL(clicked(bool)), this, SLOT(onPrev())); connect(m_next, SIGNAL(clicked(bool)), this, SLOT(onNext())); + connect(m_tick1IdCombo, SIGNAL(activated(int)), this, + SLOT(onTickIdComboActivated())); + connect(m_tick2IdCombo, SIGNAL(activated(int)), this, + SLOT(onTickIdComboActivated())); + connect(m_keyIdCombo, SIGNAL(activated(int)), this, SLOT(updatePreview())); + connect(m_tick1MarkCombo, SIGNAL(activated(int)), this, + SLOT(updatePreview())); + connect(m_tick2MarkCombo, SIGNAL(activated(int)), this, + SLOT(updatePreview())); + // The following lines are "translation word book" listing the words which may // appear in the template @@ -1969,6 +2122,10 @@ void ExportXsheetPdfPopup::initialize() { initTemplate(); m_previewPane->fitScaleTo(m_previewArea->size()); + + refreshCellMarkComboItems(m_tick1IdCombo); + refreshCellMarkComboItems(m_tick2IdCombo); + refreshCellMarkComboItems(m_keyIdCombo); } // register settings to the user env file on close @@ -1996,6 +2153,12 @@ void ExportXsheetPdfPopup::saveSettings() { (ContinuousLineMode)(m_continuousLineCombo->currentData().toInt()); XShPdfExportContinuousLineThres = (clMode == Line_Always) ? 0 : (clMode == Line_None) ? -1 : 3; + + XShPdfExportTick1Id = m_tick1IdCombo->currentData().toInt(); + XShPdfExportTick2Id = m_tick2IdCombo->currentData().toInt(); + XShPdfExportKeyId = m_keyIdCombo->currentData().toInt(); + XShPdfExportTick1Type = m_tick1MarkCombo->currentData().toInt(); + XShPdfExportTick2Type = m_tick2MarkCombo->currentData().toInt(); } // load settings from the user env file on ctor @@ -2036,6 +2199,19 @@ void ExportXsheetPdfPopup::loadSettings() { m_logoTextEdit->setEnabled(m_logoTxtRB->isChecked()); m_logoImgPathField->setEnabled(m_logoImgRB->isChecked()); m_sceneNameEdit->setEnabled(m_addSceneNameCB->isChecked()); + + int id = XShPdfExportTick1Id; + m_tick1IdCombo->setCurrentIndex(m_tick1IdCombo->findData(id)); + m_tick1MarkCombo->setEnabled(id != -1); + id = XShPdfExportTick2Id; + m_tick2IdCombo->setCurrentIndex(m_tick2IdCombo->findData(id)); + m_tick2MarkCombo->setEnabled(id != -1); + id = XShPdfExportKeyId; + m_keyIdCombo->setCurrentIndex(m_keyIdCombo->findData(id)); + int type = XShPdfExportTick1Type; + m_tick1MarkCombo->setCurrentIndex(m_tick1MarkCombo->findData(type)); + type = XShPdfExportTick2Type; + m_tick2MarkCombo->setCurrentIndex(m_tick2MarkCombo->findData(type)); } void ExportXsheetPdfPopup::initTemplate() { @@ -2096,6 +2272,12 @@ void ExportXsheetPdfPopup::setInfo() { info.serialFrameNumber = m_serialFrameNumberCB->isChecked(); info.drawLevelNameOnBottom = m_levelNameOnBottomCB->isChecked(); + info.tick1MarkId = m_tick1IdCombo->currentData().toInt(); + info.tick2MarkId = m_tick2IdCombo->currentData().toInt(); + info.keyMarkId = m_keyIdCombo->currentData().toInt(); + info.tick1MarkType = (TickMarkType)(m_tick1MarkCombo->currentData().toInt()); + info.tick2MarkType = (TickMarkType)(m_tick2MarkCombo->currentData().toInt()); + m_currentTmpl->setInfo(info); if (!m_logoImgRB->isChecked()) return; @@ -2365,6 +2547,15 @@ void ExportXsheetPdfPopup::onNext() { updatePreview(); } +void ExportXsheetPdfPopup::onTickIdComboActivated() { + QComboBox* combo = qobject_cast(sender()); + if (combo == m_tick1IdCombo) + m_tick1MarkCombo->setEnabled(m_tick1IdCombo->currentData().toInt() != -1); + else if (combo == m_tick2IdCombo) + m_tick2MarkCombo->setEnabled(m_tick2IdCombo->currentData().toInt() != -1); + updatePreview(); +} + //----------------------------------------------------------------------------- OpenPopupCommandHandler openExportXsheetPdfPopup( diff --git a/toonz/sources/toonz/exportxsheetpdf.h b/toonz/sources/toonz/exportxsheetpdf.h index 9665c23..b4c11f8 100644 --- a/toonz/sources/toonz/exportxsheetpdf.h +++ b/toonz/sources/toonz/exportxsheetpdf.h @@ -85,6 +85,12 @@ typedef void (*DecoFunc)(QPainter&, QRect, QMap&, enum ExportArea { Area_Actions = 0, Area_Cells }; enum ContinuousLineMode { Line_Always = 0, Line_MoreThan3s, Line_None }; +enum TickMarkType { + TickMark_Dot = 0, + TickMark_Circle, + TickMark_Filled, + TickMark_Asterisk +}; struct XSheetPDFFormatInfo { QColor lineColor; @@ -101,6 +107,11 @@ struct XSheetPDFFormatInfo { bool serialFrameNumber; bool drawLevelNameOnBottom; ContinuousLineMode continuousLineMode; + int tick1MarkId; + int tick2MarkId; + int keyMarkId; + TickMarkType tick1MarkType; + TickMarkType tick2MarkType; }; class XSheetPDFTemplate { @@ -172,7 +183,9 @@ protected: void addInfo(int w, QString lbl, DecoFunc f = nullptr); void drawContinuousLine(QPainter& painter, QRect rect, bool isEmpty); - void drawCellNumber(QPainter& painter, QRect rect, TXshCell& cell); + void drawCellNumber(QPainter& painter, QRect rect, TXshCell& cell, + bool isKey); + void drawTickMark(QPainter& painter, QRect rect, TickMarkType type); void drawEndMark(QPainter& painter, QRect upperRect); void drawLevelName(QPainter& painter, QRect rect, QString name, bool isBottom = false); @@ -281,6 +294,9 @@ class ExportXsheetPdfPopup final : public DVGui::Dialog { int m_totalPageCount; QPushButton *m_prev, *m_next; + QComboBox *m_tick1IdCombo, *m_tick2IdCombo, *m_keyIdCombo; + QComboBox *m_tick1MarkCombo, *m_tick2MarkCombo; + // column and column name (if manually specified) QList> m_columns; QList m_soundColumns; @@ -315,6 +331,8 @@ protected slots: void onLogoImgPathChanged(); void onPrev(); void onNext(); + + void onTickIdComboActivated(); }; #endif \ No newline at end of file diff --git a/toonz/sources/toonz/filebrowserpopup.cpp b/toonz/sources/toonz/filebrowserpopup.cpp index f276322..3569b98 100644 --- a/toonz/sources/toonz/filebrowserpopup.cpp +++ b/toonz/sources/toonz/filebrowserpopup.cpp @@ -454,8 +454,9 @@ TFilePath GenericLoadFilePopup::getPath() { // GenericSaveFilePopup implementation //*********************************************************************************** -GenericSaveFilePopup::GenericSaveFilePopup(const QString &title) - : FileBrowserPopup(title, Options(FOR_SAVING)) { +GenericSaveFilePopup::GenericSaveFilePopup(const QString &title, + QWidget *customWidget) + : FileBrowserPopup(title, Options(FOR_SAVING), "", customWidget) { connect(m_nameField, SIGNAL(returnPressedNow()), m_okButton, SLOT(animateClick())); } diff --git a/toonz/sources/toonz/filebrowserpopup.h b/toonz/sources/toonz/filebrowserpopup.h index a4e0f1a..a26cc8e 100644 --- a/toonz/sources/toonz/filebrowserpopup.h +++ b/toonz/sources/toonz/filebrowserpopup.h @@ -185,7 +185,7 @@ protected: //! asks the user for a \a single file path to save something to. class GenericSaveFilePopup : public FileBrowserPopup { public: - GenericSaveFilePopup(const QString &title); + GenericSaveFilePopup(const QString &title, QWidget *customWidget = nullptr); /*! This function shows the popup and blocks until a suitable diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index 5ad27cf..bebaf7c 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -2822,6 +2822,16 @@ void MainWindow::defineActions() { createStopMotionAction(MI_StopMotionToggleUseLiveViewImages, QT_TR_NOOP("Show original live view images."), ""); #endif // x64 + + // create cell mark actions + for (int markId = 0; markId < 12; markId++) { + std::string cmdId = (std::string)MI_SetCellMark + std::to_string(markId); + std::string labelStr = + QT_TR_NOOP("Set Cell Mark ") + std::to_string(markId); + QAction *action = + createAction(cmdId.c_str(), labelStr.c_str(), "", CellMarkCommandType); + action->setData(markId); + } } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index d63c97e..004e08f 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -455,4 +455,7 @@ #define MI_ExportXDTS "MI_ExportXDTS" #define MI_ExportTvpJson "MI_ExportTvpJson" #define MI_ExportXsheetPDF "MI_ExportXsheetPDF" + +// mark id is added for each actual command (i.g. MI_SetCellMark1) +#define MI_SetCellMark "MI_SetCellMark" #endif diff --git a/toonz/sources/toonz/scenesettingspopup.cpp b/toonz/sources/toonz/scenesettingspopup.cpp index 0998345..2856346 100644 --- a/toonz/sources/toonz/scenesettingspopup.cpp +++ b/toonz/sources/toonz/scenesettingspopup.cpp @@ -32,6 +32,7 @@ #include #include #include +#include using namespace DVGui; @@ -43,6 +44,46 @@ const int labelSize = 110; //----------------------------------------------------------------------------- +class EditCellMarkUndo final : public TUndo { + int m_id; + TSceneProperties::CellMark m_markBefore, m_markAfter; + + EditCellMarkUndo(int id) : m_id(id) { + m_markBefore = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(id); + } + +public: + EditCellMarkUndo(int id, TPixel32 color) : EditCellMarkUndo(id) { + m_markAfter = {m_markBefore.name, color}; + } + EditCellMarkUndo(int id, QString name) : EditCellMarkUndo(id) { + m_markAfter = {name, m_markBefore.color}; + } + + void set(const TSceneProperties::CellMark &mark) const { + TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->setCellMark(mark, m_id); + TApp::instance()->getCurrentScene()->notifySceneChanged(); + } + + void undo() const override { set(m_markBefore); } + + void redo() const override { set(m_markAfter); } + + int getSize() const override { return sizeof *this; } + + QString getHistoryString() override { + return QObject::tr("Edit Cell Mark #%1").arg(QString::number(m_id)); + } +}; + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- } // namespace @@ -78,11 +119,128 @@ QImage::Format_ARGB32); */ //============================================================================= +// CellMarksPopup +//----------------------------------------------------------------------------- + +CellMarksPopup::CellMarksPopup(QWidget *parent) : QDialog(parent) { + setWindowTitle(tr("Cell Marks Settings")); + + QList marks = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMarks(); + + QGridLayout *layout = new QGridLayout(); + layout->setMargin(10); + layout->setHorizontalSpacing(5); + layout->setVerticalSpacing(10); + { + int id = 0; + for (auto mark : marks) { + ColorField *colorF = new ColorField(this, false, mark.color, 20); + colorF->hideChannelsFields(true); + QLineEdit *nameF = new QLineEdit(mark.name, this); + m_fields.append({id, colorF, nameF}); + + int row = layout->rowCount(); + + layout->addWidget(new QLabel(QString("%1:").arg(id), this), row, 0, + Qt::AlignRight | Qt::AlignVCenter); + + layout->addWidget(colorF, row, 1); + layout->addWidget(nameF, row, 2); + + connect(colorF, SIGNAL(colorChanged(const TPixel32 &, bool)), this, + SLOT(onColorChanged(const TPixel32 &, bool))); + connect(nameF, SIGNAL(editingFinished()), this, SLOT(onNameChanged())); + id++; + } + } + layout->setColumnStretch(2, 1); + setLayout(layout); +} + +void CellMarksPopup::update() { + QList marks = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMarks(); + assert(marks.count() == m_fields.count()); + int id = 0; + for (auto mark : marks) { + assert(m_fields[id].id == id); + m_fields[id].colorField->setColor(mark.color); + m_fields[id].nameField->setText(mark.name); + id++; + } +} + +void CellMarksPopup::onColorChanged(const TPixel32 &color, bool isDragging) { + if (isDragging) return; + // obtain id + int id = -1; + ColorField *colorF = qobject_cast(sender()); + for (auto field : m_fields) { + if (field.colorField == colorF) { + id = field.id; + break; + } + } + if (id < 0) return; + + // return if the value is unchanged + TPixel32 oldColor = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(id) + .color; + if (color == oldColor) return; + + EditCellMarkUndo *undo = new EditCellMarkUndo(id, color); + undo->redo(); + TUndoManager::manager()->add(undo); +} + +void CellMarksPopup::onNameChanged() { + // obtain id + int id = -1; + QLineEdit *nameF = qobject_cast(sender()); + for (auto field : m_fields) { + if (field.nameField == nameF) { + id = field.id; + break; + } + } + if (id < 0) return; + + // return if the value is unchanged + QString oldName = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(id) + .name; + if (nameF->text() == oldName) return; + // reject empty string + if (nameF->text().isEmpty()) { + nameF->setText(oldName); + return; + } + + EditCellMarkUndo *undo = new EditCellMarkUndo(id, nameF->text()); + undo->redo(); + TUndoManager::manager()->add(undo); +} + +//============================================================================= // SceneSettingsPopup //----------------------------------------------------------------------------- SceneSettingsPopup::SceneSettingsPopup() - : QDialog(TApp::instance()->getMainWindow()) { + : QDialog(TApp::instance()->getMainWindow()), m_cellMarksPopup(nullptr) { setWindowTitle(tr("Scene Settings")); setObjectName("SceneSettings"); TSceneProperties *sprop = getProperties(); @@ -124,6 +282,9 @@ SceneSettingsPopup::SceneSettingsPopup() m_colorFilterOnRenderCB->setChecked( sprop->isColumnColorFilterOnRenderEnabled()); + QPushButton *editCellMarksButton = + new QPushButton(tr("Edit Cell Marks"), this); + // layout QGridLayout *mainLayout = new QGridLayout(); mainLayout->setMargin(10); @@ -164,6 +325,12 @@ SceneSettingsPopup::SceneSettingsPopup() // Use Color Filter and Transparency for Rendering mainLayout->addWidget(m_colorFilterOnRenderCB, 6, 0, 1, 4); + + // cell marks + mainLayout->addWidget(new QLabel(tr("Cell Marks:"), this), 7, 0, + Qt::AlignRight | Qt::AlignVCenter); + mainLayout->addWidget(editCellMarksButton, 7, 1, 1, 4, + Qt::AlignLeft | Qt::AlignVCenter); } mainLayout->setColumnStretch(0, 0); mainLayout->setColumnStretch(1, 0); @@ -201,6 +368,9 @@ SceneSettingsPopup::SceneSettingsPopup() // Use Color Filter and Transparency for Rendering ret = ret && connect(m_colorFilterOnRenderCB, SIGNAL(stateChanged(int)), this, SLOT(onColorFilterOnRenderChanged())); + // Cell Marks + ret = ret && connect(editCellMarksButton, SIGNAL(clicked()), this, + SLOT(onEditCellMarksButtonClicked())); assert(ret); } @@ -254,6 +424,8 @@ void SceneSettingsPopup::update() { m_startFrameFld->setValue(markerOffset + 1); m_colorFilterOnRenderCB->setChecked( sprop->isColumnColorFilterOnRenderEnabled()); + + if (m_cellMarksPopup) m_cellMarksPopup->update(); } //----------------------------------------------------------------------------- @@ -368,6 +540,14 @@ void SceneSettingsPopup::onColorFilterOnRenderChanged() { TApp::instance()->getCurrentScene()->notifySceneChanged(); } +//----------------------------------------------------------------------------- + +void SceneSettingsPopup::onEditCellMarksButtonClicked() { + if (!m_cellMarksPopup) m_cellMarksPopup = new CellMarksPopup(this); + m_cellMarksPopup->show(); + m_cellMarksPopup->raise(); +} + //============================================================================= OpenPopupCommandHandler openSceneSettingsPopup( diff --git a/toonz/sources/toonz/scenesettingspopup.h b/toonz/sources/toonz/scenesettingspopup.h index ecd487a..b6096a8 100644 --- a/toonz/sources/toonz/scenesettingspopup.h +++ b/toonz/sources/toonz/scenesettingspopup.h @@ -13,6 +13,25 @@ // forward declaration class TSceneProperties; class QComboBox; +class QLineEdit; + +class CellMarksPopup final : public QDialog { + Q_OBJECT + struct MarkerField { + int id; + DVGui::ColorField *colorField; + QLineEdit *nameField; + }; + + QList m_fields; + +public: + CellMarksPopup(QWidget *parent); + void update(); +protected slots: + void onColorChanged(const TPixel32 &, bool); + void onNameChanged(); +}; //============================================================================= // SceneSettingsPopup @@ -37,6 +56,8 @@ class SceneSettingsPopup final : public QDialog { TSceneProperties *getProperties() const; + CellMarksPopup *m_cellMarksPopup; + public: SceneSettingsPopup(); void configureNotify(); @@ -61,6 +82,8 @@ public slots: void setBgColor(const TPixel32 &value, bool isDragging); void onColorFilterOnRenderChanged(); + + void onEditCellMarksButtonClicked(); }; #endif // SCENESETTINGSPOPUP_H diff --git a/toonz/sources/toonz/shortcutpopup.cpp b/toonz/sources/toonz/shortcutpopup.cpp index f3809be..1b40ab5 100644 --- a/toonz/sources/toonz/shortcutpopup.cpp +++ b/toonz/sources/toonz/shortcutpopup.cpp @@ -205,6 +205,8 @@ ShortcutTree::ShortcutTree(QWidget *parent) : QTreeWidget(parent) { addFolder(tr("Help"), MenuHelpCommandType, menuCommandFolder); addFolder(tr("Right-click Menu Commands"), RightClickMenuCommandType); + QTreeWidgetItem *rcmSubFolder = m_folders.back(); + addFolder(tr("Cell Mark"), CellMarkCommandType, rcmSubFolder); addFolder(tr("Tools"), ToolCommandType); addFolder(tr("Tool Modifiers"), ToolModifierCommandType); diff --git a/toonz/sources/toonz/tpanels.cpp b/toonz/sources/toonz/tpanels.cpp index 4fb21e0..1406d58 100644 --- a/toonz/sources/toonz/tpanels.cpp +++ b/toonz/sources/toonz/tpanels.cpp @@ -784,30 +784,31 @@ void ColorFieldEditorController::edit(DVGui::ColorField *colorField) { connect(m_currentColorField, SIGNAL(colorChanged(const TPixel32 &, bool)), SLOT(onColorChanged(const TPixel32 &, bool))); connect(m_colorFieldHandle, SIGNAL(colorStyleChanged(bool)), - SLOT(onColorStyleChanged())); + SLOT(onColorStyleChanged(bool))); } //----------------------------------------------------------------------------- void ColorFieldEditorController::hide() { disconnect(m_colorFieldHandle, SIGNAL(colorStyleChanged(bool)), this, - SLOT(onColorStyleChanged())); + SLOT(onColorStyleChanged(bool))); } //----------------------------------------------------------------------------- -void ColorFieldEditorController::onColorStyleChanged() { +void ColorFieldEditorController::onColorStyleChanged(bool isDragging) { if (!m_currentColorField) return; assert(!!m_palette); TPixel32 color = m_palette->getStyle(1)->getMainColor(); - if (m_currentColorField->getColor() == color) return; + if (m_currentColorField->getColor() == color && isDragging) return; m_currentColorField->setColor(color); - m_currentColorField->notifyColorChanged(color, false); + m_currentColorField->notifyColorChanged(color, isDragging); } //----------------------------------------------------------------------------- -void ColorFieldEditorController::onColorChanged(const TPixel32 &color, bool) { +void ColorFieldEditorController::onColorChanged(const TPixel32 &color, + bool isDragging) { if (!m_currentColorField) return; TColorStyle *style = m_palette->getStyle(1); if (style->getMainColor() == color) return; @@ -815,7 +816,7 @@ void ColorFieldEditorController::onColorChanged(const TPixel32 &color, bool) { TApp::instance() ->getPaletteController() ->getCurrentPalette() - ->notifyColorStyleChanged(); + ->notifyColorStyleChanged(isDragging); } //============================================================================= diff --git a/toonz/sources/toonz/tpanels.h b/toonz/sources/toonz/tpanels.h index d28041e..88e7cf4 100644 --- a/toonz/sources/toonz/tpanels.h +++ b/toonz/sources/toonz/tpanels.h @@ -133,7 +133,7 @@ public: void hide() override; protected slots: - void onColorStyleChanged(); + void onColorStyleChanged(bool); void onColorChanged(const TPixel32 &color, bool); }; diff --git a/toonz/sources/toonz/xdtsimportpopup.cpp b/toonz/sources/toonz/xdtsimportpopup.cpp index acc7d1b..c426f8a 100644 --- a/toonz/sources/toonz/xdtsimportpopup.cpp +++ b/toonz/sources/toonz/xdtsimportpopup.cpp @@ -4,6 +4,8 @@ #include "tsystem.h" #include "toonzqt/filefield.h" #include "toonz/toonzscene.h" +#include "toonz/tscenehandle.h" +#include "toonz/sceneproperties.h" #include #include @@ -11,9 +13,20 @@ #include #include #include +#include using namespace DVGui; +namespace { +QIcon getColorChipIcon(TPixel32 color) { + QPixmap pm(15, 15); + pm.fill(QColor(color.r, color.g, color.b)); + return QIcon(pm); +} +} // namespace + +//============================================================================= + XDTSImportPopup::XDTSImportPopup(QStringList levelNames, ToonzScene* scene, TFilePath scenePath) : m_scene(scene) @@ -24,6 +37,26 @@ XDTSImportPopup::XDTSImportPopup(QStringList levelNames, ToonzScene* scene, QPushButton* loadButton = new QPushButton(tr("Load"), this); QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); + m_tick1Combo = new QComboBox(this); + m_tick2Combo = new QComboBox(this); + QList marks = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMarks(); + for (int i = 0; i < 2; i++) { + QComboBox* combo = (i == 0) ? m_tick1Combo : m_tick2Combo; + combo->addItem(tr("None"), -1); + int curId = 0; + for (auto mark : marks) { + QString label = QString("%1: %2").arg(curId).arg(mark.name); + combo->addItem(getColorChipIcon(mark.color), label, curId); + curId++; + } + } + m_tick1Combo->setCurrentIndex(m_tick1Combo->findData(0)); + m_tick2Combo->setCurrentIndex(m_tick2Combo->findData(1)); + QString description = tr("Please specify the level locations. Suggested paths " "are input in the fields with blue border."); @@ -62,6 +95,23 @@ XDTSImportPopup::XDTSImportPopup(QStringList levelNames, ToonzScene* scene, fieldsArea->setWidget(fieldsWidget); m_topLayout->addWidget(fieldsArea, 1); + // cell mark area + QGridLayout* markLay = new QGridLayout(); + markLay->setMargin(0); + markLay->setHorizontalSpacing(10); + markLay->setVerticalSpacing(10); + { + markLay->addWidget(new QLabel(tr("Inbetween symbol mark"), this), 0, 0, + Qt::AlignRight | Qt::AlignVCenter); + markLay->addWidget(m_tick1Combo, 0, 1); + + markLay->addWidget(new QLabel(tr("Reverse sheet symbol mark"), this), 1, 0, + Qt::AlignRight | Qt::AlignVCenter); + markLay->addWidget(m_tick2Combo, 1, 1); + } + markLay->setColumnStretch(2, 1); + m_topLayout->addLayout(markLay, 0); + connect(loadButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject())); @@ -178,8 +228,17 @@ void XDTSImportPopup::updateSuggestions(const QString samplePath) { } } +//----------------------------------------------------------------------------- + QString XDTSImportPopup::getLevelPath(QString levelName) { FileField* field = m_fields.value(levelName); if (!field) return QString(); return field->getPath(); +} + +//----------------------------------------------------------------------------- + +void XDTSImportPopup::getMarkerIds(int& tick1Id, int& tick2Id) { + tick1Id = m_tick1Combo->currentData().toInt(); + tick2Id = m_tick2Combo->currentData().toInt(); } \ No newline at end of file diff --git a/toonz/sources/toonz/xdtsimportpopup.h b/toonz/sources/toonz/xdtsimportpopup.h index a185d25..e539ee8 100644 --- a/toonz/sources/toonz/xdtsimportpopup.h +++ b/toonz/sources/toonz/xdtsimportpopup.h @@ -10,19 +10,23 @@ namespace DVGui { class FileField; } class ToonzScene; +class QComboBox; class XDTSImportPopup : public DVGui::Dialog { Q_OBJECT - QMap m_fields; + QMap m_fields; QStringList m_pathSuggestedLevels; - ToonzScene *m_scene; + ToonzScene* m_scene; + + QComboBox *m_tick1Combo, *m_tick2Combo; void updateSuggestions(const QString samplePath); public: - XDTSImportPopup(QStringList levelNames, ToonzScene *scene, + XDTSImportPopup(QStringList levelNames, ToonzScene* scene, TFilePath scenePath); QString getLevelPath(QString levelName); + void getMarkerIds(int& tick1Id, int& tick2Id); protected slots: void onPathChanged(); }; diff --git a/toonz/sources/toonz/xdtsio.cpp b/toonz/sources/toonz/xdtsio.cpp index 023acfe..dc82aaa 100644 --- a/toonz/sources/toonz/xdtsio.cpp +++ b/toonz/sources/toonz/xdtsio.cpp @@ -32,10 +32,22 @@ #include #include #include +#include +#include using namespace XdtsIo; namespace { static QByteArray identifierStr("exchangeDigitalTimeSheet Save Data"); + +QIcon getColorChipIcon(TPixel32 color) { + QPixmap pm(15, 15); + pm.fill(QColor(color.r, color.g, color.b)); + return QIcon(pm); } + +int _tick1Id = -1; +int _tick2Id = -1; +bool _exportAllColumn = true; +} // namespace //----------------------------------------------------------------------------- void XdtsHeader::read(const QJsonObject &json) { QRegExp rx("\\d{1,4}"); @@ -76,7 +88,14 @@ TFrameId XdtsFrameDataItem::str2Fid(const QString &str) const { } QString XdtsFrameDataItem::fid2Str(const TFrameId &fid) const { - if (fid.getLetter().isEmpty()) return QString::number(fid.getNumber()); + if (fid.getNumber() == -1) + return QString("SYMBOL_NULL_CELL"); + else if (fid.getNumber() == SYMBOL_TICK_1) + return QString("SYMBOL_TICK_1"); + else if (fid.getNumber() == SYMBOL_TICK_2) + return QString("SYMBOL_TICK_2"); + else if (fid.getLetter().isEmpty()) + return QString::number(fid.getNumber()); return QString::number(fid.getNumber()) + fid.getLetter(); } @@ -104,11 +123,13 @@ TFrameId XdtsFrameDataItem::getFrameId() const { if (val == "SYMBOL_NULL_CELL") return TFrameId(-1); // EMPTY // ignore sheet symbols for now - else if (val == "SYMBOL_HYPHEN" || val == "SYMBOL_TICK_1" || - val == "SYMBOL_TICK_2") + else if (val == "SYMBOL_HYPHEN") return TFrameId(-2); // IGNORE - // return -1; - // return cell number + else if (val == "SYMBOL_TICK_1") + return TFrameId(SYMBOL_TICK_1); + else if (val == "SYMBOL_TICK_2") + return TFrameId(SYMBOL_TICK_2); + // return cell number return str2Fid(m_values.at(0)); } @@ -172,7 +193,8 @@ static bool frameLessThan(const QPair &v1, return v1.first < v2.first; } -QVector XdtsFieldTrackItem::getCellFrameIdTrack() const { +QVector XdtsFieldTrackItem::getCellFrameIdTrack( + QList &tick1, QList &tick2) const { QList> frameFids; for (const XdtsTrackFrameItem &frame : m_frames) frameFids.append(frame.frameFid()); @@ -196,7 +218,15 @@ QVector XdtsFieldTrackItem::getCellFrameIdTrack() const { TFrameId cellFid = frameFid.second; if (cellFid.getNumber() == -2) // IGNORE case cells.append((cells.isEmpty()) ? TFrameId(-1) : cells.last()); - else + else if (cellFid.getNumber() == + XdtsFrameDataItem::SYMBOL_TICK_1) { // SYMBOL_TICK_1 + cells.append((cells.isEmpty()) ? TFrameId(-1) : cells.last()); + tick1.append(currentFrame); + } else if (cellFid.getNumber() == + XdtsFrameDataItem::SYMBOL_TICK_2) { // SYMBOL_TICK_2 + cells.append((cells.isEmpty()) ? TFrameId(-1) : cells.last()); + tick2.append(currentFrame); + } else cells.append(cellFid); currentFrame++; } @@ -220,8 +250,14 @@ QString XdtsFieldTrackItem::build(TXshCellColumn *column) { // handle as the empty cell if (!level || cell.m_level != level) cell = TXshCell(); // continue if the cell is continuous - if (prevCell == cell) continue; - + if (prevCell == cell) { + // cell mark to ticks + if (_tick1Id >= 0 && column->getCellMark(row) == _tick1Id) + addFrame(row, TFrameId(XdtsFrameDataItem::SYMBOL_TICK_1)); + else if (_tick2Id >= 0 && column->getCellMark(row) == _tick2Id) + addFrame(row, TFrameId(XdtsFrameDataItem::SYMBOL_TICK_2)); + continue; + } if (cell.isEmpty()) addFrame(row, TFrameId(-1)); else @@ -269,29 +305,37 @@ QList XdtsTimeTableFieldItem::getOccupiedColumns() const { return ret; } -QVector XdtsTimeTableFieldItem::getColumnTrack(int col) const { +QVector XdtsTimeTableFieldItem::getColumnTrack( + int col, QList &tick1, QList &tick2) const { for (const XdtsFieldTrackItem &track : m_tracks) { if (track.getTrackNo() != col) continue; - return track.getCellFrameIdTrack(); + return track.getCellFrameIdTrack(tick1, tick2); } return QVector(); } void XdtsTimeTableFieldItem::build(TXsheet *xsheet, QStringList &columnLabels) { - m_fieldId = CELL; + m_fieldId = CELL; + int exportCol = 0; for (int col = 0; col < xsheet->getFirstFreeColumnIndex(); col++) { if (xsheet->isColumnEmpty(col)) { columnLabels.append(""); + exportCol++; continue; } TXshCellColumn *column = xsheet->getColumn(col)->getCellColumn(); + // skip non-cell column if (!column) { - columnLabels.append(""); continue; } - XdtsFieldTrackItem track(col); + // skip inactive column + if (!_exportAllColumn && !column->isPreviewVisible()) { + continue; + } + XdtsFieldTrackItem track(exportCol); columnLabels.append(track.build(column)); if (!track.isEmpty()) m_tracks.append(track); + exportCol++; } } //----------------------------------------------------------------------------- @@ -504,6 +548,9 @@ bool XdtsIo::loadXdtsScene(ToonzScene *scene, const TFilePath &scenePath) { return false; } + int tick1Id, tick2Id; + popup.getMarkerIds(tick1Id, tick2Id); + TXsheet *xsh = scene->getXsheet(); XdtsTimeTableFieldItem cellField = xdtsData.timeTable().getCellField(); XdtsTimeTableHeaderItem cellHeader = xdtsData.timeTable().getCellHeader(); @@ -511,9 +558,10 @@ bool XdtsIo::loadXdtsScene(ToonzScene *scene, const TFilePath &scenePath) { QStringList layerNames = cellHeader.getLayerNames(); QList columns = cellField.getOccupiedColumns(); for (int column : columns) { - QString levelName = layerNames.at(column); - TXshLevel *level = levels.value(levelName); - QVector track = cellField.getColumnTrack(column); + QString levelName = layerNames.at(column); + TXshLevel *level = levels.value(levelName); + QList tick1, tick2; + QVector track = cellField.getColumnTrack(column, tick1, tick2); int row = 0; std::vector::iterator it; @@ -531,6 +579,15 @@ bool XdtsIo::loadXdtsScene(ToonzScene *scene, const TFilePath &scenePath) { xsh->setCell(row, column, TXshCell(level, TFrameId(lastFid))); } + // set cell marks + TXshCellColumn *cellColumn = xsh->getColumn(column)->getCellColumn(); + if (tick1Id >= 0) { + for (auto tick1f : tick1) cellColumn->setCellMark(tick1f, tick1Id); + } + if (tick2Id >= 0) { + for (auto tick2f : tick2) cellColumn->setCellMark(tick2f, tick2Id); + } + TStageObject *pegbar = xsh->getStageObject(TStageObjectId::ColumnId(column)); if (pegbar) pegbar->setName(levelName.toStdString()); @@ -572,18 +629,80 @@ void ExportXDTSCommand::execute() { else duration = xsheet->getFrameCount(); - XdtsData xdtsData; - xdtsData.build(xsheet, QString::fromStdString(fp.getName()), duration); - if (xdtsData.isEmpty()) { - DVGui::error(QObject::tr("No columns can be exported.")); - return; + { + _tick1Id = -1; + _tick2Id = -1; + _exportAllColumn = true; + XdtsData pre_xdtsData; + pre_xdtsData.build(xsheet, QString::fromStdString(fp.getName()), duration); + if (pre_xdtsData.isEmpty()) { + DVGui::error(QObject::tr("No columns can be exported.")); + return; + } } static GenericSaveFilePopup *savePopup = 0; + static QComboBox *tick1Id = nullptr; + static QComboBox *tick2Id = nullptr; + static QComboBox *targetColumnCombo = nullptr; + + auto refreshCellMarkComboItems = [](QComboBox *combo) { + int current = -1; + if (combo->count()) current = combo->currentData().toInt(); + + combo->clear(); + QList marks = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMarks(); + combo->addItem(tr("None"), -1); + int curId = 0; + for (auto mark : marks) { + QString label = QString("%1: %2").arg(curId).arg(mark.name); + combo->addItem(getColorChipIcon(mark.color), label, curId); + curId++; + } + if (current >= 0) combo->setCurrentIndex(combo->findData(current)); + }; + if (!savePopup) { + // create custom widget + QWidget *custonWidget = new QWidget(); + tick1Id = new QComboBox(); + tick2Id = new QComboBox(); + refreshCellMarkComboItems(tick1Id); + refreshCellMarkComboItems(tick2Id); + tick1Id->setCurrentIndex(tick1Id->findData(0)); + tick2Id->setCurrentIndex(tick2Id->findData(1)); + targetColumnCombo = new QComboBox(); + targetColumnCombo->addItem(tr("All columns"), true); + targetColumnCombo->addItem(tr("Only active columns"), false); + targetColumnCombo->setCurrentIndex(targetColumnCombo->findData(true)); + + QGridLayout *customLay = new QGridLayout(); + customLay->setMargin(0); + customLay->setSpacing(10); + { + customLay->addWidget(new QLabel(tr("Inbetween symbol mark")), 0, 0, + Qt::AlignRight | Qt::AlignVCenter); + customLay->addWidget(tick1Id, 0, 1); + customLay->addWidget(new QLabel(tr("Reverse sheet symbol mark")), 1, 0, + Qt::AlignRight | Qt::AlignVCenter); + customLay->addWidget(tick2Id, 1, 1); + customLay->addWidget(new QLabel(tr("Target column")), 2, 0, + Qt::AlignRight | Qt::AlignVCenter); + customLay->addWidget(targetColumnCombo, 2, 1); + } + customLay->setColumnStretch(0, 1); + custonWidget->setLayout(customLay); + savePopup = new GenericSaveFilePopup( - QObject::tr("Export Exchange Digital Time Sheet (XDTS)")); + QObject::tr("Export Exchange Digital Time Sheet (XDTS)"), custonWidget); savePopup->addFilterType("xdts"); + } else { + refreshCellMarkComboItems(tick1Id); + refreshCellMarkComboItems(tick2Id); } if (!scene->isUntitled()) savePopup->setFolder(fp.getParentDir()); @@ -601,6 +720,16 @@ void ExportXDTSCommand::execute() { return; } + _tick1Id = tick1Id->currentData().toInt(); + _tick2Id = tick2Id->currentData().toInt(); + _exportAllColumn = targetColumnCombo->currentData().toBool(); + XdtsData xdtsData; + xdtsData.build(xsheet, QString::fromStdString(fp.getName()), duration); + if (xdtsData.isEmpty()) { + DVGui::error(QObject::tr("No columns can be exported.")); + return; + } + QJsonObject xdtsObject; xdtsData.write(xdtsObject); QJsonDocument saveDoc(xdtsObject); diff --git a/toonz/sources/toonz/xdtsio.h b/toonz/sources/toonz/xdtsio.h index d8b1df2..846643f 100644 --- a/toonz/sources/toonz/xdtsio.h +++ b/toonz/sources/toonz/xdtsio.h @@ -83,10 +83,11 @@ class XdtsFrameDataItem { QString fid2Str(const TFrameId &) const; public: + enum { SYMBOL_TICK_1 = -100, SYMBOL_TICK_2 = -200 }; + XdtsFrameDataItem() : m_id(Default) {} XdtsFrameDataItem(TFrameId fId) : m_id(Default) { - m_values.append((fId.getNumber() == -1) ? QString("SYMBOL_NULL_CELL") - : fid2Str(fId)); + m_values.append(fid2Str(fId)); } void read(const QJsonObject &json); void write(QJsonObject &json) const; @@ -132,7 +133,8 @@ public: void write(QJsonObject &json) const; bool isEmpty() const { return m_frames.isEmpty(); } int getTrackNo() const { return m_trackNo; } - QVector getCellFrameIdTrack() const; + QVector getCellFrameIdTrack(QList &tick1, + QList &tick2) const; QString build(TXshCellColumn *); void addFrame(int frame, TFrameId fId) { @@ -154,7 +156,8 @@ public: void write(QJsonObject &json) const; bool isCellField() { return m_fieldId == CELL; } QList getOccupiedColumns() const; - QVector getColumnTrack(int col) const; + QVector getColumnTrack(int col, QList &tick1, + QList &tick2) const; void build(TXsheet *, QStringList &); }; diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index bdc33cf..8c2578b 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -63,7 +63,6 @@ // TnzCore includes #include "tconvert.h" -#include "tundo.h" // Qt includes #include @@ -446,6 +445,14 @@ int getParamStep(TStageObject *stageObject, int frame) { //----------------------------------------------------------------------------- +QIcon getColorChipIcon(TPixel32 color) { + QPixmap pm(15, 15); + pm.fill(QColor(color.r, color.g, color.b)); + return QIcon(pm); +} + +//----------------------------------------------------------------------------- + void setParamStep(int indexKeyframe, int step, TDoubleParam *param) { KeyframeSetter setter(param, indexKeyframe); setter.setStep(step); @@ -550,6 +557,57 @@ bool isCtrlPressed = false; namespace XsheetGUI { //============================================================================= +// SetCellMarkUndo +//----------------------------------------------------------------------------- + +SetCellMarkUndo::SetCellMarkUndo(int row, int col, int idAfter) + : m_row(row), m_col(col), m_idAfter(idAfter) { + TXshCellColumn *cellColumn = TApp::instance() + ->getCurrentXsheet() + ->getXsheet() + ->getColumn(col) + ->getCellColumn(); + assert(cellColumn); + m_idBefore = cellColumn->getCellMark(row); + if (m_idBefore == m_idAfter) m_idAfter = -1; +} + +void SetCellMarkUndo::setId(int id) const { + TXshCellColumn *cellColumn = TApp::instance() + ->getCurrentXsheet() + ->getXsheet() + ->getColumn(m_col) + ->getCellColumn(); + assert(cellColumn); + cellColumn->setCellMark(m_row, id); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); +} + +void SetCellMarkUndo::undo() const { setId(m_idBefore); } + +void SetCellMarkUndo::redo() const { setId(m_idAfter); } + +int SetCellMarkUndo::getSize() const { return sizeof *this; } + +QString SetCellMarkUndo::getHistoryString() { + QString markName; + if (m_idAfter < 0) + markName = QObject::tr("None", "Cell Mark"); + else + markName = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(m_idAfter) + .name; + return QObject::tr("Set Cell Mark at Column %1 Frame %2 to %3") + .arg(QString::number(m_col + 1)) + .arg(QString::number(m_row + 1)) + .arg(markName); +} +int SetCellMarkUndo::getHistoryType() { return HistoryType::Xsheet; } + +//============================================================================= // RenameCellField //----------------------------------------------------------------------------- @@ -1081,7 +1139,7 @@ void CellArea::drawFrameSeparator(QPainter &p, int row, int col, layerAxisRange = NumberRange(layerAxis + 1, layerAxis + adjY); } - // marker interval every 6 frames + // mark interval every 6 frames int distance, offset; TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers( distance, offset); @@ -1424,6 +1482,24 @@ void CellArea::drawSoundCell(QPainter &p, int row, int col, bool isReference) { 1, 1, (!m_viewer->orientation()->isVerticalTimeline() && !isNextEmpty ? 2 : 0), 0); + + int markId = soundColumn->getCellMark(row); + QColor markColor; + if (markId >= 0) { + TPixel32 col = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(markId) + .color; + markColor = QColor(col.r, col.g, col.b, 196); // semi transparent + } + QRect markRect = + o->rect(PredefinedRect::CELL_MARK_AREA) + .adjusted(0, -std::round(double(frameAdj.y()) * 0.1), -frameAdj.y(), + -std::round(double(frameAdj.y()) * 0.9)) + .translated(xy); + int maxNumFrame = soundColumn->getMaxFrame() + 1; int startFrame = soundColumn->getFirstRow(); TXshCell cell = soundColumn->getSoundCell(row); @@ -1443,7 +1519,15 @@ void CellArea::drawSoundCell(QPainter &p, int row, int col, bool isReference) { TXshSoundLevelP soundLevel = cell.getSoundLevel(); int r0, r1; - if (!soundColumn->getLevelRange(row, r0, r1)) return; + if (!soundColumn->getLevelRange(row, r0, r1)) { + // only draw mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } + return; + } bool isFirstRow = (row == r0); bool isLastRow = (row == r1); @@ -1579,6 +1663,13 @@ void CellArea::drawSoundCell(QPainter &p, int row, int col, bool isReference) { p.setPen(m_viewer->getMarkerLineColor()); p.drawLine(o->line(PredefinedLine::SEE_MARKER_THROUGH).translated(xy)); } + + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } } //----------------------------------------------------------------------------- @@ -1686,10 +1777,25 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, TXshCell cell = xsh->getCell(row, col); TXshCell prevCell; + TXshCellColumn *cellColumn = xsh->getColumn(col)->getCellColumn(); + int markId = (cellColumn) ? cellColumn->getCellMark(row) : -1; + QColor markColor; + if (markId >= 0) { + TPixel32 col = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(markId) + .color; + markColor = QColor(col.r, col.g, col.b, 196); // semi transparent + } + TCellSelection *cellSelection = m_viewer->getCellSelection(); TColumnSelection *columnSelection = m_viewer->getColumnSelection(); bool isSelected = cellSelection->isCellSelected(row, col) || columnSelection->isColumnSelected(col); + bool isSimpleView = m_viewer->getFrameZoomFactor() <= + o->dimension(PredefinedDimension::SCALE_THRESHOLD); if (row > 0) prevCell = xsh->getCell(row - 1, col); // cell in previous frame @@ -1714,11 +1820,16 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, .translated(QPoint(x, y)); cellRect.adjust(0, 0, -frameAdj.x(), -frameAdj.y()); QRect rect = cellRect.adjusted( - 1, 1, - (!m_viewer->orientation()->isVerticalTimeline() && !nextCell.isEmpty() - ? 2 - : 0), - 0); + 1, 1, (!o->isVerticalTimeline() && !nextCell.isEmpty() ? 2 : 0), 0); + + QRect markRect = + o->rect(PredefinedRect::CELL_MARK_AREA) + .adjusted(0, -std::round(double(frameAdj.y()) * 0.1), -frameAdj.y(), + -std::round(double(frameAdj.y()) * 0.9)) + .translated(xy); + if (showLevelName && (!isSimpleView || !o->isVerticalTimeline())) + markRect.moveCenter(cellRect.center()); + if (markRect.right() > rect.right()) markRect.setRight(rect.right()); // get cell colors QColor cellColor, sideColor; @@ -1747,6 +1858,14 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, p.fillRect(rect, QBrush(cellColor)); } + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + // p.fillRect(rect, QBrush(markColor)); + } + drawFrameSeparator(p, row, col, true); if (TApp::instance()->getCurrentFrame()->isEditingScene() && @@ -1763,6 +1882,14 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, if (cell.isEmpty()) { // it means previous is not empty // diagonal cross meaning end of level + + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } + QColor levelEndColor = m_viewer->getTextColor(); levelEndColor.setAlphaF(0.3); p.setPen(levelEndColor); @@ -1806,14 +1933,6 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, else p.fillRect(rect, QBrush(cellColor)); - if (TApp::instance()->getCurrentFrame()->isEditingScene() && - !m_viewer->orientation()->isVerticalTimeline() && - row == m_viewer->getCurrentRow() && - Preferences::instance()->isCurrentTimelineIndicatorEnabled()) - drawCurrentTimeIndicator(p, xy); - - drawDragHandle(p, xy, sideColor); - if (yetToCleanupCell) // ORIENTATION: what's this? { if (o->isVerticalTimeline()) @@ -1826,6 +1945,14 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, : m_viewer->getFullcolorColumnColor()); } + if (TApp::instance()->getCurrentFrame()->isEditingScene() && + !m_viewer->orientation()->isVerticalTimeline() && + row == m_viewer->getCurrentRow() && + Preferences::instance()->isCurrentTimelineIndicatorEnabled()) + drawCurrentTimeIndicator(p, xy); + + drawDragHandle(p, xy, sideColor); + bool isLastRow = nextCell.isEmpty() || cell.m_level.getPointer() != nextCell.m_level.getPointer(); drawEndOfDragHandle(p, isLastRow, xy, cellColor); @@ -1838,8 +1965,6 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, // if (distance == 0) distance = 6; bool isAfterMarkers = distance > 0 && ((row - offset) % distance) == 0 && row != 0; - bool isSimpleView = m_viewer->getFrameZoomFactor() <= - o->dimension(PredefinedDimension::SCALE_THRESHOLD); // draw marker interval if (o->isVerticalTimeline() && isAfterMarkers) { @@ -1889,27 +2014,36 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, // draw continue line QString fnum; if (sameLevel && prevCell.m_frameId == cell.m_frameId) { - if (!o->isVerticalTimeline()) return; + if (o->isVerticalTimeline()) { + // not on line marker + PredefinedLine which = + Preferences::instance()->isLevelNameOnEachMarkerEnabled() + ? PredefinedLine::CONTINUE_LEVEL_WITH_NAME + : PredefinedLine::CONTINUE_LEVEL; - // not on line marker - PredefinedLine which = - Preferences::instance()->isLevelNameOnEachMarkerEnabled() - ? PredefinedLine::CONTINUE_LEVEL_WITH_NAME - : PredefinedLine::CONTINUE_LEVEL; + QLine continueLine = o->line(which).translated(xy); + continueLine.setP2(QPoint(continueLine.x2(), continueLine.y2()) - + frameAdj); - QLine continueLine = o->line(which).translated(xy); - continueLine.setP2(QPoint(continueLine.x2(), continueLine.y2()) - frameAdj); + if (!showLevelName) { + penColor.setAlphaF(0.5); + p.setPen(penColor); + } - if (!showLevelName) { - penColor.setAlphaF(0.5); - p.setPen(penColor); + p.drawLine(continueLine); } - - p.drawLine(continueLine); } // draw frame number else { if (isSimpleView) { + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + p.setPen(penColor); + } + if (!o->isVerticalTimeline()) { // Lets not draw normal marker if there is a keyframe here TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); @@ -1940,6 +2074,14 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference, p.drawText(nameRect, alignFlag, fnum); } + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + p.setPen(penColor); + } + // draw level name if (showLevelName && (!sameLevel || @@ -1989,6 +2131,34 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { xy.setX(xy.x() + 1); } + TXshCell nextCell = xsh->getCell(row + 1, col); + QPoint frameAdj = m_viewer->getFrameZoomAdjustment(); + QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); + cellRect.adjust(0, 0, -frameAdj.x(), -frameAdj.y()); + QRect rect = cellRect.adjusted( + 1, 1, + (!m_viewer->orientation()->isVerticalTimeline() && !nextCell.isEmpty() + ? 2 + : 0), + 0); + int markId = xsh->getColumn(col)->getCellColumn()->getCellMark(row); + QColor markColor; + if (markId >= 0) { + TPixel32 col = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(markId) + .color; + markColor = QColor(col.r, col.g, col.b, 196); // semi transparent + } + QRect markRect = + o->rect(PredefinedRect::CELL_MARK_AREA) + .adjusted(0, -std::round(double(frameAdj.y()) * 0.1), -frameAdj.y(), + -std::round(double(frameAdj.y()) * 0.9)) + .translated(xy); + if (markRect.right() > rect.right()) markRect.setRight(rect.right()); + if (cell.isEmpty() && prevCell.isEmpty()) { drawFrameSeparator(p, row, col, true); if (TApp::instance()->getCurrentFrame()->isEditingScene() && @@ -1997,6 +2167,12 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { Preferences::instance()->isCurrentTimelineIndicatorEnabled()) drawCurrentTimeIndicator(p, xy); + // only draw mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } return; } @@ -2004,18 +2180,6 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { prevCell.m_frameId == cell.m_frameId); drawFrameSeparator(p, row, col, false, heldFrame); - TXshCell nextCell; - nextCell = xsh->getCell(row + 1, col); - - QPoint frameAdj = m_viewer->getFrameZoomAdjustment(); - QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); - cellRect.adjust(0, 0, -frameAdj.x(), -frameAdj.y()); - QRect rect = cellRect.adjusted( - 1, 1, - (!m_viewer->orientation()->isVerticalTimeline() && !nextCell.isEmpty() - ? 2 - : 0), - 0); if (cell.isEmpty()) { // diagonal cross meaning end of level QColor levelEndColor = m_viewer->getTextColor(); levelEndColor.setAlphaF(0.3); @@ -2023,6 +2187,12 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { p.drawLine(rect.topLeft(), rect.bottomRight()); p.drawLine(rect.topRight(), rect.bottomLeft()); + // only draw mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } if (TApp::instance()->getCurrentFrame()->isEditingScene() && !m_viewer->orientation()->isVerticalTimeline() && row == m_viewer->getCurrentRow() && @@ -2060,6 +2230,13 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { drawLockedDottedLine(p, xsh->getColumn(col)->isLocked(), xy, cellColor); + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } + TFrameId fid = cell.m_frameId; if (fid.getNumber() - 1 < 0) return; @@ -2155,6 +2332,9 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, TXshPaletteLevel *pl = cell.getPaletteLevel(); if (pl && !pl->getPalette()) isRed = true; + bool isSimpleView = m_viewer->getFrameZoomFactor() <= + o->dimension(PredefinedDimension::SCALE_THRESHOLD); + QPoint xy = m_viewer->positionToXY(CellPosition(row, col)); int x = xy.x(); int y = xy.y(); @@ -2165,6 +2345,35 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, xy.setX(xy.x() + 1); } + QPoint frameAdj = m_viewer->getFrameZoomAdjustment(); + QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); + cellRect.adjust(0, 0, -frameAdj.x(), -frameAdj.y()); + QRect rect = cellRect.adjusted( + 1, 1, + (!m_viewer->orientation()->isVerticalTimeline() && !nextCell.isEmpty() + ? 2 + : 0), + 0); + int markId = xsh->getColumn(col)->getCellColumn()->getCellMark(row); + QColor markColor; + if (markId >= 0) { + TPixel32 col = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMark(markId) + .color; + markColor = QColor(col.r, col.g, col.b, 196); // semi transparent + } + QRect markRect = + o->rect(PredefinedRect::CELL_MARK_AREA) + .adjusted(0, -std::round(double(frameAdj.y()) * 0.1), -frameAdj.y(), + -std::round(double(frameAdj.y()) * 0.9)) + .translated(xy); + if (!isSimpleView || !o->isVerticalTimeline()) + markRect.moveCenter(cellRect.center()); + if (markRect.right() > rect.right()) markRect.setRight(rect.right()); + if (cell.isEmpty() && prevCell.isEmpty()) { drawFrameSeparator(p, row, col, true); if (TApp::instance()->getCurrentFrame()->isEditingScene() && @@ -2173,6 +2382,12 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, Preferences::instance()->isCurrentTimelineIndicatorEnabled()) drawCurrentTimeIndicator(p, xy); + // only draw mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } return; } @@ -2180,15 +2395,6 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, prevCell.m_frameId == cell.m_frameId); drawFrameSeparator(p, row, col, false, heldFrame); - QPoint frameAdj = m_viewer->getFrameZoomAdjustment(); - QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); - cellRect.adjust(0, 0, -frameAdj.x(), -frameAdj.y()); - QRect rect = cellRect.adjusted( - 1, 1, - (!m_viewer->orientation()->isVerticalTimeline() && !nextCell.isEmpty() - ? 2 - : 0), - 0); if (cell.isEmpty()) { // this means the former is not empty QColor levelEndColor = m_viewer->getTextColor(); levelEndColor.setAlphaF(0.3); @@ -2196,6 +2402,12 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, p.drawLine(rect.topLeft(), rect.bottomRight()); p.drawLine(rect.topRight(), rect.bottomLeft()); + // only draw mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } if (TApp::instance()->getCurrentFrame()->isEditingScene() && !m_viewer->orientation()->isVerticalTimeline() && row == m_viewer->getCurrentRow() && @@ -2245,16 +2457,24 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, if (sameLevel && prevCell.m_frameId == cell.m_frameId && !isAfterMarkers) { // cell equal to previous one (not on marker line): // do not write anything and draw a vertical line - if (!o->isVerticalTimeline()) return; - QPen oldPen = p.pen(); - p.setPen(QPen(m_viewer->getTextColor(), 1)); - QLine continueLine = o->line(PredefinedLine::CONTINUE_LEVEL).translated(xy); - continueLine.setP2(QPoint(continueLine.x2(), continueLine.y2()) - frameAdj); - p.drawLine(continueLine); - p.setPen(oldPen); + if (o->isVerticalTimeline()) { + QPen oldPen = p.pen(); + p.setPen(QPen(m_viewer->getTextColor(), 1)); + QLine continueLine = + o->line(PredefinedLine::CONTINUE_LEVEL).translated(xy); + continueLine.setP2(QPoint(continueLine.x2(), continueLine.y2()) - + frameAdj); + p.drawLine(continueLine); + p.setPen(oldPen); + } } else { - if (m_viewer->getFrameZoomFactor() <= - o->dimension(PredefinedDimension::SCALE_THRESHOLD)) { + if (isSimpleView) { + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } if (!o->isVerticalTimeline()) { // Lets not draw normal marker if there is a keyframe here TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); @@ -2332,6 +2552,13 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, if (!sameLevel || isAfterMarkers) p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName); } + + // cell mark + if (markId >= 0) { + p.setBrush(markColor); + p.setPen(Qt::NoPen); + p.drawEllipse(markRect); + } } //----------------------------------------------------------------------------- @@ -3136,13 +3363,13 @@ void CellArea::contextMenuEvent(QContextMenuEvent *event) { } if (areCellsEmpty) break; } - createCellMenu(menu, areCellsEmpty, cell); + createCellMenu(menu, areCellsEmpty, cell, row, col); } else { m_viewer->getCellSelection()->makeCurrent(); m_viewer->getCellSelection()->selectCell(row, col); m_viewer->setCurrentColumn(col); - createCellMenu(menu, !cell.isEmpty(), cell); + createCellMenu(menu, !cell.isEmpty(), cell, row, col); } if (!menu.isEmpty()) menu.exec(event->globalPos()); @@ -3231,7 +3458,8 @@ void CellArea::onControlPressed(bool pressed) { const bool CellArea::isControlPressed() { return isCtrlPressed; } //----------------------------------------------------------------------------- -void CellArea::createCellMenu(QMenu &menu, bool isCellSelected, TXshCell cell) { +void CellArea::createCellMenu(QMenu &menu, bool isCellSelected, TXshCell cell, + int row, int col) { CommandManager *cmdManager = CommandManager::instance(); bool soundCellsSelected = m_viewer->areSoundCellsSelected(); @@ -3419,10 +3647,21 @@ void CellArea::createCellMenu(QMenu &menu, bool isCellSelected, TXshCell cell) { } else if (selectionContainTlvImage(m_viewer->getCellSelection(), m_viewer->getXsheet())) menu.addAction(cmdManager->getAction(MI_CanvasSize)); - if (sl || - (TApp::instance()->getCurrentLevel()->getLevel() && - TApp::instance()->getCurrentLevel()->getLevel()->getChildLevel())) - menu.addAction(cmdManager->getAction(MI_LipSyncPopup)); + + QMenu *lipSyncMenu = new QMenu(tr("Lip Sync"), this); + { + if (sl || + (TApp::instance()->getCurrentLevel()->getLevel() && + TApp::instance()->getCurrentLevel()->getLevel()->getChildLevel())) + lipSyncMenu->addAction(cmdManager->getAction(MI_LipSyncPopup)); + if (!soundCellsSelected) + lipSyncMenu->addAction(cmdManager->getAction(MI_ImportMagpieFile)); + } + if (lipSyncMenu->actions().isEmpty()) + delete lipSyncMenu; + else + menu.addMenu(lipSyncMenu); + } else { menu.addAction(cmdManager->getAction(MI_CreateBlankDrawing)); menu.addSeparator(); @@ -3434,9 +3673,40 @@ void CellArea::createCellMenu(QMenu &menu, bool isCellSelected, TXshCell cell) { menu.addAction(cmdManager->getAction(MI_ShiftKeyframesUp)); } } - menu.addSeparator(); - if (!soundCellsSelected) - menu.addAction(cmdManager->getAction(MI_ImportMagpieFile)); + + // cell mark menu + TXshCellColumn *cellColumn = + (m_viewer->getXsheet()->getColumn(col)) + ? m_viewer->getXsheet()->getColumn(col)->getCellColumn() + : nullptr; + if (cellColumn) { + QMenu *marksMenu = new QMenu(tr("Cell Mark"), this); + int markId = cellColumn->getCellMark(row); + QAction *markAction = marksMenu->addAction(tr("None")); + markAction->setCheckable(true); + markAction->setChecked(markId == -1); + markAction->setEnabled(markId != -1); + markAction->setData(QList{row, col, -1}); + connect(markAction, SIGNAL(triggered()), this, SLOT(onSetCellMark())); + QList marks = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getCellMarks(); + int curId = 0; + for (auto mark : marks) { + QString label = QString("%1: %2").arg(curId).arg(mark.name); + markAction = marksMenu->addAction(getColorChipIcon(mark.color), label); + markAction->setCheckable(true); + markAction->setChecked(markId == curId); + markAction->setEnabled(markId != curId); + markAction->setData(QList{row, col, curId}); + connect(markAction, SIGNAL(triggered()), this, SLOT(onSetCellMark())); + curId++; + } + + menu.addMenu(marksMenu); + } } //----------------------------------------------------------------------------- @@ -3689,4 +3959,19 @@ void CellArea::onStepChanged(QAction *act) { //----------------------------------------------------------------------------- +void CellArea::onSetCellMark() { + QAction *senderAction = qobject_cast(sender()); + assert(senderAction); + QList params = senderAction->data().toList(); + assert(params.count() == 3); + int row = params[0].toInt(); + int col = params[1].toInt(); + int id = params[2].toInt(); + SetCellMarkUndo *undo = new SetCellMarkUndo(row, col, id); + undo->redo(); + TUndoManager::manager()->add(undo); +} + +//----------------------------------------------------------------------------- + } // namespace XsheetGUI diff --git a/toonz/sources/toonz/xshcellviewer.h b/toonz/sources/toonz/xshcellviewer.h index bc89374..8e27466 100644 --- a/toonz/sources/toonz/xshcellviewer.h +++ b/toonz/sources/toonz/xshcellviewer.h @@ -8,6 +8,7 @@ #include "orientation.h" #include "toonz/txshcell.h" +#include "tundo.h" // forward declaration class XsheetViewer; @@ -17,6 +18,20 @@ class TXshSoundTextColumn; namespace XsheetGUI { +class SetCellMarkUndo final : public TUndo { + int m_row, m_col; + int m_idBefore, m_idAfter; + +public: + SetCellMarkUndo(int row, int col, int idAfter); + void setId(int id) const; + void undo() const override; + void redo() const override; + int getSize() const override; + QString getHistoryString() override; + int getHistoryType() override; +}; + class NoteWidget; class DragTool; @@ -154,7 +169,8 @@ protected: /*!Crea il menu' del tasto destro che si visualizza quando si clicca sulla cella, distinguendo i due casi: cella piena, cella vuota.*/ - void createCellMenu(QMenu &menu, bool isCellSelected, TXshCell cell); + void createCellMenu(QMenu &menu, bool isCellSelected, TXshCell cell, int row, + int col); //! Crea il menu' del tasto destro che si visualizza si clicca su un key //! frame. void createKeyMenu(QMenu &menu); @@ -170,6 +186,7 @@ protected slots: void onStepChanged(QAction *); // replace level with another level in the cast void onReplaceByCastedLevel(QAction *action); + void onSetCellMark(); }; } // namespace XsheetGUI diff --git a/toonz/sources/toonz/xsheetcmd.cpp b/toonz/sources/toonz/xsheetcmd.cpp index 93b2c48..7e360dd 100644 --- a/toonz/sources/toonz/xsheetcmd.cpp +++ b/toonz/sources/toonz/xsheetcmd.cpp @@ -59,6 +59,7 @@ #include "duplicatepopup.h" #include "menubarcommandids.h" #include "columncommand.h" +#include "xshcellviewer.h" // SetCellMarkUndo // Qt includes #include @@ -2145,3 +2146,41 @@ public: } } ToggleXsheetCameraColumnCommand; + +//----------------------------------------------------------------------------- + +class SetCellMarkCommand final : public MenuItemHandler { + int m_markId; + +public: + SetCellMarkCommand(int markId) + : MenuItemHandler( + ((std::string)MI_SetCellMark + std::to_string(markId)).c_str()) + , m_markId(markId) {} + + void execute() override { + TApp *app = TApp::instance(); + TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); + int currentRow = app->getCurrentFrame()->getFrame(); + int currentColumn = app->getCurrentColumn()->getColumnIndex(); + if (!xsh->getColumn(currentColumn)) return; + TXshCellColumn *cellColumn = xsh->getColumn(currentColumn)->getCellColumn(); + if (!cellColumn) return; + XsheetGUI::SetCellMarkUndo *undo = + new XsheetGUI::SetCellMarkUndo(currentRow, currentColumn, m_markId); + undo->redo(); + TUndoManager::manager()->add(undo); + } +}; +SetCellMarkCommand CellMarkCommand0(0); +SetCellMarkCommand CellMarkCommand1(1); +SetCellMarkCommand CellMarkCommand2(2); +SetCellMarkCommand CellMarkCommand3(3); +SetCellMarkCommand CellMarkCommand4(4); +SetCellMarkCommand CellMarkCommand5(5); +SetCellMarkCommand CellMarkCommand6(6); +SetCellMarkCommand CellMarkCommand7(7); +SetCellMarkCommand CellMarkCommand8(8); +SetCellMarkCommand CellMarkCommand9(9); +SetCellMarkCommand CellMarkCommand10(10); +SetCellMarkCommand CellMarkCommand11(11); \ No newline at end of file diff --git a/toonz/sources/toonzlib/orientation.cpp b/toonz/sources/toonzlib/orientation.cpp index aad2e55..1c6a54b 100644 --- a/toonz/sources/toonzlib/orientation.cpp +++ b/toonz/sources/toonzlib/orientation.cpp @@ -333,6 +333,9 @@ TopToBottomOrientation::TopToBottomOrientation() { addRect(PredefinedRect::KEYFRAME_AREA, QRect(CELL_WIDTH - KEY_ICON_WIDTH, 0, KEY_ICON_WIDTH, CELL_HEIGHT)); addRect(PredefinedRect::DRAG_AREA, QRect(0, 0, CELL_DRAG_WIDTH, CELL_HEIGHT)); + int markSize = CELL_HEIGHT * 8 / 10; // 80% size + addRect(PredefinedRect::CELL_MARK_AREA, + QRect(CELL_DRAG_WIDTH - 3, 2, markSize, markSize)); QRect soundRect(CELL_DRAG_WIDTH, 0, CELL_WIDTH - CELL_DRAG_WIDTH - SOUND_PREVIEW_WIDTH, CELL_HEIGHT); @@ -1104,6 +1107,9 @@ LeftToRightOrientation::LeftToRightOrientation() { EXTENDER_HEIGHT)); addRect(PredefinedRect::KEYFRAME_AREA, keyRect); addRect(PredefinedRect::DRAG_AREA, QRect(0, 0, CELL_WIDTH, CELL_DRAG_HEIGHT)); + int markSize = CELL_HEIGHT / 2; // 50% size (12px) + addRect(PredefinedRect::CELL_MARK_AREA, + QRect(1, CELL_DRAG_HEIGHT + 1, markSize, markSize)); QRect soundRect(0, CELL_DRAG_HEIGHT, CELL_WIDTH, CELL_HEIGHT - CELL_DRAG_HEIGHT - SOUND_PREVIEW_HEIGHT); addRect(PredefinedRect::SOUND_TRACK, soundRect); diff --git a/toonz/sources/toonzlib/sceneproperties.cpp b/toonz/sources/toonzlib/sceneproperties.cpp index 7bc3c59..72627fe 100644 --- a/toonz/sources/toonzlib/sceneproperties.cpp +++ b/toonz/sources/toonzlib/sceneproperties.cpp @@ -24,6 +24,23 @@ #include "tproperty.h" #include "tiio.h" +namespace { +const TSceneProperties::CellMark cellMarkDefault[12] = { + {QObject::tr("Red"), TPixel32(167, 55, 55)}, + {QObject::tr("Orange"), TPixel32(195, 115, 40)}, + {QObject::tr("Yellow"), TPixel32(214, 183, 22)}, + {QObject::tr("Light Green"), TPixel32(165, 179, 57)}, + {QObject::tr("Green"), TPixel32(82, 157, 79)}, + {QObject::tr("Light Blue"), TPixel32(71, 142, 165)}, + {QObject::tr("Blue"), TPixel32(64, 103, 172)}, + {QObject::tr("Dark Blue"), TPixel32(60, 49, 187)}, + {QObject::tr("Purple"), TPixel32(108, 66, 170)}, + {QObject::tr("Pink"), TPixel32(161, 75, 140)}, + {QObject::tr("Dark Pink"), TPixel32(111, 29, 108)}, + {QObject::tr("White"), TPixel32(255, 255, 255)}}; + +} + //============================================================================= TSceneProperties::TSceneProperties() @@ -50,6 +67,9 @@ TSceneProperties::TSceneProperties() m_notesColor.push_back(TPixel32(145, 240, 145)); m_notesColor.push_back(TPixel32(130, 255, 210)); m_notesColor.push_back(TPixel32(150, 245, 255)); + + // Default Cell Marks + for (int i = 0; i < 12; i++) m_cellMarks.push_back(cellMarkDefault[i]); } //----------------------------------------------------------------------------- @@ -82,7 +102,7 @@ void TSceneProperties::assign(const TSceneProperties *sprop) { if (sprop != this) { m_cameras = sprop->m_cameras; - for (int i = 0; i < (int)m_cameras.size(); i++) + for (int i = 0; i < (int)m_cameras.size(); i++) m_cameras[i] = new TCamera(*m_cameras[i]); } m_bgColor = sprop->m_bgColor; @@ -149,7 +169,7 @@ void TSceneProperties::setFieldGuideSize(int size) { void TSceneProperties::setFieldGuideAspectRatio(double ar) { assert(ar >= 0); - if (ar <= 0) ar = 1; + if (ar <= 0) ar = 1; m_fieldGuideAspectRatio = ar; } @@ -203,8 +223,8 @@ void TSceneProperties::saveData(TOStream &os) const { os.child("threadsIndex") << out.getThreadIndex(); os.child("maxTileSizeIndex") << out.getMaxTileSizeIndex(); os.child("subcameraPrev") << (out.isSubcameraPreview() ? 1 : 0); - os.child("stereoscopic") << (rs.m_stereoscopic ? 1 : 0) - << rs.m_stereoscopicShift; + os.child("stereoscopic") + << (rs.m_stereoscopic ? 1 : 0) << rs.m_stereoscopicShift; switch (rs.m_quality) { case TRenderSettings::StandardResampleQuality: @@ -323,6 +343,12 @@ void TSceneProperties::saveData(TOStream &os) const { os.openChild("noteColors"); for (i = 0; i < m_notesColor.size(); i++) os << m_notesColor.at(i); os.closeChild(); + + if (!hasDefaultCellMarks()) { + os.openChild("cellMarks"); + for (auto mark : m_cellMarks) os << mark.name.toStdString() << mark.color; + os.closeChild(); + } } //----------------------------------------------------------------------------- @@ -423,7 +449,7 @@ void TSceneProperties::loadData(TIStream &is, bool isLoadingProject) { if (name == "preview") outPtr = m_previewProp; else if (name == "main") - outPtr = m_outputProp; + outPtr = m_outputProp; TOutputProperties &out = *outPtr; TRenderSettings renderSettings; if (globFrom != -1) @@ -707,6 +733,15 @@ void TSceneProperties::loadData(TIStream &is, bool isLoadingProject) { assert(i == 7); } else if (tagName == "cameraCaputureSaveInPath") { is >> m_camCapSaveInPath; + } else if (tagName == "cellMarks") { + int i = 0; + while (!is.eos()) { + TPixel32 color; + std::string name; + is >> name >> color; + m_cellMarks.replace(i, {QString::fromStdString(name), color}); + i++; + } } else { throw TException("unexpected property tag: " + tagName); } @@ -780,3 +815,34 @@ TPixel32 TSceneProperties::getNoteColor(int colorIndex) const { void TSceneProperties::setNoteColor(TPixel32 color, int colorIndex) { m_notesColor[colorIndex] = color; } + +//----------------------------------------------------------------------------- + +QList TSceneProperties::getCellMarks() const { + return m_cellMarks; +} + +//----------------------------------------------------------------------------- + +TSceneProperties::CellMark TSceneProperties::getCellMark(int index) const { + return m_cellMarks[index]; +} + +//----------------------------------------------------------------------------- + +void TSceneProperties::setCellMark(const TSceneProperties::CellMark &mark, + int index) { + m_cellMarks[index] = mark; +} + +//----------------------------------------------------------------------------- +// check if the cell mark settings are modified +bool TSceneProperties::hasDefaultCellMarks() const { + if (m_cellMarks.size() != 12) return false; + for (int i = 0; i < 12; i++) { + if (m_cellMarks.at(i).name != cellMarkDefault[i].name || + m_cellMarks.at(i).color != cellMarkDefault[i].color) + return false; + } + return true; +} \ No newline at end of file diff --git a/toonz/sources/toonzlib/txshcolumn.cpp b/toonz/sources/toonzlib/txshcolumn.cpp index 2f4f6c7..df5dcad 100644 --- a/toonz/sources/toonzlib/txshcolumn.cpp +++ b/toonz/sources/toonzlib/txshcolumn.cpp @@ -19,6 +19,7 @@ #include "toonz/txshleveltypes.h" #include +#include "tstream.h" namespace { QMap> filterColors; @@ -144,12 +145,12 @@ void TXshCellColumn::getCells(int row, int rowCount, TXshCell cells[]) { } if (n + src > cellCount) n = cellCount - src; - TXshCell *dstCell = cells; - TXshCell *endDstCell = dstCell + dst; + TXshCell *dstCell = cells; + TXshCell *endDstCell = dstCell + dst; while (dstCell < endDstCell) *dstCell++ = emptyCell; endDstCell += n; while (dstCell < endDstCell) *dstCell++ = m_cells[src++]; - endDstCell = cells + rowCount; + endDstCell = cells + rowCount; while (dstCell < endDstCell) *dstCell++ = emptyCell; } @@ -243,7 +244,7 @@ bool TXshCellColumn::setCells(int row, int rowCount, const TXshCell cells[]) { if (row > c_rb) // sono oltre l'ultima riga { if (oldCellCount == 0) m_first = row; // row 'e la nuova firstrow - int newCellCount = row - m_first + rowCount; + int newCellCount = row - m_first + rowCount; m_cells.resize(newCellCount); } else if (row < m_first) { int delta = m_first - row; @@ -278,8 +279,9 @@ bool TXshCellColumn::setCells(int row, int rowCount, const TXshCell cells[]) { //----------------------------------------------------------------------------- void TXshCellColumn::insertEmptyCells(int row, int rowCount) { - if (m_cells.empty()) return; // se la colonna e' vuota non devo inserire - // celle + if (m_cells.empty()) + return; // se la colonna e' vuota non devo inserire + // celle if (row >= m_first + (int)m_cells.size()) return; // dopo:non inserisco nulla if (row <= m_first) // prima @@ -426,6 +428,70 @@ bool TXshCellColumn::getLevelRange(int row, int &r0, int &r1) const { return true; } +//----------------------------------------------------------------------------- + +void TXshCellColumn::saveCellMarks(TOStream &os) { + if (m_cellMarkIds.isEmpty()) return; + // gather frame numbers with the same id + QMap idStrMap; + QMap::const_iterator i = m_cellMarkIds.constBegin(); + while (i != m_cellMarkIds.constEnd()) { + if (!idStrMap.contains(i.value())) + idStrMap.insert(i.value(), QString::number(i.key())); + else + idStrMap[i.value()] += " " + QString::number(i.key()); + ++i; + } + os.openChild("cellMarks"); + QMap::const_iterator j = idStrMap.constBegin(); + while (j != idStrMap.constEnd()) { + std::map attr; + attr["id"] = std::to_string(j.key()); + os.openChild("cellMark", attr); + os << j.value(); + os.closeChild(); + ++j; + } + os.closeChild(); +} + +bool TXshCellColumn::loadCellMarks(std::string tagName, TIStream &is) { + if (tagName != "cellMarks") return false; + m_cellMarkIds.clear(); + while (is.openChild(tagName)) { + if (tagName == "cellMark") { + int id; + QString frameStr; + if (is.getTagParam("id", id)) { + is >> frameStr; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList frameStrList = frameStr.split(" ", Qt::SkipEmptyParts); +#else + QStringList frameStrList = frameStr.split(" ", QString::SkipEmptyParts); +#endif + for (auto fStr : frameStrList) m_cellMarkIds.insert(fStr.toInt(), id); + } + } + is.closeChild(); + } + return true; +} + +void TXshCellColumn::setCellMark(int frame, int id) { + if (id < 0) + m_cellMarkIds.remove(frame); + else + m_cellMarkIds.insert(frame, id); +} + +int TXshCellColumn::getCellMark(int frame) const { + return m_cellMarkIds.value(frame, -1); +} + +QMap TXshCellColumn::getCellMarks() const { return m_cellMarkIds; } + +void TXshCellColumn::clearCellMarks() { m_cellMarkIds.clear(); } + //============================================================================= // TXshColumn diff --git a/toonz/sources/toonzlib/txsheet.cpp b/toonz/sources/toonzlib/txsheet.cpp index 02b41de..cc7d02b 100644 --- a/toonz/sources/toonzlib/txsheet.cpp +++ b/toonz/sources/toonzlib/txsheet.cpp @@ -1219,6 +1219,12 @@ void TXsheet::loadData(TIStream &is) { } } } + } else if (tagName == "cameraColumn") { + while (is.openChild(tagName)) { + if (!m_cameraColumn->getCellColumn()->loadCellMarks(tagName, is)) + throw TException("Camera Column, unknown tag: " + tagName); + is.closeChild(); + } } else if (tagName == "pegbars") { TPersist *p = m_imp->m_pegTree; m_imp->m_pegTree->loadData(is, this); @@ -1275,6 +1281,14 @@ void TXsheet::saveData(TOStream &os) { if (column && c < getFirstFreeColumnIndex()) os << column.getPointer(); } os.closeChild(); + + // save cell marks in the camera column + if (!m_cameraColumn->getCellColumn()->getCellMarks().isEmpty()) { + os.openChild("cameraColumn"); + m_cameraColumn->getCellColumn()->saveCellMarks(os); + os.closeChild(); + } + os.openChild("pegbars"); m_imp->m_pegTree->saveData(os, getFirstFreeColumnIndex(), this); // os << *(m_imp->m_pegTree); diff --git a/toonz/sources/toonzlib/txshlevelcolumn.cpp b/toonz/sources/toonzlib/txshlevelcolumn.cpp index affee58..609c93a 100644 --- a/toonz/sources/toonzlib/txshlevelcolumn.cpp +++ b/toonz/sources/toonzlib/txshlevelcolumn.cpp @@ -151,9 +151,10 @@ void TXshLevelColumn::loadData(TIStream &is) { { TFxSet fxSet; fxSet.loadData(is); - } else { + } else if (loadCellMarks(tagName, is)) { + // do nothing + } else throw TException("TXshLevelColumn, unknown tag: " + tagName); - } is.closeChild(); } } @@ -201,6 +202,9 @@ void TXshLevelColumn::saveData(TOStream &os) { os.closeChild(); } os.child("fx") << m_fx; + + // cell marks + saveCellMarks(os); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/txshmeshcolumn.cpp b/toonz/sources/toonzlib/txshmeshcolumn.cpp index 76302db..7e0080d 100644 --- a/toonz/sources/toonzlib/txshmeshcolumn.cpp +++ b/toonz/sources/toonzlib/txshmeshcolumn.cpp @@ -111,6 +111,8 @@ void TXshMeshColumn::saveData(TOStream &os) { } os.closeChild(); } + // cell marks + saveCellMarks(os); } //------------------------------------------------------------------ @@ -168,6 +170,8 @@ void TXshMeshColumn::loadData(TIStream &is) { } is.closeChild(); + } else if (loadCellMarks(tagName, is)) { + is.closeChild(); } else is.skipCurrentTag(); } diff --git a/toonz/sources/toonzlib/txshpalettecolumn.cpp b/toonz/sources/toonzlib/txshpalettecolumn.cpp index 45203d6..2c37726 100644 --- a/toonz/sources/toonzlib/txshpalettecolumn.cpp +++ b/toonz/sources/toonzlib/txshpalettecolumn.cpp @@ -73,6 +73,8 @@ void TXshPaletteColumn::loadData(TIStream &is) { TPersist *p = 0; is >> p; if (TFx *fx = dynamic_cast(p)) setFx(fx); + } else if (loadCellMarks(tagName, is)) { + // do nothing } else { throw TException("TXshLevelColumn, unknown tag: " + tagName); } @@ -107,6 +109,9 @@ void TXshPaletteColumn::saveData(TOStream &os) { os.closeChild(); } os.child("fx") << m_fx; + + // cell marks + saveCellMarks(os); } PERSIST_IDENTIFIER(TXshPaletteColumn, "paletteColumn") diff --git a/toonz/sources/toonzlib/txshsoundcolumn.cpp b/toonz/sources/toonzlib/txshsoundcolumn.cpp index afcbe55..6a328bc 100644 --- a/toonz/sources/toonzlib/txshsoundcolumn.cpp +++ b/toonz/sources/toonzlib/txshsoundcolumn.cpp @@ -260,6 +260,13 @@ void TXshSoundColumn::loadData(TIStream &is) { is >> status; setStatusWord(status); } + + std::string tagName; + while (is.openChild(tagName)) { + if (!loadCellMarks(tagName, is)) + throw TException("TXshLevelColumn, unknown tag: " + tagName); + is.closeChild(); + } } //----------------------------------------------------------------------------- @@ -272,6 +279,8 @@ void TXshSoundColumn::saveData(TOStream &os) { int i; for (i = 0; i < levelsCount; i++) m_levels.at(i)->saveData(os); os << getStatusWord(); + // cell marks + saveCellMarks(os); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzlib/txshsoundtextcolumn.cpp b/toonz/sources/toonzlib/txshsoundtextcolumn.cpp index 163fec3..65e4752 100644 --- a/toonz/sources/toonzlib/txshsoundtextcolumn.cpp +++ b/toonz/sources/toonzlib/txshsoundtextcolumn.cpp @@ -80,6 +80,8 @@ void TXshSoundTextColumn::loadData(TIStream &is) { throw TException("TXshLevelColumn, unknown tag(2): " + tagName); is.closeChild(); } + } else if (loadCellMarks(tagName, is)) { + // do nothing } else throw TException("TXshLevelColumn, unknown tag: " + tagName); is.closeChild(); @@ -100,6 +102,8 @@ void TXshSoundTextColumn::saveData(TOStream &os) { } os.closeChild(); } + // cell marks + saveCellMarks(os); } PERSIST_IDENTIFIER(TXshSoundTextColumn, "soundTextColumn") diff --git a/toonz/sources/toonzlib/txshzeraryfxcolumn.cpp b/toonz/sources/toonzlib/txshzeraryfxcolumn.cpp index b7940d5..17bf05a 100644 --- a/toonz/sources/toonzlib/txshzeraryfxcolumn.cpp +++ b/toonz/sources/toonzlib/txshzeraryfxcolumn.cpp @@ -162,6 +162,8 @@ void TXshZeraryFxColumn::loadData(TIStream &is) { throw TException("expected "); is.closeChild(); } + } else if (loadCellMarks(tagName, is)) { + // do nothing } else throw TException("expected or "); is.closeChild(); @@ -186,6 +188,8 @@ void TXshZeraryFxColumn::saveData(TOStream &os) { } os.closeChild(); } + // cell marks + saveCellMarks(os); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/menubarcommand.cpp b/toonz/sources/toonzqt/menubarcommand.cpp index 16d51c9..74d817f 100644 --- a/toonz/sources/toonzqt/menubarcommand.cpp +++ b/toonz/sources/toonzqt/menubarcommand.cpp @@ -125,9 +125,10 @@ void CommandManager::define(CommandId id, CommandType type, node->m_qaction = qaction; node->m_qaction->setEnabled( (node->m_enabled && - (node->m_handler || node->m_qaction->actionGroup() != 0)) || + (node->m_handler || node->m_qaction->actionGroup() != 0)) || node->m_type == MiscCommandType || - node->m_type == ToolModifierCommandType); + node->m_type == ToolModifierCommandType || + node->m_type == CellMarkCommandType); m_qactionTable[qaction] = node; qaction->setShortcutContext(Qt::ApplicationShortcut); @@ -357,7 +358,7 @@ QAction *CommandManager::createAction(CommandId id, QObject *parent, if (!refAction) return 0; QString text = refAction->text(); if (node->m_onText != "" && node->m_offText != "") - text = state ? node->m_onText : node->m_offText; + text = state ? node->m_onText : node->m_offText; QAction *action = new QAction(text, parent); action->setShortcut(refAction->shortcut()); return action; @@ -519,24 +520,24 @@ void DVMenuAction::setActions(QList actions) { namespace { QString changeStringNumber(QString str, int index) { - QString newStr = str; - int n = 3; + QString newStr = str; + int n = 3; if (index >= 10) n = 4; QString number; newStr.replace(0, n, number.number(index + 1) + QString(". ")); return newStr; } -} +} // namespace //----------------------------------------------------------------------------- void DVMenuAction::onTriggered(QAction *action) { - QVariant data = action->data(); + QVariant data = action->data(); if (data.isValid()) m_triggeredActionIndex = data.toInt(); CommandManager::instance()->execute(action, menuAction()); int oldIndex = m_triggeredActionIndex; if (m_triggeredActionIndex != -1) m_triggeredActionIndex = -1; - QString str = data.toString(); + QString str = data.toString(); QAction *tableAction = CommandManager::instance()->getAction(str.toStdString().c_str()); if (tableAction || oldIndex == 0) return; diff --git a/toonz/sources/toonzqt/stageschematicnode.cpp b/toonz/sources/toonzqt/stageschematicnode.cpp index c93b595..65a54d4 100644 --- a/toonz/sources/toonzqt/stageschematicnode.cpp +++ b/toonz/sources/toonzqt/stageschematicnode.cpp @@ -1014,8 +1014,8 @@ SplineAimChanger::~SplineAimChanger() {} void SplineAimChanger::mouseMoveEvent(QGraphicsSceneMouseEvent *me) { if (m_buttonState == Qt::LeftButton) { - bool increase = false; - int delta = me->screenPos().y() - me->lastScreenPos().y(); + bool increase = false; + int delta = me->screenPos().y() - me->lastScreenPos().y(); if (delta < 0) increase = true; m_delta += abs(delta); if (m_delta > 15) { @@ -2222,8 +2222,8 @@ StageSchematicGroupNode::StageSchematicGroupNode( , m_groupedObj(groupedObj) { SchematicViewer *viewer = scene->getSchematicViewer(); int i; - for (i = 0; i < m_groupedObj.size(); i++) m_groupedObj[i]->addRef(); - bool ret = true; + for (i = 0; i < m_groupedObj.size(); i++) m_groupedObj[i]->addRef(); + bool ret = true; std::wstring name = m_stageObject->getGroupName(false); m_name = QString::fromStdWString(name);