#include "xdtsio.h"
#include "tsystem.h"
#include "toonz/toonzscene.h"
#include "toonz/tproject.h"
#include "toonz/levelset.h"
#include "toonz/txsheet.h"
#include "toonz/txshcell.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshchildlevel.h"
#include "toonz/txsheethandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/preferences.h"
#include "toonz/sceneproperties.h"
#include "toonz/tstageobject.h"
#include "toutputproperties.h"
#include "toonzqt/menubarcommand.h"
#include "toonzqt/gutil.h"
#include "tapp.h"
#include "menubarcommandids.h"
#include "xdtsimportpopup.h"
#include "filebrowserpopup.h"
#include <iostream>
#include <QJsonObject>
#include <QJsonArray>
#include <QRegExp>
#include <QFile>
#include <QJsonDocument>
#include <QApplication>
#include <QDesktopServices>
#include <QUrl>
#include <QComboBox>
#include <QLabel>
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}");
// TODO: We could check if the keys are valid
// before attempting to read them with QJsonObject::contains(),
// but we assume that they are.
m_cut = json["cut"].toString();
if (!rx.exactMatch(m_cut)) // TODO: should handle an error
std::cout << "The XdtsHeader value \"cut\" does not match the pattern."
<< std::endl;
m_scene = json["scene"].toString();
if (!rx.exactMatch(m_scene))
std::cout << "The XdtsHeader value \"scene\" does not match the pattern."
<< std::endl;
}
void XdtsHeader::write(QJsonObject &json) const {
json["cut"] = m_cut;
json["scene"] = m_scene;
}
//-----------------------------------------------------------------------------
TFrameId XdtsFrameDataItem::str2Fid(const QString &str) const {
if (str.isEmpty()) return TFrameId::EMPTY_FRAME;
bool ok;
int frame = str.toInt(&ok);
if (ok) return TFrameId(frame);
QString regExpStr = QString("^%1$").arg(TFilePath::fidRegExpStr());
QRegExp rx(regExpStr);
int pos = rx.indexIn(str);
if (pos < 0) return TFrameId();
if (rx.cap(2).isEmpty())
return TFrameId(rx.cap(1).toInt());
else
return TFrameId(rx.cap(1).toInt(), rx.cap(2));
}
QString XdtsFrameDataItem::fid2Str(const TFrameId &fid) const {
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();
}
void XdtsFrameDataItem::read(const QJsonObject &json) {
m_id = DataId(qRound(json["id"].toDouble()));
QJsonArray valuesArray = json["values"].toArray();
for (int vIndex = 0; vIndex < valuesArray.size(); ++vIndex) {
m_values.append(valuesArray[vIndex].toString());
}
}
void XdtsFrameDataItem::write(QJsonObject &json) const {
json["id"] = int(m_id);
QJsonArray valuesArray;
foreach (const QString &value, m_values) {
valuesArray.append(value);
}
json["values"] = valuesArray;
}
TFrameId XdtsFrameDataItem::getFrameId() const {
// int getCellNumber() const {
if (m_values.isEmpty())
return TFrameId(-1); // EMPTY
// if (m_values.isEmpty()) return 0;
QString val = m_values.at(0);
if (val == "SYMBOL_NULL_CELL")
return TFrameId(-1); // EMPTY
// ignore sheet symbols for now
else if (val == "SYMBOL_HYPHEN")
return TFrameId(-2); // IGNORE
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));
}
//-----------------------------------------------------------------------------
void XdtsTrackFrameItem::read(const QJsonObject &json) {
QJsonArray dataArray = json["data"].toArray();
for (int dataIndex = 0; dataIndex < dataArray.size(); ++dataIndex) {
QJsonObject dataObject = dataArray[dataIndex].toObject();
XdtsFrameDataItem data;
data.read(dataObject);
m_data.append(data);
}
m_frame = json["frame"].toInt();
}
void XdtsTrackFrameItem::write(QJsonObject &json) const {
QJsonArray dataArray;
foreach (const XdtsFrameDataItem &data, m_data) {
QJsonObject dataObject;
data.write(dataObject);
dataArray.append(dataObject);
}
json["data"] = dataArray;
json["frame"] = m_frame;
}
//-----------------------------------------------------------------------------
QPair<int, TFrameId> XdtsTrackFrameItem::frameFid() const {
return QPair<int, TFrameId>(m_frame, m_data[0].getFrameId());
}
//-----------------------------------------------------------------------------
void XdtsFieldTrackItem::read(const QJsonObject &json) {
QJsonArray frameArray = json["frames"].toArray();
for (int frameIndex = 0; frameIndex < frameArray.size(); ++frameIndex) {
QJsonObject frameObject = frameArray[frameIndex].toObject();
XdtsTrackFrameItem frame;
frame.read(frameObject);
m_frames.append(frame);
}
m_trackNo = json["trackNo"].toInt();
}
void XdtsFieldTrackItem::write(QJsonObject &json) const {
QJsonArray frameArray;
foreach (const XdtsTrackFrameItem &frame, m_frames) {
QJsonObject frameObject;
frame.write(frameObject);
frameArray.append(frameObject);
}
json["frames"] = frameArray;
json["trackNo"] = m_trackNo;
}
//-----------------------------------------------------------------------------
static bool frameLessThan(const QPair<int, TFrameId> &v1,
const QPair<int, TFrameId> &v2) {
return v1.first < v2.first;
}
QVector<TFrameId> XdtsFieldTrackItem::getCellFrameIdTrack(
QList<int> &tick1, QList<int> &tick2) const {
QList<QPair<int, TFrameId>> frameFids;
for (const XdtsTrackFrameItem &frame : m_frames)
frameFids.append(frame.frameFid());
std::sort(frameFids.begin(), frameFids.end(), frameLessThan);
QVector<TFrameId> cells;
int currentFrame = 0;
TFrameId initialNumber = TFrameId();
for (QPair<int, TFrameId> &frameFid : frameFids) {
while (currentFrame < frameFid.first) {
cells.append((cells.isEmpty()) ? initialNumber : cells.last());
currentFrame++;
}
// CSP may export negative frame data (although it is not allowed in XDTS
// format specification) so handle such case.
if (frameFid.first < 0) {
initialNumber = frameFid.second;
continue;
}
// ignore sheet symbols for now
TFrameId cellFid = frameFid.second;
if (cellFid.getNumber() == -2) // IGNORE case
cells.append((cells.isEmpty()) ? TFrameId(-1) : cells.last());
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++;
}
return cells;
}
QString XdtsFieldTrackItem::build(TXshCellColumn *column) {
// register the firstly-found level
TXshLevel *level = nullptr;
TXshCell prevCell;
int r0, r1;
column->getRange(r0, r1);
if (r0 > 0) addFrame(0, TFrameId(-1));
for (int row = r0; row <= r1; row++) {
TXshCell cell = column->getCell(row);
// try to register the level. simple levels and sub xsheet levels are
// exported
if (!level) level = cell.getSimpleLevel();
if (!level) level = cell.getChildLevel();
// if the level does not match with the registered one,
// handle as the empty cell
if (!level || cell.m_level != level) cell = TXshCell();
// continue if the cell is continuous
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
addFrame(row, cell.getFrameId());
prevCell = cell;
}
addFrame(r1 + 1, TFrameId(-1));
if (level)
return QString::fromStdWString(level->getName());
else {
m_frames.clear();
return QString();
}
}
//-----------------------------------------------------------------------------
void XdtsTimeTableFieldItem::read(const QJsonObject &json) {
m_fieldId = FieldId(qRound(json["fieldId"].toDouble()));
QJsonArray trackArray = json["tracks"].toArray();
for (int trackIndex = 0; trackIndex < trackArray.size(); ++trackIndex) {
QJsonObject trackObject = trackArray[trackIndex].toObject();
XdtsFieldTrackItem track;
track.read(trackObject);
m_tracks.append(track);
}
}
void XdtsTimeTableFieldItem::write(QJsonObject &json) const {
json["fieldId"] = int(m_fieldId);
QJsonArray trackArray;
foreach (const XdtsFieldTrackItem &track, m_tracks) {
QJsonObject trackObject;
track.write(trackObject);
trackArray.append(trackObject);
}
json["tracks"] = trackArray;
}
QList<int> XdtsTimeTableFieldItem::getOccupiedColumns() const {
QList<int> ret;
for (const XdtsFieldTrackItem &track : m_tracks) {
if (!track.isEmpty()) ret.append(track.getTrackNo());
}
return ret;
}
QVector<TFrameId> XdtsTimeTableFieldItem::getColumnTrack(
int col, QList<int> &tick1, QList<int> &tick2) const {
for (const XdtsFieldTrackItem &track : m_tracks) {
if (track.getTrackNo() != col) continue;
return track.getCellFrameIdTrack(tick1, tick2);
}
return QVector<TFrameId>();
}
void XdtsTimeTableFieldItem::build(TXsheet *xsheet, QStringList &columnLabels) {
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) {
continue;
}
// skip inactive column
if (!_exportAllColumn && !column->isPreviewVisible()) {
continue;
}
XdtsFieldTrackItem track(exportCol);
columnLabels.append(track.build(column));
if (!track.isEmpty()) m_tracks.append(track);
exportCol++;
}
}
//-----------------------------------------------------------------------------
void XdtsTimeTableHeaderItem::read(const QJsonObject &json) {
m_fieldId = FieldId(qRound(json["fieldId"].toDouble()));
QJsonArray namesArray = json["names"].toArray();
for (int nIndex = 0; nIndex < namesArray.size(); ++nIndex) {
m_names.append(namesArray[nIndex].toString());
}
}
void XdtsTimeTableHeaderItem::write(QJsonObject &json) const {
json["fieldId"] = int(m_fieldId);
QJsonArray namesArray;
foreach (const QString name, m_names) {
namesArray.append(name);
}
json["names"] = namesArray;
}
void XdtsTimeTableHeaderItem::build(QStringList &columnLabels) {
m_names.append(columnLabels);
}
//-----------------------------------------------------------------------------
void XdtsTimeTableItem::read(const QJsonObject &json) {
if (json.contains("fields")) {
QJsonArray fieldArray = json["fields"].toArray();
for (int fieldIndex = 0; fieldIndex < fieldArray.size(); ++fieldIndex) {
QJsonObject fieldObject = fieldArray[fieldIndex].toObject();
XdtsTimeTableFieldItem field;
field.read(fieldObject);
m_fields.append(field);
if (field.isCellField()) m_cellFieldIndex = fieldIndex;
}
}
m_duration = json["duration"].toInt();
m_name = json["name"].toString();
QJsonArray headerArray = json["timeTableHeaders"].toArray();
for (int headerIndex = 0; headerIndex < headerArray.size(); ++headerIndex) {
QJsonObject headerObject = headerArray[headerIndex].toObject();
XdtsTimeTableHeaderItem header;
header.read(headerObject);
m_timeTableHeaders.append(header);
if (header.isCellField()) m_cellHeaderIndex = headerIndex;
}
}
void XdtsTimeTableItem::write(QJsonObject &json) const {
if (!m_fields.isEmpty()) {
QJsonArray fieldArray;
foreach (const XdtsTimeTableFieldItem &field, m_fields) {
QJsonObject fieldObject;
field.write(fieldObject);
fieldArray.append(fieldObject);
}
json["fields"] = fieldArray;
}
json["duration"] = m_duration;
json["name"] = m_name;
QJsonArray headerArray;
foreach (const XdtsTimeTableHeaderItem header, m_timeTableHeaders) {
QJsonObject headerObject;
header.write(headerObject);
headerArray.append(headerObject);
}
json["timeTableHeaders"] = headerArray;
}
QStringList XdtsTimeTableItem::getLevelNames() const {
// obtain column labels from the header
assert(m_cellHeaderIndex >= 0);
QStringList labels = m_timeTableHeaders.at(m_cellHeaderIndex).getLayerNames();
// obtain occupied column numbers in the field
assert(m_cellFieldIndex >= 0);
QList<int> occupiedColumns =
m_fields.at(m_cellFieldIndex).getOccupiedColumns();
// return the names of occupied columns
QStringList ret;
for (const int columnId : occupiedColumns) ret.append(labels.at(columnId));
return ret;
}
void XdtsTimeTableItem::build(TXsheet *xsheet, QString name, int duration) {
m_duration = duration;
m_name = name;
QStringList columnLabels;
XdtsTimeTableFieldItem field;
field.build(xsheet, columnLabels);
m_fields.append(field);
while (!columnLabels.isEmpty() && columnLabels.last().isEmpty())
columnLabels.removeLast();
if (columnLabels.isEmpty()) {
m_fields.clear();
return;
}
XdtsTimeTableHeaderItem header;
header.build(columnLabels);
m_timeTableHeaders.append(header);
m_cellHeaderIndex = 0;
m_cellFieldIndex = 0;
}
//-----------------------------------------------------------------------------
void XdtsData::read(const QJsonObject &json) {
if (json.contains("header")) {
QJsonObject headerObject = json["header"].toObject();
m_header.read(headerObject);
}
QJsonArray tableArray = json["timeTables"].toArray();
for (int tableIndex = 0; tableIndex < tableArray.size(); ++tableIndex) {
QJsonObject tableObject = tableArray[tableIndex].toObject();
XdtsTimeTableItem table;
table.read(tableObject);
m_timeTables.append(table);
}
m_version = Version(qRound(json["version"].toDouble()));
}
void XdtsData::write(QJsonObject &json) const {
if (!m_header.isEmpty()) {
QJsonObject headerObject;
m_header.write(headerObject);
json["header"] = headerObject;
}
QJsonArray tableArray;
foreach (const XdtsTimeTableItem &table, m_timeTables) {
QJsonObject tableObject;
table.write(tableObject);
tableArray.append(tableObject);
}
json["timeTables"] = tableArray;
json["version"] = int(m_version);
}
QStringList XdtsData::getLevelNames() const {
// currently support only the first page of time tables
return m_timeTables.at(0).getLevelNames();
}
void XdtsData::build(TXsheet *xsheet, QString name, int duration) {
XdtsTimeTableItem timeTable;
timeTable.build(xsheet, name, duration);
if (timeTable.isEmpty()) return;
m_timeTables.append(timeTable);
}
//-----------------------------------------------------------------------------
bool XdtsIo::loadXdtsScene(ToonzScene *scene, const TFilePath &scenePath) {
QApplication::restoreOverrideCursor();
// read the Json file
QFile loadFile(scenePath.getQString());
if (!loadFile.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open save file.");
return false;
}
QByteArray dataArray = loadFile.readAll();
if (!dataArray.startsWith(identifierStr)) {
qWarning("The first line does not start with XDTS identifier string.");
return false;
}
// remove identifier
dataArray.remove(0, identifierStr.length());
QJsonDocument loadDoc(QJsonDocument::fromJson(dataArray));
XdtsData xdtsData;
xdtsData.read(loadDoc.object());
// obtain level names
QStringList levelNames = xdtsData.getLevelNames();
// in case multiple columns have the same name
levelNames.removeDuplicates();
scene->clear();
TProjectManager *pm = TProjectManager::instance();
TProjectP sceneProject = pm->loadSceneProject(scenePath);
if (!sceneProject) return false;
scene->setProject(sceneProject.getPointer());
std::string sceneFileName = scenePath.getName() + ".tnz";
scene->setScenePath(scenePath.getParentDir() + sceneFileName);
// set the current scene here in order to use $scenefolder node properly
// in the file browser which opens from XDTSImportPopup
TApp::instance()->getCurrentScene()->setScene(scene);
XDTSImportPopup popup(levelNames, scene, scenePath);
int ret = popup.exec();
if (ret == QDialog::Rejected) return false;
QMap<QString, TXshLevel *> levels;
try {
for (QString name : levelNames) {
QString levelPath = popup.getLevelPath(name);
TXshLevel *level = nullptr;
if (!levelPath.isEmpty())
level = scene->loadLevel(scene->decodeFilePath(TFilePath(levelPath)),
nullptr, name.toStdWString());
if (!level) {
int levelType = Preferences::instance()->getDefLevelType();
level = scene->createNewLevel(levelType, name.toStdWString());
}
levels.insert(name, level);
}
} catch (...) {
return false;
}
int tick1Id, tick2Id;
popup.getMarkerIds(tick1Id, tick2Id);
TFrameId tmplFId = scene->getProperties()->formatTemplateFIdForInput();
TXsheet *xsh = scene->getXsheet();
XdtsTimeTableFieldItem cellField = xdtsData.timeTable().getCellField();
XdtsTimeTableHeaderItem cellHeader = xdtsData.timeTable().getCellHeader();
int duration = xdtsData.timeTable().getDuration();
QStringList layerNames = cellHeader.getLayerNames();
QList<int> columns = cellField.getOccupiedColumns();
for (int column : columns) {
QString levelName = layerNames.at(column);
TXshLevel *level = levels.value(levelName);
TXshSimpleLevel *sl = level->getSimpleLevel();
QList<int> tick1, tick2;
QVector<TFrameId> track = cellField.getColumnTrack(column, tick1, tick2);
int row = 0;
std::vector<TFrameId>::iterator it;
for (TFrameId fid : track) {
if (fid.getNumber() == -1) // EMPTY cell case
row++;
else {
// modify frameId to be with the same frame format as existing frames
if (sl) sl->formatFId(fid, tmplFId);
xsh->setCell(row++, column, TXshCell(level, fid));
}
}
// if the last cell is not "SYMBOL_NULL_CELL", continue the cell
// to the end of the sheet
TFrameId lastFid = track.last();
if (lastFid.getNumber() != -1) {
// modify frameId to be with the same frame format as existing frames
if (sl) sl->formatFId(lastFid, tmplFId);
for (; row < duration; row++)
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());
}
xsh->updateFrameCount();
// if the duration is shorter than frame count, then set it both in
// preview range and output range.
if (duration < xsh->getFrameCount()) {
scene->getProperties()->getPreviewProperties()->setRange(0, duration - 1,
1);
scene->getProperties()->getOutputProperties()->setRange(0, duration - 1, 1);
}
// emit signal here for updating the frame slider range of flip console
TApp::instance()->getCurrentScene()->notifySceneChanged();
return true;
}
class ExportXDTSCommand final : public MenuItemHandler {
public:
ExportXDTSCommand() : MenuItemHandler(MI_ExportXDTS) {}
void execute() override;
} exportXDTSCommand;
void ExportXDTSCommand::execute() {
ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
TFilePath fp = scene->getScenePath().withType("xdts");
// if the current xsheet is top xsheet in the scene and the output
// frame range is specified, set the "to" frame value as duration
int duration;
TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
int from, to, step;
if (scene->getTopXsheet() == xsheet && oprop->getRange(from, to, step))
duration = to + 1;
else
duration = xsheet->getFrameCount();
{
_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<TSceneProperties::CellMark> 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(QObject::tr("All columns"), true);
targetColumnCombo->addItem(QObject::tr("Only active columns"), false);
targetColumnCombo->setCurrentIndex(targetColumnCombo->findData(true));
QGridLayout *customLay = new QGridLayout();
customLay->setMargin(0);
customLay->setSpacing(10);
{
customLay->addWidget(
new QLabel(QObject::tr("Cell Mark for Inbetween Symbol 1 (O)")), 0, 0,
Qt::AlignRight | Qt::AlignVCenter);
customLay->addWidget(tick1Id, 0, 1);
customLay->addWidget(
new QLabel(QObject::tr("Cell Mark for Inbetween Symbol 2 (*)")), 1, 0,
Qt::AlignRight | Qt::AlignVCenter);
customLay->addWidget(tick2Id, 1, 1);
customLay->addWidget(new QLabel(QObject::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)"), custonWidget);
savePopup->addFilterType("xdts");
} else {
refreshCellMarkComboItems(tick1Id);
refreshCellMarkComboItems(tick2Id);
}
if (!scene->isUntitled())
savePopup->setFolder(fp.getParentDir());
else
savePopup->setFolder(
TProjectManager::instance()->getCurrentProject()->getScenesPath());
savePopup->setFilename(fp.withoutParentDir());
fp = savePopup->getPath();
if (fp.isEmpty()) return;
QFile saveFile(fp.getQString());
if (!saveFile.open(QIODevice::WriteOnly)) {
qWarning("Couldn't open save file.");
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);
QByteArray jsonByteArrayData = saveDoc.toJson();
jsonByteArrayData.prepend(identifierStr + '\n');
saveFile.write(jsonByteArrayData);
QString str = QObject::tr("The file %1 has been exported successfully.")
.arg(QString::fromStdString(fp.getLevelName()));
std::vector<QString> buttons = {QObject::tr("OK"),
QObject::tr("Open containing folder")};
int ret = DVGui::MsgBox(DVGui::INFORMATION, str, buttons);
if (ret == 2) {
TFilePath folderPath = fp.getParentDir();
if (TSystem::isUNC(folderPath))
QDesktopServices::openUrl(QUrl(folderPath.getQString()));
else
QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath.getQString()));
}
}