diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index c73aa8a..b468f19 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -82,6 +82,7 @@ set(MOC_HEADERS ruler.h savepresetpopup.h scanpopup.h + scenebrowser.h scenesettingspopup.h sceneviewer.h sceneviewercontextmenu.h @@ -285,6 +286,8 @@ set(SOURCES runscriptcommand.cpp savepresetpopup.cpp scanpopup.cpp + scenebrowser.cpp + scenebrowserversioncontrol.cpp sceneviewercontextmenu.cpp scenesettingspopup.cpp scriptconsolepanel.cpp diff --git a/toonz/sources/toonz/dvitemview.h b/toonz/sources/toonz/dvitemview.h index 24be943..864afc5 100644 --- a/toonz/sources/toonz/dvitemview.h +++ b/toonz/sources/toonz/dvitemview.h @@ -255,6 +255,9 @@ public: } QColor getSelectedItemBackground() const { return m_selectedItemBackground; } + //exposed view parameters + void setIconSize(QSize size) { m_iconSize = size; } + private: ViewType to_enum(int n) { switch (n) { diff --git a/toonz/sources/toonz/filebrowser.cpp b/toonz/sources/toonz/filebrowser.cpp index d1a0ef0..02a19e0 100644 --- a/toonz/sources/toonz/filebrowser.cpp +++ b/toonz/sources/toonz/filebrowser.cpp @@ -2351,7 +2351,7 @@ calculateTask: //----------------------------------------------------------------------------- -inline void FrameCountReader::stopReading() { m_executor.cancelAll(); } +void FrameCountReader::stopReading() { m_executor.cancelAll(); } //============================================================================= // FrameCountTask methods diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index e522d3f..354ad65 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -2113,6 +2113,8 @@ void MainWindow::defineActions() { createMenuWindowsAction(MI_OpenFileBrowser, QT_TR_NOOP("&File Browser"), "", "filebrowser"); + createMenuWindowsAction(MI_OpenPreproductionBoard, QT_TR_NOOP("&Preproduction Board"), "", + "scenebrowser"); createMenuWindowsAction(MI_OpenFileViewer, QT_TR_NOOP("&Flipbook"), "", "flipbook"); createMenuWindowsAction(MI_OpenFunctionEditor, QT_TR_NOOP("&Function Editor"), diff --git a/toonz/sources/toonz/menubar.cpp b/toonz/sources/toonz/menubar.cpp index 2bbf9db..e39e898 100644 --- a/toonz/sources/toonz/menubar.cpp +++ b/toonz/sources/toonz/menubar.cpp @@ -420,6 +420,7 @@ QMenuBar *StackedMenuBar::createCleanupMenuBar() { //----Windows Menu QMenu *windowsMenu = addMenu(tr("Windows"), cleanupMenuBar); addMenuItem(windowsMenu, MI_OpenFileBrowser); + addMenuItem(windowsMenu, MI_OpenPreproductionBoard); addMenuItem(windowsMenu, MI_OpenStyleControl); addMenuItem(windowsMenu, MI_OpenComboViewer); addMenuItem(windowsMenu, MI_OpenXshView); @@ -586,6 +587,7 @@ QMenuBar *StackedMenuBar::createPltEditMenuBar() { //---Windows Menu QMenu *windowsMenu = addMenu(tr("Windows"), pltEditMenuBar); addMenuItem(windowsMenu, MI_OpenFileBrowser); + addMenuItem(windowsMenu, MI_OpenPreproductionBoard); addMenuItem(windowsMenu, MI_OpenFileViewer); addMenuItem(windowsMenu, MI_OpenFilmStrip); addMenuItem(windowsMenu, MI_OpenPalette); @@ -770,6 +772,7 @@ QMenuBar *StackedMenuBar::createInknPaintMenuBar() { addMenuItem(windowsMenu, MI_OpenTimelineView); addMenuItem(windowsMenu, MI_OpenColorModel); addMenuItem(windowsMenu, MI_OpenFileBrowser); + addMenuItem(windowsMenu, MI_OpenPreproductionBoard); addMenuItem(windowsMenu, MI_OpenFilmStrip); addMenuItem(windowsMenu, MI_OpenToolbar); addMenuItem(windowsMenu, MI_OpenToolOptionBar); @@ -945,6 +948,7 @@ QMenuBar *StackedMenuBar::createXsheetMenuBar() { addMenuItem(windowsMenu, MI_OpenSchematic); addMenuItem(windowsMenu, MI_OpenComboViewer); addMenuItem(windowsMenu, MI_OpenFileBrowser); + addMenuItem(windowsMenu, MI_OpenPreproductionBoard); addMenuItem(windowsMenu, MI_OpenFunctionEditor); addMenuItem(windowsMenu, MI_OpenFileViewer); addMenuItem(windowsMenu, MI_OpenFilmStrip); @@ -1010,6 +1014,7 @@ QMenuBar *StackedMenuBar::createBatchesMenuBar() { //----Windows Menu QMenu *windowsMenu = addMenu(tr("Windows"), batchesMenuBar); addMenuItem(windowsMenu, MI_OpenFileBrowser); + addMenuItem(windowsMenu, MI_OpenPreproductionBoard); addMenuItem(windowsMenu, MI_OpenBatchServers); addMenuItem(windowsMenu, MI_OpenTasks); @@ -1415,6 +1420,7 @@ QMenuBar *StackedMenuBar::createFullMenuBar() { addMenuItem(windowsMenu, MI_OpenFilmStrip); windowsMenu->addSeparator(); addMenuItem(windowsMenu, MI_OpenFileBrowser); + addMenuItem(windowsMenu, MI_OpenPreproductionBoard); addMenuItem(windowsMenu, MI_OpenFileBrowser2); windowsMenu->addSeparator(); addMenuItem(windowsMenu, MI_OpenCleanupSettings); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 11ee0b3..694a6ed 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -85,6 +85,7 @@ // #define MI_OpenCurrentScene "MI_OpenCurrentScene" #define MI_OpenFileBrowser "MI_OpenFileBrowser" +#define MI_OpenPreproductionBoard "MI_OpenPreproductionBoard" #define MI_OpenFileViewer "MI_OpenFileViewer" #define MI_OpenFilmStrip "MI_OpenFilmStrip" #define MI_OpenPalette "MI_OpenPalette" diff --git a/toonz/sources/toonz/scenebrowser.cpp b/toonz/sources/toonz/scenebrowser.cpp new file mode 100644 index 0000000..bff45b0 --- /dev/null +++ b/toonz/sources/toonz/scenebrowser.cpp @@ -0,0 +1,2206 @@ + + +#include "scenebrowser.h" + +// Tnz6 includes +#include "dvdirtreeview.h" +#include "filebrowser.h" +#include "filebrowsermodel.h" +#include "fileselection.h" +#include "filmstripselection.h" +#include "castselection.h" +#include "menubarcommandids.h" +#include "floatingpanelcommand.h" +#include "iocommand.h" +#include "history.h" +#include "tapp.h" + +// TnzQt includes +#include "toonzqt/dvdialog.h" +#include "toonzqt/icongenerator.h" +#include "toonzqt/menubarcommand.h" +#include "toonzqt/gutil.h" +#include "toonzqt/trepetitionguard.h" + +// TnzLib includes +#include "toonz/tscenehandle.h" +#include "toonz/toonzscene.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/txshsoundlevel.h" +#include "toonz/tproject.h" +#include "toonz/txshlevelhandle.h" +#include "toonz/namebuilder.h" +#include "toonz/toonzimageutils.h" +#include "toonz/preferences.h" + +// TnzBase includes +#include "tenv.h" + +// TnzCore includes +#include "tsystem.h" +#include "tconvert.h" +#include "tfiletype.h" +#include "tlevel_io.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// tcg includes +#include "tcg/boost/range_utility.h" +#include "tcg/boost/permuted_range.h" + +// boost includes +#include +#include +#include +#include + +namespace ba = boost::adaptors; + +using namespace DVGui; + + +//============================================================================= +// Local namespace +//============================================================================= + +namespace { +std::set activePreproductionBoards; +//std::map frameCountMap; +//QMutex frameCountMapMutex; +QMutex levelFileMutex; + +} // namespace + +//============================================================================= +// +// SceneBrowserButtonBar +// +//----------------------------------------------------------------------------- + +SceneBrowserButtonBar::SceneBrowserButtonBar(DvItemViewer *itemViewer, + QWidget *parent) + : QToolBar(parent) { + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setIconSize(QSize(17, 17)); + setObjectName("buttonBar"); + // buttonBar->setIconSize(QSize(10,10)); + + QIcon newFolderIcon = createQIcon("newfolder"); + QAction *newScene = new QAction(newFolderIcon, tr("Create new scene"), this); + newScene->setIconText(tr("Create scene")); + addAction(newScene); + //addSeparator(); + + connect(newScene, SIGNAL(triggered()), SIGNAL(newScene())); +} + +//============================================================================= +// SceneBrowser +//----------------------------------------------------------------------------- + +#if QT_VERSION >= 0x050500 +SceneBrowser::SceneBrowser(QWidget *parent, Qt::WindowFlags flags, + bool noContextMenu, bool multiSelectionEnabled) +#else +SceneBrowser::SceneBrowser(QWidget *parent, Qt::WFlags flags, bool noContextMenu, + bool multiSelectionEnabled) +#endif + : QFrame(parent), m_folderName(0), m_itemViewer(0) { + // style sheet + setObjectName("SceneBrowser"); + setFrameStyle(QFrame::StyledPanel); + + //m_mainSplitter = new QSplitter(this); + m_folderTreeView = new DvDirTreeView(this); + QFrame *box = new QFrame(this); + QLabel *folderLabel = new QLabel(tr("Folder: "), this); + m_folderName = new QLineEdit(); + m_itemViewer = new DvItemViewer(box, noContextMenu, multiSelectionEnabled, + DvItemViewer::Browser); + DvItemViewerTitleBar *titleBar = new DvItemViewerTitleBar(m_itemViewer, box); + SceneBrowserButtonBar *buttonBar = + new SceneBrowserButtonBar(m_itemViewer, box); + DvItemViewerPanel *viewerPanel = m_itemViewer->getPanel(); + viewerPanel->setThumbnailsView(); + viewerPanel->setIconSize(QSize(192,108)); //default 80, 60 + viewerPanel->addColumn(DvItemListModel::FileType, 50); + viewerPanel->addColumn(DvItemListModel::FrameCount, 50); + viewerPanel->addColumn(DvItemListModel::FileSize, 50); + viewerPanel->addColumn(DvItemListModel::CreationDate, 130); + viewerPanel->addColumn(DvItemListModel::ModifiedDate, 130); + if (Preferences::instance()->isSVNEnabled()) + viewerPanel->addColumn(DvItemListModel::VersionControlStatus, 120); + + viewerPanel->setSelection(new FileSelection()); + DVItemViewPlayDelegate *itemViewPlayDelegate = + new DVItemViewPlayDelegate(viewerPanel); + viewerPanel->setItemViewPlayDelegate(itemViewPlayDelegate); + + //m_mainSplitter->setObjectName("SceneBrowserSplitter"); + m_folderTreeView->hide(); + m_folderTreeView->setObjectName("DirTreeView"); + box->setObjectName("castFrame"); + box->setFrameStyle(QFrame::StyledPanel); + + m_itemViewer->setModel(this); + + // layout + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->setMargin(3); + mainLayout->setSpacing(2); + { + mainLayout->addWidget(buttonBar); + + QHBoxLayout *folderLay = new QHBoxLayout(); + folderLay->setMargin(0); + folderLay->setSpacing(0); + { + folderLay->addWidget(folderLabel, 0); + folderLay->addWidget(m_folderName, 1); + } + mainLayout->addLayout(folderLay, 0); + + //m_mainSplitter->addWidget(m_folderTreeView); + QVBoxLayout *boxLayout = new QVBoxLayout(box); + boxLayout->setMargin(0); + boxLayout->setSpacing(0); + { + boxLayout->addWidget(titleBar, 0); + boxLayout->addWidget(m_itemViewer, 1); + } + //m_mainSplitter->addWidget(box); + mainLayout->addWidget(box, 1); + } + setLayout(mainLayout); + + //m_mainSplitter->setSizes(QList() << 270 << 500); + + // signal-slot connections + bool ret = connect(m_folderTreeView, SIGNAL(currentNodeChanged()), + itemViewPlayDelegate, SLOT(resetPlayWidget())); + // if the current forder is changed in the folder tree, then update in the + // item view + ret = ret && connect(m_folderTreeView, SIGNAL(currentNodeChanged()), this, + SLOT(onTreeFolderChanged())); + + ret = ret && connect(m_itemViewer, SIGNAL(clickedItem(int)), this, + SLOT(onClickedItem(int))); + ret = ret && connect(m_itemViewer, SIGNAL(doubleClickedItem(int)), this, + SLOT(onDoubleClickedItem(int))); + ret = + ret && connect(m_itemViewer, SIGNAL(selectedItems(const std::set &)), + this, SLOT(onSelectedItems(const std::set &))); + ret = ret && connect(buttonBar, SIGNAL(newScene()), this, SLOT(newScene())); + + ret = ret && connect(&m_frameCountReader, SIGNAL(calculatedFrameCount()), + m_itemViewer->getPanel(), SLOT(update())); + + QAction *refresh = CommandManager::instance()->getAction(MI_RefreshTree); + ret = ret && connect(refresh, SIGNAL(triggered()), this, SLOT(refresh())); + addAction(refresh); + + // Version Control instance connection + if (Preferences::instance()->isSVNEnabled()) + ret = + ret && connect(VersionControl::instance(), + SIGNAL(commandDone(const QStringList &)), this, + SLOT(onVersionControlCommandDone(const QStringList &))); + + // if the folderName is edited, move the current folder accordingly + ret = ret && connect(m_folderName, SIGNAL(editingFinished()), this, + SLOT(onFolderEdited())); + + // folder history + ret = ret && connect(m_folderTreeView, SIGNAL(currentNodeChanged()), this, + SLOT(storeFolderHistory())); + + // check out the update of the current folder. + // Use MyFileSystemWatcher which is shared by all browsers. + // Adding and removing paths to the watcher is done in DvDirTreeView. + ret = ret && connect(MyFileSystemWatcher::instance(), + SIGNAL(directoryChanged(const QString &)), this, + SLOT(onFileSystemChanged(const QString &))); + + // when the scene switched, update the path of the scene location node + TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene(); + //ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this, + // SLOT(onSceneSwitched())); + ret = ret && connect(sceneHandle, SIGNAL(nameSceneChanged()), this, + SLOT(onSceneSwitched())); + + //onSceneSwitched(); + + // store the first item("Root") in the history + m_indexHistoryList.append(m_folderTreeView->currentIndex()); + m_currentPosition = 0; + + refreshHistoryButtons(); + + assert(ret); +} + +//----------------------------------------------------------------------------- + +SceneBrowser::~SceneBrowser() {} + +//----------------------------------------------------------------------------- +/*! when the m_folderName is edited, move the current folder accordingly + */ +void SceneBrowser::onFolderEdited() { + TFilePath inputPath(m_folderName->text().toStdWString()); + QModelIndex index = DvDirModel::instance()->getIndexByPath(inputPath); + + // If there is no node matched + if (!index.isValid()) { + QMessageBox::warning(this, tr("Open folder failed"), + tr("The input folder path was invalid.")); + return; + } + m_folderTreeView->collapseAll(); + + m_folderTreeView->setCurrentIndex(index); + + // expand the folder tree + QModelIndex tmpIndex = index; + while (tmpIndex.isValid()) { + m_folderTreeView->expand(tmpIndex); + tmpIndex = tmpIndex.parent(); + } + + m_folderTreeView->scrollTo(index); + m_folderTreeView->update(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::storeFolderHistory() { + QModelIndex currentModelIndex = m_folderTreeView->currentIndex(); + + if (!currentModelIndex.isValid()) return; + + if (m_indexHistoryList[m_currentPosition] == currentModelIndex) return; + + // If there is no next history item, then create it + if (m_currentPosition == m_indexHistoryList.size() - 1) { + m_indexHistoryList << currentModelIndex; + m_currentPosition++; + } + // If the next hitory item is the same as the current one, just move to it + else if (m_indexHistoryList[m_currentPosition + 1] == currentModelIndex) { + m_currentPosition++; + } + // If the next history item is different from the current one, then replace + // with the new one + else { + int size = m_indexHistoryList.size(); + // remove the old history items + for (int i = m_currentPosition + 1; i < size; i++) + m_indexHistoryList.removeLast(); + m_indexHistoryList << currentModelIndex; + m_currentPosition++; + } + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refreshHistoryButtons() { + emit historyChanged((m_currentPosition != 0), + (m_currentPosition != m_indexHistoryList.size() - 1)); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onBackButtonPushed() { + if (m_currentPosition == 0) return; + m_currentPosition--; + QModelIndex currentIndex = m_indexHistoryList[m_currentPosition]; + m_folderTreeView->setCurrentIndex(currentIndex); + m_folderTreeView->collapseAll(); + while (currentIndex.isValid()) { + currentIndex = currentIndex.parent(); + m_folderTreeView->expand(currentIndex); + } + m_folderTreeView->update(); + + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onFwdButtonPushed() { + if (m_currentPosition >= m_indexHistoryList.size() - 1) return; + m_currentPosition++; + QModelIndex currentIndex = m_indexHistoryList[m_currentPosition]; + m_folderTreeView->setCurrentIndex(currentIndex); + m_folderTreeView->collapseAll(); + while (currentIndex.isValid()) { + currentIndex = currentIndex.parent(); + m_folderTreeView->expand(currentIndex); + } + m_folderTreeView->update(); + + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- +/*! clear the history when the tree date is replaced + */ +void SceneBrowser::clearHistory() { + int size = m_indexHistoryList.size(); + // leave the last item + for (int i = 1; i < size; i++) m_indexHistoryList.removeLast(); + m_currentPosition = 0; + refreshHistoryButtons(); +} + +//----------------------------------------------------------------------------- +/*! update the current folder when changes detected from QFileSystemWatcher + */ +void SceneBrowser::onFileSystemChanged(const QString &folderPath) { + if (folderPath != m_folder.getQString()) return; + // changes may create/delete of folder, so update the DvDirModel + QModelIndex parentFolderIndex = m_folderTreeView->currentIndex(); + DvDirModel::instance()->refresh(parentFolderIndex); + + refreshCurrentFolderItems(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::sortByDataModel(DataType dataType, bool isDiscendent) { + struct locals { + static inline bool itemLess(int aIdx, int bIdx, SceneBrowser &fb, + DataType dataType) { + return (fb.compareData(dataType, aIdx, bIdx) > 0); + } + + static inline bool indexLess(int aIdx, int bIdx, + const std::vector &vec) { + return (vec[aIdx] < vec[bIdx]); + } + + static inline int complement(int val, int max) { + return (assert(0 <= val && val <= max), max - val); + } + }; // locals + + if (dataType != getCurrentOrderType()) { + // Build the permutation table + std::vector new2OldIdx( + boost::make_counting_iterator(0), + boost::make_counting_iterator(int(m_items.size()))); + + std::stable_sort( + new2OldIdx.begin(), new2OldIdx.end(), + boost::bind(locals::itemLess, _1, _2, boost::ref(*this), dataType)); + + // Use the renumbering table to permutate elements + std::vector( + boost::make_permutation_iterator(m_items.begin(), new2OldIdx.begin()), + boost::make_permutation_iterator(m_items.begin(), new2OldIdx.end())) + .swap(m_items); + + // Use the permutation table to update current file selection, if any + FileSelection *fs = + static_cast(m_itemViewer->getPanel()->getSelection()); + + if (!fs->isEmpty()) { + std::vector old2NewIdx( + boost::make_counting_iterator(0), + boost::make_counting_iterator(int(m_items.size()))); + + std::sort(old2NewIdx.begin(), old2NewIdx.end(), + boost::bind(locals::indexLess, _1, _2, boost::ref(new2OldIdx))); + + std::vector newSelectedIndices; + tcg::substitute( + newSelectedIndices, + tcg::permuted_range(old2NewIdx, fs->getSelectedIndices() | + ba::filtered(boost::bind( + std::less(), _1, + int(old2NewIdx.size()))))); + + fs->select(!newSelectedIndices.empty() ? &newSelectedIndices.front() : 0, + int(newSelectedIndices.size())); + } + + setIsDiscendentOrder(true); + setOrderType(dataType); + } + + // Reverse lists if necessary + if (isDiscendentOrder() != isDiscendent) { + std::reverse(m_items.begin(), m_items.end()); + + // Reverse file selection, if any + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + + if (!fs->isEmpty()) { + int iCount = int(m_items.size()), lastIdx = iCount - 1; + + std::vector newSelectedIndices; + tcg::substitute( + newSelectedIndices, + fs->getSelectedIndices() | + ba::filtered(boost::bind(std::less(), _1, iCount)) | + ba::transformed(boost::bind(locals::complement, _1, lastIdx))); + + fs->select(!newSelectedIndices.empty() ? &newSelectedIndices.front() : 0, + int(newSelectedIndices.size())); + } + + setIsDiscendentOrder(isDiscendent); + } + + m_itemViewer->getPanel()->update(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setFilterTypes(const QStringList &types) { m_filter = types; } + +//----------------------------------------------------------------------------- + +void SceneBrowser::addFilterType(const QString &type) { + if (!m_filter.contains(type)) m_filter.push_back(type); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::removeFilterType(const QString &type) { + m_filter.removeAll(type); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refreshCurrentFolderItems() { + m_items.clear(); + + // put the parent directory item + //TFilePath parentFp = m_folder.getParentDir(); + //if (parentFp != TFilePath("") && parentFp != m_folder) + // m_items.push_back(Item(parentFp, true, false)); + + // register the file items + if (m_folder != TFilePath()) { + TFilePathSet files; + TFilePathSet all_files; // for updating m_multiFileItemMap + + TFileStatus fpStatus(m_folder); + // if the item is link, then set the link target of it + if (fpStatus.isLink()) { + QFileInfo info(toQString(m_folder)); + setFolder(TFilePath(info.symLinkTarget().toStdWString())); + return; + } + if (fpStatus.doesExist() && fpStatus.isDirectory() && + fpStatus.isReadable()) { + try { + TSystem::readDirectory(files, all_files, m_folder); + } catch (...) { + } + } + TFilePathSet::iterator it; + for (it = files.begin(); it != files.end(); ++it) { + // skip the plt file (Palette file for TOONZ 4.6 and earlier) + if (it->getType() == "plt") continue; + + // filter the file + else if (m_filter.isEmpty()) { + if (it->getType() != "tnz" && it->getType() != "scr" && + it->getType() != "tnzbat" && it->getType() != "mpath" && + it->getType() != "curve" && it->getType() != "tpl" && + TFileType::getInfo(*it) == TFileType::UNKNOW_FILE) + continue; + } else if (m_filter.contains(QString::fromStdString(it->getType()))) + continue; + // store the filtered file paths + m_items.push_back(Item(*it)); + } + + // update the m_multiFileItemMap + m_multiFileItemMap.clear(); + + for (it = all_files.begin(); it != all_files.end(); it++) { + TFrameId tFrameId; + tFrameId = it->getFrame(); + TFilePath levelName(it->getLevelName()); + + if (levelName.isLevelName()) { + Item &levelItem = m_multiFileItemMap[levelName]; + + // TODO: + // とりあえず残すが、FileInfoの取得に時間がかかるようならオプション化も検討 + // 2015/12/28 shun_iwasawa + QFileInfo fileInfo(QString::fromStdWString(it->getWideString())); + // Update level infos + if (levelItem.m_creationDate.isNull() || + (fileInfo.created() < levelItem.m_creationDate)) + levelItem.m_creationDate = fileInfo.created(); + if (levelItem.m_modifiedDate.isNull() || + (fileInfo.lastModified() > levelItem.m_modifiedDate)) + levelItem.m_modifiedDate = fileInfo.lastModified(); + levelItem.m_fileSize += fileInfo.size(); + + // store frameId + levelItem.m_frameIds.push_back(tFrameId); + + levelItem.m_frameCount++; + } + } + } + + // Set Missing Version Control Items + int missingItemCount = 0; + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (node) { + QList list = node->getMissingFiles(); + missingItemCount = list.size(); + for (int i = 0; i < missingItemCount; i++) + m_items.push_back(Item(list.at(i))); + } + + // Refresh Data (fill Item field) + refreshData(); + + // If I added some missing items I need to sort items. + if (missingItemCount > 0) { + DataType currentDataType = getCurrentOrderType(); + int i; + for (i = 1; i < m_items.size(); i++) { + int index = i; + while (index > 0 && compareData(currentDataType, index - 1, index) > 0) { + std::swap(m_items[index - 1], m_items[index]); + index = index - 1; + } + } + } + // update the ordering rules + bool discendentOrder = isDiscendentOrder(); + DataType currentDataType = getCurrentOrderType(); + setOrderType(Name); + setIsDiscendentOrder(true); + sortByDataModel(currentDataType, discendentOrder); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setFolder(const TFilePath &fp, bool expandNode, + bool forceUpdate) { + if (fp == m_folder && !forceUpdate) return; + + // set the current folder path + m_folder = fp; + m_dayDateString = ""; + // set the folder name + if (fp == TFilePath()) + m_folderName->setText(""); + else + m_folderName->setText(toQString(fp)); + + refreshCurrentFolderItems(); + + if (!TFileStatus(fp).isLink()) + m_folderTreeView->setCurrentNode(fp, expandNode); +} + +//----------------------------------------------------------------------------- +/*! process when inputting the folder which is not regitered in the folder tree + (e.g. UNC path in Windows) + */ +void SceneBrowser::setUnregisteredFolder(const TFilePath &fp) { + if (fp != TFilePath()) { + TFileStatus fpStatus(fp); + // if the item is link, then set the link target of it + if (fpStatus.isLink()) { + QFileInfo info(toQString(fp)); + setFolder(TFilePath(info.symLinkTarget().toStdWString())); + return; + } + + // get both the folder & file list by readDirectory and + // readDirectory_Dir_ReadExe + TFilePathSet folders; + TFilePathSet files; + // for updating m_multiFileItemMap + TFilePathSet all_files; + + if (fpStatus.doesExist() && fpStatus.isDirectory() && + fpStatus.isReadable()) { + try { + TSystem::readDirectory(files, all_files, fp); + TSystem::readDirectory_Dir_ReadExe(folders, fp); + } catch (...) { + } + } + + TFilePathSet::iterator it; + + // register all folder items + for (it = folders.begin(); it != folders.end(); ++it) { + if (TFileStatus(*it).isLink()) + m_items.push_back(Item(*it, true, true)); + else + m_items.push_back(Item(*it, true, false)); + } + + for (it = files.begin(); it != files.end(); ++it) { + // skip the plt file (Palette file for TOONZ 4.6 and earlier) + if (it->getType() == "plt") continue; + + // filtering + else if (m_filter.isEmpty()) { + if (it->getType() != "tnz" && it->getType() != "scr" && + it->getType() != "tnzbat" && it->getType() != "mpath" && + it->getType() != "curve" && it->getType() != "tpl" && + TFileType::getInfo(*it) == TFileType::UNKNOW_FILE) + continue; + } else if (m_filter.contains(QString::fromStdString(it->getType()))) + continue; + + m_items.push_back(Item(*it)); + } + + // update the m_multiFileItemMap + m_multiFileItemMap.clear(); + for (it = all_files.begin(); it != all_files.end(); it++) { + TFilePath levelName(it->getLevelName()); + if (levelName.isLevelName()) { + Item &levelItem = m_multiFileItemMap[levelName]; + levelItem.m_frameIds.push_back(it->getFrame()); + levelItem.m_frameCount++; + } + } + } + // for all items in the folder, retrieve the file names(m_name) from the + // paths(m_path) + refreshData(); + + // update the ordering rules + bool discendentOrder = isDiscendentOrder(); + DataType currentDataType = getCurrentOrderType(); + setOrderType(Name); + setIsDiscendentOrder(true); + sortByDataModel(currentDataType, discendentOrder); + + m_itemViewer->repaint(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setHistoryDay(std::string dayDateString) { + m_folder = TFilePath(); + m_dayDateString = dayDateString; + const History::Day *day = History::instance()->getDay(dayDateString); + m_items.clear(); + if (day == 0) { + m_folderName->setText(""); + } else { + m_folderName->setText(QString::fromStdString(dayDateString)); + std::vector files; + day->getFiles(files); + std::vector::iterator it; + for (it = files.begin(); it != files.end(); ++it) + m_items.push_back(Item(*it)); + } + refreshData(); +} + +//----------------------------------------------------------------------------- +/*! for all items in the folder, retrieve the file names(m_name) from the + * paths(m_path) + */ +void SceneBrowser::refreshData() { + std::vector::iterator it; + for (it = m_items.begin(); it != m_items.end(); ++it) { + if (it->m_name == QString("")) + it->m_name = toQString(it->m_path.withoutParentDir()); + } +} + +//----------------------------------------------------------------------------- + +int SceneBrowser::getItemCount() const { return m_items.size(); } + +//----------------------------------------------------------------------------- + +void SceneBrowser::readInfo(Item &item) { + TFilePath fp = item.m_path; + QFileInfo info(toQString(fp)); + if (info.exists()) { + item.m_creationDate = info.created(); + item.m_modifiedDate = info.lastModified(); + item.m_fileType = info.suffix(); + item.m_fileSize = info.size(); + if (fp.getType() == "tnz") { + ToonzScene scene; + try { + item.m_frameCount = scene.loadFrameCount(fp); + } catch (...) { + } + } else + readFrameCount(item); + + item.m_validInfo = true; + } else if (fp.isLevelName()) // for levels johndoe..tif etc. + { + try { + // Find this level's item + std::map::iterator it = + m_multiFileItemMap.find(TFilePath(item.m_path.getLevelName())); + if (it == m_multiFileItemMap.end()) throw ""; + + item.m_creationDate = it->second.m_creationDate; + item.m_modifiedDate = it->second.m_modifiedDate; + item.m_fileType = it->second.m_fileType; + item.m_fileSize = it->second.m_fileSize; + item.m_frameCount = it->second.m_frameCount; + item.m_validInfo = true; + + // keep the list of frameIds at the first time and try to reuse it. + item.m_frameIds = it->second.m_frameIds; + + // The old way + /*TLevelReaderP lr(fp); +item.m_frameCount = lr->loadInfo()->getFrameCount(); +item.m_creationDate = QDateTime(); +item.m_modifiedDate = QDateTime(); +item.m_fileSize = -1; +item.m_validInfo = true;*/ + } catch (...) { + item.m_frameCount = 0; + } + } + + item.m_validInfo = true; +} + +//----------------------------------------------------------------------------- + +//! Frame count needs a special access function for viewable types - for they +//! are +//! calculated by using a dedicated thread and therefore cannot be simply +//! classified as *valid* or *invalid* infos... +void SceneBrowser::readFrameCount(Item &item) { + if (TFileType::isViewable(TFileType::getInfo(item.m_path))) { + if (isMultipleFrameType(item.m_path.getType())) + item.m_frameCount = m_frameCountReader.getFrameCount(item.m_path); + else + item.m_frameCount = 1; + } else + item.m_frameCount = 0; +} + +//----------------------------------------------------------------------------- + +QVariant SceneBrowser::getItemData(int index, DataType dataType, + bool isSelected) { + if (index < 0 || index >= (int)m_items.size()) return QVariant(); + Item &item = m_items[index]; + if (dataType == Name) { + // show two dots( ".." ) for the paret directory item + if (item.m_path == m_folder.getParentDir()) + return QString(".."); + else + return item.m_name; + } else if (dataType == Thumbnail) { + QSize iconSize = m_itemViewer->getPanel()->getIconSize(); + // parent folder icons + if (item.m_path == m_folder.getParentDir()) { + static QPixmap folderUpPixmap(svgToPixmap(":Resources/folderup_icon.svg", + iconSize, Qt::KeepAspectRatio)); + return folderUpPixmap; + } + // folder icons + else if (item.m_isFolder) { + if (item.m_isLink) { + static QPixmap linkIcon(svgToPixmap(":Resources/link_icon.svg", + iconSize, Qt::KeepAspectRatio)); + return linkIcon; + } else { + static QPixmap folderIcon(svgToPixmap(":Resources/folder_icon.svg", + iconSize, Qt::KeepAspectRatio)); + return folderIcon; + } + } + + QPixmap pixmap = IconGenerator::instance()->getIcon(item.m_path); + if (pixmap.isNull()) { + pixmap = QPixmap(iconSize); + pixmap.fill(Qt::white); + } + return scalePixmapKeepingAspectRatio(pixmap, iconSize, Qt::transparent); + } else if (dataType == Icon) + return QVariant(); + else if (dataType == ToolTip || dataType == FullPath) + return QString::fromStdWString(item.m_path.getWideString()); + + else if (dataType == IsFolder) { + return item.m_isFolder; + } + + if (!item.m_validInfo) { + readInfo(item); + if (!item.m_validInfo) return QVariant(); + } + + if (dataType == CreationDate) return item.m_creationDate; + if (dataType == ModifiedDate) return item.m_modifiedDate; + if (dataType == FileType) { + if (item.m_isLink) + return QString(""); + else if (item.m_isFolder) + return QString(""); + else + return QString::fromStdString(item.m_path.getType()).toUpper(); + } else if (dataType == FileSize) + return (item.m_fileSize == -1) ? QVariant() : item.m_fileSize; + else if (dataType == FrameCount) { + if (item.m_frameCount == -1) readFrameCount(item); + return item.m_frameCount; + } else if (dataType == PlayAvailable) { + std::string type = item.m_path.getType(); + if (item.m_frameCount > 1 && type != "tzp" && type != "tzu") return true; + return false; + } else if (dataType == VersionControlStatus) { + return getItemVersionControlStatus(item); + } else + return QVariant(); +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::isSceneItem(int index) const { + return 0 <= index && index < (int)m_items.size() && + m_items[index].m_path.getType() == "tnz"; +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::canRenameItem(int index) const { + // se sto guardando la history non posso rinominare nulla + if (getFolder() == TFilePath()) return false; + if (index < 0 || index >= (int)m_items.size()) return false; + // for now, disable rename for folders + if (m_items[index].m_isFolder) return false; + TFilePath fp = m_items[index].m_path; + return TFileStatus(fp).doesExist(); +} + +//----------------------------------------------------------------------------- + +int SceneBrowser::findIndexWithPath(TFilePath path) { + int i; + for (i = 0; i < m_items.size(); i++) + if (m_items[i].m_path == path) return i; + return -1; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::renameItem(int index, const QString &newName) { + if (getFolder() == TFilePath()) return; + if (index < 0 || index >= (int)m_items.size()) return; + + TFilePath fp = m_items[index].m_path; + TFilePath newFp = fp; + if (renameFile(newFp, newName)) { + m_items[index].m_name = QString::fromStdWString(newFp.getLevelNameW()); + m_items[index].m_path = newFp; + + // ho rinominato anche la palette devo aggiornarla. + if (newFp.getType() == "tlv" || newFp.getType() == "tzp" || + newFp.getType() == "tzu") { + const char *type = (newFp.getType() == "tlv") ? "tpl" : "plt"; + int paletteIndex = findIndexWithPath(fp.withNoFrame().withType(type)); + if (paletteIndex >= 0) { + TFilePath palettePath = newFp.withNoFrame().withType(type); + m_items[paletteIndex].m_name = + QString::fromStdWString(palettePath.getLevelNameW()); + m_items[paletteIndex].m_path = palettePath; + } + } + m_itemViewer->update(); + + if (fp.getType() == "tnz") { + // ho cambiato il folder _files. Devo aggiornare il folder che lo contiene + // nel tree view + QModelIndex index = m_folderTreeView->currentIndex(); + if (index.isValid()) { + DvDirModel::instance()->refresh(index); + // m_folderTreeView->getDvDirModel()->refresh(index); + m_folderTreeView->update(); + } + } + } +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::renameFile(TFilePath &fp, QString newName) { + if (isSpaceString(newName)) return true; + + TFilePath newFp(newName.toStdWString()); + if (newFp.getType() != "" && newFp.getType() != fp.getType()) { + DVGui::error(tr("Can't change file extension")); + return false; + } + if (newFp.getType() == "") newFp = newFp.withType(fp.getType()); + if (newFp.getFrame() != TFrameId::EMPTY_FRAME && + newFp.getFrame() != TFrameId::NO_FRAME) { + DVGui::error(tr("Can't set a drawing number")); + return false; + } + if (newFp.getDots() != fp.getDots()) { + if (fp.getDots() == ".") + newFp = newFp.withNoFrame(); + else if (fp.getDots() == "..") + newFp = newFp.withFrame(TFrameId::EMPTY_FRAME); + } + newFp = newFp.withParentDir(fp.getParentDir()); + + // se sono uguali non devo rinominare nulla + if (newFp == fp) return false; + + if (TSystem::doesExistFileOrLevel(newFp)) { + DVGui::error(tr("Can't rename. File already exists: ") + toQString(newFp)); + return false; + } + + try { + TSystem::renameFileOrLevel_throw(newFp, fp, true); + IconGenerator::instance()->remove(fp); + if (fp.getType() == "tnz") { + /* TFilePath folder = fp.getParentDir() + (fp.getName() + "_files"); +TFilePath newFolder = newFp.getParentDir() + (newFp.getName() + "_files"); +TSystem::renameFile(newFolder, folder); +*/ + TFilePath sceneIconFp = ToonzScene::getIconPath(fp); + TFilePath sceneIconNewFp = ToonzScene::getIconPath(newFp); + if (TFileStatus(sceneIconFp).doesExist()) { + if (TFileStatus(sceneIconNewFp).doesExist()) + TSystem::deleteFile(sceneIconNewFp); + TSystem::renameFile(sceneIconNewFp, sceneIconFp); + } + } + + } catch (...) { + DVGui::error(tr("Couldn't rename ") + toQString(fp) + " to " + + toQString(newFp)); + return false; + } + + fp = newFp; + return true; +} + +//----------------------------------------------------------------------------- + +QMenu *SceneBrowser::getContextMenu(QWidget *parent, int index) { + auto isOldLevelType = [](TFilePath &path) -> bool { + return path.getType() == "tzp" || path.getType() == "tzu"; + }; + + bool ret = true; + + // TODO: spostare in questa classe anche la definizione delle azioni? + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return 0; + std::vector files; + fs->getSelectedFiles(files); + + QMenu *menu = new QMenu(parent); + CommandManager *cm = CommandManager::instance(); + + if (files.empty()) { + menu->addAction(cm->getAction(MI_ShowFolderContents)); + menu->addAction(cm->getAction(MI_SelectAll)); + if (!Preferences::instance()->isWatchFileSystemEnabled()) { + menu->addAction(cm->getAction(MI_RefreshTree)); + } + return menu; + } + + if (files.size() == 1 && files[0].getType() == "tnz") { + menu->addAction(cm->getAction(MI_LoadScene)); + } + + bool areResources = true; + bool areScenes = false; + int i, j; + for (i = 0; i < (int)files.size(); i++) { + TFileType::Type type = TFileType::getInfo(files[i]); + if (areResources && !TFileType::isResource(type)) areResources = false; + if (!areScenes && TFileType::isScene(type)) areScenes = true; + } + + bool areFullcolor = true; + for (i = 0; i < (int)files.size(); i++) { + TFileType::Type type = TFileType::getInfo(files[i]); + if (!TFileType::isFullColor(type)) { + areFullcolor = false; + break; + } + } + + TFilePath clickedFile; + if (0 <= index && index < (int)m_items.size()) + clickedFile = m_items[index].m_path; + + if (areResources) { + QString title; + if (clickedFile != TFilePath() && clickedFile.getType() == "tnz") + title = tr("Load As Sub-xsheet"); + else + title = tr("Load"); + QAction *action = new QAction(title, menu); + ret = ret && + connect(action, SIGNAL(triggered()), this, SLOT(loadResources())); + menu->addAction(action); + menu->addSeparator(); + } + + menu->addAction(cm->getAction(MI_DuplicateFile)); + if (!areScenes) { + menu->addAction(cm->getAction(MI_Copy)); + menu->addAction(cm->getAction(MI_Paste)); + } + menu->addAction(cm->getAction(MI_Clear)); + menu->addAction(cm->getAction(MI_ShowFolderContents)); + menu->addAction(cm->getAction(MI_SelectAll)); + menu->addAction(cm->getAction(MI_FileInfo)); + if (!clickedFile.isEmpty() && + (clickedFile.getType() == "tnz" || clickedFile.getType() == "tab")) { + menu->addSeparator(); + menu->addAction(cm->getAction(MI_AddToBatchRenderList)); + menu->addAction(cm->getAction(MI_AddToBatchCleanupList)); + } + + for (i = 0; i < files.size(); i++) + if (!TFileType::isViewable(TFileType::getInfo(files[i]))) break; + if (i == files.size()) { + std::string type = files[0].getType(); + for (j = 0; j < files.size(); j++) + if (isOldLevelType(files[j])) break; + if (j == files.size()) menu->addAction(cm->getAction(MI_ViewFile)); + + for (j = 0; j < files.size(); j++) { + if ((files[0].getType() == "pli" && files[j].getType() != "pli") || + (files[0].getType() != "pli" && files[j].getType() == "pli")) + break; + else if ((isOldLevelType(files[0]) && !isOldLevelType(files[j])) || + (!isOldLevelType(files[0]) && isOldLevelType(files[j]))) + break; + } + if (j == files.size()) { + menu->addAction(cm->getAction(MI_ConvertFiles)); + // iwsw commented out temporarily + // menu->addAction(cm->getAction(MI_ToonShadedImageToTLV)); + } + if (areFullcolor) menu->addAction(cm->getAction(MI_SeparateColors)); + + if (!areFullcolor) menu->addSeparator(); + } + if (files.size() == 1 && files[0].getType() != "tnz") { + QAction *action = new QAction(tr("Rename"), menu); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(renameAsToonzLevel())); + menu->addAction(action); + } +#ifdef LEVO + + if (files.size() == 2 && + (files[0].getType() == "tif" || files[0].getType() == "tiff" || + files[0].getType() == "png" || files[0].getType() == "TIF" || + files[0].getType() == "TIFF" || files[0].getType() == "PNG") && + (files[1].getType() == "tif" || files[1].getType() == "tiff" || + files[1].getType() == "png" || files[1].getType() == "TIF" || + files[1].getType() == "TIFF" || files[1].getType() == "PNG")) { + QAction *action = new QAction(tr("Convert to Painted TLV"), menu); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(convertToPaintedTlv())); + menu->addAction(action); + } + if (areFullcolor) { + QAction *action = new QAction(tr("Convert to Unpainted TLV"), menu); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(convertToUnpaintedTlv())); + menu->addAction(action); + menu->addSeparator(); + } +#endif + + if (!clickedFile.isEmpty() && (clickedFile.getType() == "tnz")) { + menu->addSeparator(); + menu->addAction(cm->getAction(MI_CollectAssets)); + menu->addAction(cm->getAction(MI_ImportScenes)); + menu->addAction(cm->getAction(MI_ExportScenes)); + } + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (node) { + // Check Version Control Status + DvItemListModel::Status status = + (DvItemListModel::Status)m_itemViewer->getModel() + ->getItemData(index, DvItemListModel::VersionControlStatus) + .toInt(); + + // Remove the added actions + if (status == DvItemListModel::VC_Missing) menu->clear(); + + QMenu *vcMenu = new QMenu(tr("Version Control"), parent); + QAction *action; + + if (status == DvItemListModel::VC_ReadOnly || + (status == DvItemListModel::VC_ToUpdate && files.size() == 1)) { + if (status == DvItemListModel::VC_ReadOnly) { + action = vcMenu->addAction(tr("Edit")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(editVersionControl())); + + TFilePath path = files.at(0); + std::string fileType = path.getType(); + if (fileType == "tlv" || fileType == "pli" || path.getDots() == "..") { + action = vcMenu->addAction(tr("Edit Frame Range...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(editFrameRangeVersionControl())); + } + } else { + action = vcMenu->addAction(tr("Edit")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(updateAndEditVersionControl())); + } + } + + if (status == DvItemListModel::VC_Modified) { + action = vcMenu->addAction(tr("Put...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(putVersionControl())); + + action = vcMenu->addAction(tr("Revert")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(revertVersionControl())); + } + + if (status == DvItemListModel::VC_ReadOnly || + status == DvItemListModel::VC_ToUpdate) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (status == DvItemListModel::VC_ReadOnly) { + action = vcMenu->addAction(tr("Delete")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(deleteVersionControl())); + } + + vcMenu->addSeparator(); + + if (files.size() == 1) { + action = vcMenu->addAction(tr("Get Revision...")); + TFilePath path = files.at(0); + if (path.getDots() == "..") + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionVersionControl())); + else + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionHistory())); + } else if (files.size() > 1) { + action = vcMenu->addAction("Get Revision..."); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionVersionControl())); + } + } + + if (status == DvItemListModel::VC_Edited) { + action = vcMenu->addAction(tr("Unlock")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(unlockVersionControl())); + } + + if (status == DvItemListModel::VC_Unversioned) { + action = vcMenu->addAction(tr("Put...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(putVersionControl())); + } + + if (status == DvItemListModel::VC_Locked && files.size() == 1) { + action = vcMenu->addAction(tr("Unlock")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(unlockVersionControl())); + + action = vcMenu->addAction(tr("Edit Info")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(showLockInformation())); + } + + if (status == DvItemListModel::VC_Missing) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (files.size() == 1) { + vcMenu->addSeparator(); + action = vcMenu->addAction(tr("Revision History...")); + TFilePath path = files.at(0); + if (path.getDots() == "..") + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionVersionControl())); + else + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getRevisionHistory())); + } + } + + if (status == DvItemListModel::VC_PartialLocked) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + if (files.size() == 1) { + action = vcMenu->addAction(tr("Edit Frame Range...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(editFrameRangeVersionControl())); + + action = vcMenu->addAction(tr("Edit Info")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(showFrameRangeLockInfo())); + } + + } else if (status == DvItemListModel::VC_PartialEdited) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (files.size() == 1) { + action = vcMenu->addAction(tr("Unlock Frame Range")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(unlockFrameRangeVersionControl())); + + action = vcMenu->addAction(tr("Edit Info")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(showFrameRangeLockInfo())); + } + } else if (status == DvItemListModel::VC_PartialModified) { + action = vcMenu->addAction(tr("Get")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(getVersionControl())); + + if (files.size() == 1) { + action = vcMenu->addAction(tr("Put...")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(putFrameRangeVersionControl())); + + action = vcMenu->addAction(tr("Revert")); + ret = ret && connect(action, SIGNAL(triggered()), this, + SLOT(revertFrameRangeVersionControl())); + } + } + + if (!vcMenu->isEmpty()) { + menu->addSeparator(); + menu->addMenu(vcMenu); + } + } + + if (!Preferences::instance()->isWatchFileSystemEnabled()) { + menu->addSeparator(); + menu->addAction(cm->getAction(MI_RefreshTree)); + } + + assert(ret); + + return menu; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::startDragDrop() { + TRepetitionGuard guard; + if (!guard.hasLock()) return; + + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + std::vector files; + fs->getSelectedFiles(files); + if (files.empty()) return; + + QList urls; + for (int i = 0; i < (int)files.size(); i++) { + if (TSystem::doesExistFileOrLevel(files[i])) + urls.append(QUrl::fromLocalFile( + QString::fromStdWString(files[i].getWideString()))); + } + if (urls.isEmpty()) return; + + QMimeData *mimeData = new QMimeData; + mimeData->setUrls(urls); + QDrag *drag = new QDrag(this); + QSize iconSize = m_itemViewer->getPanel()->getIconSize(); + QPixmap icon = IconGenerator::instance()->getIcon(files[0]); + QPixmap dropThumbnail = + scalePixmapKeepingAspectRatio(icon, iconSize, Qt::transparent); + if (!dropThumbnail.isNull()) drag->setPixmap(dropThumbnail); + drag->setMimeData(mimeData); + Qt::DropAction dropAction = drag->exec(Qt::CopyAction); +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::dropMimeData(QTreeWidgetItem *parent, int index, + const QMimeData *data, Qt::DropAction action) { + return false; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onTreeFolderChanged() { + // Commented by KD + DvDirModelNode *node = m_folderTreeView->getCurrentNode(); + //if (node) + // node->visualizeContent(this); + //else + // setFolder(TFilePath()); + m_itemViewer->resetVerticalScrollBar(); + m_itemViewer->updateContentSize(); + m_itemViewer->getPanel()->update(); + m_frameCountReader.stopReading(); + IconGenerator::instance()->clearRequests(); + + DvDirModelFileFolderNode *fileFolderNode = + dynamic_cast(node); + if (fileFolderNode) emit treeFolderChanged(fileFolderNode->getPath()); + + // Restore scroll position + m_itemViewer->verticalScrollBar()->setValue(m_currentScroll); + +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::changeFolder(const QModelIndex &index) {} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight) { + onTreeFolderChanged(); +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::acceptDrop(const QMimeData *data) const { + // se il browser non sta visualizzando un folder standard non posso accettare + // nessun drop + if (getFolder() == TFilePath()) return false; + + if (data->hasFormat("application/vnd.toonz.levels") || + data->hasFormat("application/vnd.toonz.currentscene") || + data->hasFormat("application/vnd.toonz.drawings") || + acceptResourceDrop(data->urls())) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneBrowser::drop(const QMimeData *mimeData) { + // se il browser non sta visualizzando un folder standard non posso accettare + // nessun drop + TFilePath folderPath = getFolder(); + if (folderPath == TFilePath()) return false; + + if (mimeData->hasFormat(CastItems::getMimeFormat())) { + const CastItems *items = dynamic_cast(mimeData); + if (!items) return false; + + int i; + for (i = 0; i < items->getItemCount(); i++) { + CastItem *item = items->getItem(i); + if (TXshSimpleLevel *sl = item->getSimpleLevel()) { + TFilePath levelPath = sl->getPath().withParentDir(getFolder()); + IoCmd::saveLevel(levelPath, sl, false); + } else if (TXshSoundLevel *level = item->getSoundLevel()) { + TFilePath soundPath = level->getPath().withParentDir(getFolder()); + IoCmd::saveSound(soundPath, level, false); + } + } + refreshFolder(getFolder()); + return true; + } else if (mimeData->hasFormat("application/vnd.toonz.currentscene")) { + TFilePath scenePath; + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + if (scene->isUntitled()) { + bool ok; + QString sceneName = + QInputDialog::getText(this, tr("Save Scene"), tr("Scene name:"), + QLineEdit::Normal, QString(), &ok); + if (!ok || sceneName == "") return false; + scenePath = folderPath + sceneName.toStdWString(); + } else + scenePath = folderPath + scene->getSceneName(); + return IoCmd::saveScene(scenePath, false); + } else if (mimeData->hasFormat("application/vnd.toonz.drawings")) { + TFilmstripSelection *s = + dynamic_cast(TSelection::getCurrent()); + if (!s) return false; + TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel(); + if (!sl) return false; + + std::wstring levelName = sl->getName(); + folderPath += + TFilePath(levelName + ::to_wstring(sl->getPath().getDottedType())); + if (TSystem::doesExistFileOrLevel(folderPath)) { + QString question = "Level " + toQString(folderPath) + + " already exists\nDo you want to duplicate it?"; + int ret = DVGui::MsgBox(question, QObject::tr("Duplicate"), + QObject::tr("Don't Duplicate"), 0); + if (ret == 2 || ret == 0) return false; + TFilePath path = folderPath; + NameBuilder *nameBuilder = + NameBuilder::getBuilder(::to_wstring(path.getName())); + do + levelName = nameBuilder->getNext(); + while (TSystem::doesExistFileOrLevel(path.withName(levelName))); + folderPath = path.withName(levelName); + } + assert(!TSystem::doesExistFileOrLevel(folderPath)); + + TXshSimpleLevel *newSl = new TXshSimpleLevel(); + newSl->setType(sl->getType()); + newSl->clonePropertiesFrom(sl); + newSl->setName(levelName); + newSl->setPalette(sl->getPalette()); + newSl->setScene(sl->getScene()); + std::set frames = s->getSelectedFids(); + for (auto const &fid : frames) { + newSl->setFrame(fid, sl->getFrame(fid, false)); + } + + IoCmd::saveLevel(folderPath, newSl, false); + refreshFolder(folderPath.getParentDir()); + return true; + } else if (mimeData->hasUrls()) { + int count = 0; + for (const QUrl &url : mimeData->urls()) { + TFilePath srcFp(url.toLocalFile().toStdWString()); + TFilePath dstFp = srcFp.withParentDir(folderPath); + if (dstFp != srcFp) { + if (!TSystem::copyFileOrLevel(dstFp, srcFp)) + DVGui::error(tr("There was an error copying %1 to %2") + .arg(toQString(srcFp)) + .arg(toQString(dstFp))); + } + } + refreshFolder(folderPath); + return true; + } else + return false; +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::loadResources() { + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + + std::vector filePaths; + fs->getSelectedFiles(filePaths); + + if (filePaths.empty()) return; + + IoCmd::LoadResourceArguments args; + args.resourceDatas.assign(filePaths.begin(), filePaths.end()); + + IoCmd::loadResources(args); +} + +//----------------------------------------------------------------------------- + +namespace { + +bool parsePathName(const QString &fullpath, QString &parentPath, QString &name, + QString &format) { + int index = fullpath.lastIndexOf('\\'); + if (index == -1) index = fullpath.lastIndexOf('/'); + + QString filename; + + if (index != -1) { + parentPath = fullpath.left(index + 1); + filename = fullpath.right(fullpath.size() - index - 1); + } else { + parentPath = ""; + filename = fullpath; + } + + index = filename.lastIndexOf('.'); + + if (index <= 0) return false; + + format = filename.right(filename.size() - index - 1); + if (format == "") return false; + + index--; + if (!filename.at(index).isDigit()) return false; + + while (index >= 0 && filename.at(index).isDigit()) index--; + + if (index < 0) return false; + + name = filename.left(index + 1); + + return true; +} + +//--------------------------------------------------------- + +void getLevelFiles(const QString &parentPath, const QString &name, + const QString &format, QStringList &pathIn) { + QString dummy, dummy1, filter = "*." + format; + QDir dir(parentPath, filter); + QStringList list = dir.entryList(); + + for (int i = 0; i < list.size(); i++) { + QString item = list.at(i); + QString itemName; + if (!parsePathName(item, dummy, itemName, dummy1) || name != itemName) + continue; + + pathIn.push_back(item); + } +} + +//--------------------------------------------------------- + +QString getFrame(const QString &filename) { + int index = filename.lastIndexOf('.'); + + if (index <= 0) return ""; + + index--; + if (!filename.at(index).isDigit()) return ""; + + int to, from; + to = from = index; + while (from >= 0 && filename.at(from).isDigit()) from--; + + if (from < 0) return ""; + + char padStr[5]; + padStr[4] = '\0'; + + int i, frame = 0; + + QString number = filename.mid(from + 1, to - from); + for (i = 0; i < 4 - number.size(); i++) padStr[i] = '0'; + for (i = 0; i < number.size(); i++) +#if QT_VERSION >= 0x050500 + padStr[4 - number.size() + i] = number.at(i).toLatin1(); +#else + padStr[4 - number.size() + i] = number.at(i).toAscii(); +#endif + return QString(padStr); +} + +//------------------------------------------------------------------ + +//----------------------------------------------------------- + +void renameSingleFileOrToonzLevel(const QString &fullpath) { + TFilePath fpin(fullpath.toStdString()); + + RenameAsToonzPopup popup( + QString::fromStdWString(fpin.withoutParentDir().getWideString())); + if (popup.exec() != QDialog::Accepted) return; + + std::string name = popup.getName().toStdString(); + + if (name == fpin.getName()) { + DVGui::error(QString( + QObject::tr("The specified name is already assigned to the %1 file.") + .arg(fullpath))); + return; + } + + if (popup.doOverwrite()) + TSystem::renameFileOrLevel(fpin.withName(name), fpin, true); + else + TSystem::copyFileOrLevel(fpin.withName(name), fpin); +} + +//---------------------------------------------------------- + +void doRenameAsToonzLevel(const QString &fullpath) { + QString parentPath, name, format; + + if (!parsePathName(fullpath, parentPath, name, format)) { + renameSingleFileOrToonzLevel(fullpath); + return; + } + + QStringList pathIn; + + getLevelFiles(parentPath, name, format, pathIn); + + if (pathIn.empty()) return; + + while (name.endsWith('_') || name.endsWith('.') || name.endsWith(' ')) + name.chop(1); + + RenameAsToonzPopup popup(name, pathIn.size()); + if (popup.exec() != QDialog::Accepted) return; + + name = popup.getName(); + + QString levelOutStr = parentPath + "/" + name + ".." + format; + + TFilePath levelOut(levelOutStr.toStdWString()); + if (TSystem::doesExistFileOrLevel(levelOut)) { + QApplication::restoreOverrideCursor(); + int ret = DVGui::MsgBox( + QObject::tr("Warning: level %1 already exists; overwrite?") + .arg(toQString(levelOut)), + QObject::tr("Yes"), QObject::tr("No"), 1); + QApplication::setOverrideCursor(Qt::WaitCursor); + if (ret == 2 || ret == 0) return; + TSystem::removeFileOrLevel(levelOut); + } + + int i; + for (i = 0; i < pathIn.size(); i++) { + QString padStr = getFrame(pathIn[i]); + if (padStr == "") continue; + QString pathOut = parentPath + "/" + name + "." + padStr + "." + format; + + if (popup.doOverwrite()) { + if (!QFile::rename(parentPath + "/" + pathIn[i], pathOut)) { + QString tmp(parentPath + "/" + pathIn[i]); + DVGui::error(QString( + QObject::tr("It is not possible to rename the %1 file.").arg(tmp))); + return; + } + } else if (!QFile::copy(parentPath + "/" + pathIn[i], pathOut)) { + QString tmp(parentPath + "/" + pathIn[i]); + DVGui::error(QString( + QObject::tr("It is not possible to copy the %1 file.").arg(tmp))); + + return; + } + } +} + +} // namespace + +//------------------------------------------------------------------------------- + +void SceneBrowser::renameAsToonzLevel() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.size() != 1) return; + + doRenameAsToonzLevel(QString::fromStdWString(filePaths[0].getWideString())); + + QApplication::restoreOverrideCursor(); + + FileBrowser::refreshFolder(filePaths[0].getParentDir()); +} + +#ifdef LEVO + +void SceneBrowser::convertToUnpaintedTlv() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + + QStringList sl; + sl << "Apply Autoclose " + << "Don't Apply Autoclose "; + bool ok; + QString autoclose = QInputDialog::getItem( + this, tr("Convert To Unpainted Tlv"), "", sl, 0, false, &ok); + if (!ok) return; + + QApplication::setOverrideCursor(Qt::WaitCursor); + + int i, totFrames = 0; + std::vector converters; + for (i = 0; i < filePaths.size(); i++) { + Convert2Tlv *converter = + new Convert2Tlv(filePaths[i], TFilePath(), TFilePath(), -1, -1, + autoclose == sl.at(0), TFilePath(), 0, 0, 0); + + if (TSystem::doesExistFileOrLevel(converter->m_levelOut)) { + QApplication::restoreOverrideCursor(); + int ret = DVGui::MsgBox(tr("Warning: level %1 already exists; overwrite?") + .arg(toQString(converter->m_levelOut)), + tr("Yes"), tr("No"), 1); + QApplication::setOverrideCursor(Qt::WaitCursor); + if (ret == 2 || ret == 0) { + delete converter; + continue; + } + TSystem::removeFileOrLevel(converter->m_levelOut); + } + + totFrames += converter->getFramesToConvertCount(); + converters.push_back(converter); + } + + if (converters.empty()) { + QApplication::restoreOverrideCursor(); + return; + } + + ProgressDialog pb("", "Cancel", 0, totFrames); + int j, l, k = 0; + for (i = 0; i < converters.size(); i++) { + std::string errorMessage; + if (!converters[i]->init(errorMessage)) { + converters[i]->abort(); + DVGui::error(QString::fromStdString(errorMessage)); + delete converters[i]; + converters[i] = 0; + continue; + } + + int count = converters[i]->getFramesToConvertCount(); + + pb.setLabelText("Generating level " + toQString(converters[i]->m_levelOut)); + pb.show(); + + for (j = 0; j < count; j++) { + std::string errorMessage = ""; + if (!converters[i]->convertNext(errorMessage) || pb.wasCanceled()) { + for (l = i; l < converters.size(); l++) { + converters[l]->abort(); + delete converters[i]; + converters[i] = 0; + } + if (errorMessage != "") + DVGui::error(QString::fromStdString(errorMessage)); + QApplication::restoreOverrideCursor(); + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); + return; + } + pb.setValue(++k); + } + TFilePath levelOut(converters[i]->m_levelOut); + delete converters[i]; + IconGenerator::instance()->invalidate(levelOut); + + converters[i] = 0; + } + + QApplication::restoreOverrideCursor(); + pb.hide(); + DVGui::info(tr("Done: All Levels converted to TLV Format")); + + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::convertToPaintedTlv() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + + if (filePaths.size() != 2) return; + + QStringList sl; + sl << "Apply Autoclose " + << "Don't Apply Autoclose "; + bool ok; + QString autoclose = QInputDialog::getItem(this, tr("Convert To Painted Tlv"), + "", sl, 0, false, &ok); + if (!ok) return; + + QApplication::setOverrideCursor(Qt::WaitCursor); + + Convert2Tlv *converter = + new Convert2Tlv(filePaths[0], filePaths[1], TFilePath(), -1, -1, + autoclose == sl.at(0), TFilePath(), 0, 0, 0); + + if (TSystem::doesExistFileOrLevel(converter->m_levelOut)) { + QApplication::restoreOverrideCursor(); + int ret = DVGui::MsgBox(tr("Warning: level %1 already exists; overwrite?") + .arg(toQString(converter->m_levelOut)), + tr("Yes"), tr("No"), 1); + QApplication::setOverrideCursor(Qt::WaitCursor); + if (ret == 2 || ret == 0) { + QApplication::restoreOverrideCursor(); + return; + } + TSystem::removeFileOrLevel(converter->m_levelOut); + } + + std::string errorMessage; + if (!converter->init(errorMessage)) { + converter->abort(); + delete converter; + DVGui::error(QString::fromStdString(errorMessage)); + QApplication::restoreOverrideCursor(); + return; + } + int count = converter->getFramesToConvertCount(); + + ProgressDialog pb("Generating level " + toQString(converter->m_levelOut), + "Cancel", 0, count); + pb.show(); + + for (int i = 0; i < count; i++) { + errorMessage = ""; + if (!converter->convertNext(errorMessage) || pb.wasCanceled()) { + converter->abort(); + delete converter; + if (errorMessage != "") + DVGui::error(QString::fromStdString(errorMessage)); + QApplication::restoreOverrideCursor(); + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); + return; + } + + pb.setValue(i + 1); + } + + TFilePath levelOut(converter->m_levelOut); + delete converter; + IconGenerator::instance()->invalidate(levelOut); + + QApplication::restoreOverrideCursor(); + pb.hide(); + DVGui::info(tr("Done: 2 Levels converted to TLV Format")); + + fs->selectNone(); + SceneBrowser::refreshFolder(filePaths[0].getParentDir()); +} +#endif + +//----------------------------------------------------------------------------- + +void SceneBrowser::onSceneSwitched() { + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + TFilePath scenesFolder = scene->getScenePath().getParentDir(); + //TFilePath scenesFolder = TProjectManager::instance()->getCurrentProject()->getScenesPath(); + setFolder(scenesFolder, true); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onSelectedItems(const std::set &indexes) { + std::set filePaths; + std::set::const_iterator it; + + // pass the frameId list for reuse + std::list> frameIDs; + + if (indexes.empty()) { // inform selection is released + emit filePathsSelected(filePaths, frameIDs); + return; + } + + for (it = indexes.begin(); it != indexes.end(); ++it) { + filePaths.insert(m_items[*it].m_path); + frameIDs.insert(frameIDs.begin(), m_items[*it].m_frameIds); + } + + // reuse the list of TFrameId in order to skip loadInfo() when loading the + // level with sequencial frames. + emit filePathsSelected(filePaths, frameIDs); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onClickedItem(int index) { + if (0 <= index && index < (int)m_items.size()) { + // if the folder is clicked, then move the current folder + TFilePath fp = m_items[index].m_path; + if (m_items[index].m_isFolder) { + setFolder(fp, true); + QModelIndex index = m_folderTreeView->currentIndex(); + if (index.isValid()) m_folderTreeView->scrollTo(index); + } else + emit filePathClicked(fp); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onDoubleClickedItem(int index) { + // TODO: Avoid duplicate code with onClickedItem(). + if (0 <= index && index < (int)m_items.size()) { + // if the folder is clicked, then move the current folder + TFilePath fp = m_items[index].m_path; + if (m_items[index].m_isFolder) { + setFolder(fp, true); + QModelIndex index = m_folderTreeView->currentIndex(); + if (index.isValid()) m_folderTreeView->scrollTo(index); + } else + emit filePathDoubleClicked(fp); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refreshFolder(const TFilePath &folderPath) { + std::set::iterator it; + for (it = activePreproductionBoards.begin(); it != activePreproductionBoards.end(); ++it) { + SceneBrowser *browser = *it; + DvDirModel::instance()->refreshFolder(folderPath); + if (browser->getFolder() == folderPath) { + browser->setFolder(folderPath, false, true); + } + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::updateItemViewerPanel() { + std::set::iterator it; + for (it = activePreproductionBoards.begin(); it != activePreproductionBoards.end(); ++it) { + SceneBrowser *browser = *it; + browser->m_itemViewer->getPanel()->update(); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getExpandedFolders(DvDirModelNode *node, + QList &expandedNodes) { + if (!node) return; + QModelIndex newIndex = DvDirModel::instance()->getIndexByNode(node); + if (!m_folderTreeView->isExpanded(newIndex)) return; + expandedNodes.push_back(node); + + int i = 0; + for (i = 0; i < node->getChildCount(); i++) + getExpandedFolders(node->getChild(i), expandedNodes); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::refresh() { + TFilePath originalFolder( + m_folder); // setFolder is invoked by Qt throughout the following... + + int dx = m_folderTreeView->verticalScrollBar()->value(); + DvDirModelNode *rootNode = DvDirModel::instance()->getNode(QModelIndex()); + + QModelIndex index = DvDirModel::instance()->getIndexByNode(rootNode); + + bool vcEnabled = m_folderTreeView->refreshVersionControlEnabled(); + + m_folderTreeView->setRefreshVersionControlEnabled(false); + DvDirModel::instance()->refreshFolderChild(index); + m_folderTreeView->setRefreshVersionControlEnabled(vcEnabled); + + QList expandedNodes; + int i; + for (i = 0; i < rootNode->getChildCount(); i++) + getExpandedFolders(rootNode->getChild(i), expandedNodes); + + for (i = 0; i < expandedNodes.size(); i++) { + DvDirModelNode *node = expandedNodes[i]; + if (!node || !node->hasChildren()) continue; + QModelIndex ind = DvDirModel::instance()->getIndexByNode(node); + if (!ind.isValid()) continue; + m_folderTreeView->expand(ind); + } + m_folderTreeView->verticalScrollBar()->setValue(dx); + + setFolder(originalFolder, false, true); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::newScene() { + + m_currentScroll = m_itemViewer->verticalScrollBar()->value(); + + TFilePath parentFolder = getFolder(); + QString sceneName; + TFilePath scenePath; + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + if (scene->isUntitled()) { + bool ok; + sceneName = + QInputDialog::getText(this, tr("Save Scene"), tr("Scene name:"), + QLineEdit::Normal, QString(), &ok); + if (!ok || sceneName == "") return; + } else { + sceneName = QString::fromWCharArray( scene->getSceneName().c_str() ); + } + QString prefix; + QString number; + for(int j = 0; jcurrentIndex(); + model->refresh(parentFolderIndex); + m_folderTreeView->expand(parentFolderIndex); + + std::wstring newFolderName = folderPath.getWideName(); + QModelIndex newFolderIndex = + model->childByName(parentFolderIndex, newFolderName); + if (newFolderIndex.isValid()) { + m_folderTreeView->setCurrentIndex(newFolderIndex); + m_folderTreeView->scrollTo(newFolderIndex); + m_folderTreeView->QAbstractItemView::edit(newFolderIndex); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::showEvent(QShowEvent *) { + activePreproductionBoards.insert(this); + // refresh + if (getFolder() != TFilePath()) + setFolder(getFolder(), false, true); + else if (getDayDateString() != "") + setHistoryDay(getDayDateString()); + m_folderTreeView->scrollTo(m_folderTreeView->currentIndex()); + + // Refresh SVN + DvDirVersionControlNode *vcNode = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (vcNode) m_folderTreeView->refreshVersionControl(vcNode); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::hideEvent(QHideEvent *) { + activePreproductionBoards.erase(this); + m_itemViewer->getPanel()->getItemViewPlayDelegate()->resetPlayWidget(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::makeCurrentProjectVisible() {} + +//----------------------------------------------------------------------------- + +void SceneBrowser::enableGlobalSelection(bool enabled) { + m_folderTreeView->enableGlobalSelection(enabled); + m_itemViewer->enableGlobalSelection(enabled); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::selectNone() { m_itemViewer->selectNone(); } + +//----------------------------------------------------------------------------- + +void SceneBrowser::enableDoubleClickToOpenScenes() { + // perhaps this should disconnect existing signal handlers first + connect(this, SIGNAL(filePathDoubleClicked(const TFilePath &)), this, + SLOT(tryToOpenScene(const TFilePath &))); +} + +void SceneBrowser::enableSingleClickToOpenScenes() { + // perhaps this should disconnect existing signal handlers first + connect(this, SIGNAL(filePathClicked(const TFilePath &)), this, + SLOT(tryToOpenScene(const TFilePath &))); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::tryToOpenScene(const TFilePath &filePath) { + if (filePath.getType() == "tnz") { + IoCmd::loadScene(filePath); + } +} + +//============================================================================= + +OpenFloatingPanel openPreproductionBoardPane(MI_OpenPreproductionBoard, "PreproductionBoard", + QObject::tr("Preproduction Board")); diff --git a/toonz/sources/toonz/scenebrowser.h b/toonz/sources/toonz/scenebrowser.h new file mode 100644 index 0000000..5a4edb0 --- /dev/null +++ b/toonz/sources/toonz/scenebrowser.h @@ -0,0 +1,265 @@ +#pragma once + +#ifndef SCENEBROWSER_INCLUDED +#define SCENEBROWSER_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include "dvitemview.h" +#include "tfilepath.h" +#include "toonzqt/dvdialog.h" +#include "versioncontrol.h" +#include "filebrowser.h" + +#include "tthread.h" + +class QLineEdit; +class QTreeWidgetItem; +class QSplitter; +class DvDirModelNode; +class DvDirTreeView; +class QFileSystemWatcher; + +//----------------------------------------------------------------------------- +class SceneBrowserButtonBar final : public QToolBar { + Q_OBJECT + QAction *m_folderBack; + QAction *m_folderFwd; + +public: + SceneBrowserButtonBar(DvItemViewer *itemViewer, QWidget *parent = 0); + +signals: + void newScene(); +}; +//----------------------------------------------------------------------------- + +class SceneBrowser final : public QFrame, public DvItemListModel { + Q_OBJECT + +public: +#if QT_VERSION >= 0x050500 + SceneBrowser(QWidget *parent, Qt::WindowFlags flags = 0, + bool noContextMenu = false, bool multiSelectionEnabled = false); +#else + SceneBrowser(QWidget *parent, Qt::WFlags flags = 0, bool noContextMenu = false, + bool multiSelectionEnabled = false); +#endif + ~SceneBrowser(); + + void sortByDataModel(DataType dataType, bool isDiscendent) override; + void refreshData() override; + + int getItemCount() const override; + QVariant getItemData(int index, DataType dataType, + bool isSelected = false) override; + + bool canRenameItem(int index) const override; + void renameItem(int index, const QString &newName) override; + + bool isSceneItem(int index) const override; + void startDragDrop() override; + QMenu *getContextMenu(QWidget *parent, int index) override; + + /*! +This functions adds to the types to be filtered a new type; +if this function is never called, the default filter is all image +files and scene files and palette files +*/ + void addFilterType(const QString &type); + + /*! +The setFilterTypes function directly specifies the list of file +types to be displayed in the file browser. +*/ + void setFilterTypes(const QStringList &types); + const QStringList &getFilterTypes() const { return m_filter; } + void removeFilterType(const QString &type); + + void setFolder(const TFilePath &fp, bool expandNode = false, + bool forceUpdate = false); + // process when inputting the folder which is not regitered in the folder tree + // (e.g. UNC path in Windows) + void setUnregisteredFolder(const TFilePath &fp); + + void setHistoryDay(std::string dayDateString); + + TFilePath getFolder() const { return m_folder; } + std::string getDayDateString() const { return m_dayDateString; } + + static void refreshFolder(const TFilePath &folder); + + static void updateItemViewerPanel(); + + // ritorna true se il file e' stato rinominato. dopo la chiamata fp contiene + // il nuovo path + static bool renameFile(TFilePath &fp, QString newName); + + void makeCurrentProjectVisible(); + + void enableGlobalSelection(bool enabled); + void selectNone(); + + QSplitter *getMainSplitter() const { return m_mainSplitter; } + + // Enable double-click to open a scene. + // This is not always desirable (e.g. if a user double-clicks on a file in + // a "Save As" dialog, they expect the file will be saved to, not opened). + // So it is disabled by default. + void enableDoubleClickToOpenScenes(); + + void enableSingleClickToOpenScenes(); + +protected: + int findIndexWithPath(TFilePath path); + void getExpandedFolders(DvDirModelNode *node, + QList &expandedNodes); + + bool dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, + Qt::DropAction action); + + bool acceptDrop(const QMimeData *data) const override; + bool drop(const QMimeData *data) override; + void showEvent(QShowEvent *) override; + void hideEvent(QHideEvent *) override; + + // Fill the QStringList with files selected in the browser, auxiliary files + // (palette for tlv, hooks, sceneIcons) + // retrieve also the path, and return also the sceneIconsCount + void setupVersionControlCommand(QStringList &files, QString &path, + int &sceneIconsCount); + void setupVersionControlCommand(QString &file, QString &path); + + void refreshHistoryButtons(); + +public slots: + + void onTreeFolderChanged(); + +protected slots: + + void refresh(); + + void changeFolder(const QModelIndex &index); + void onDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight); + void loadResources(); + void onClickedItem(int index); + void onDoubleClickedItem(int index); + void onSelectedItems(const std::set &indexes); + void onSceneSwitched(); + void newScene(); + + void onBackButtonPushed(); + void onFwdButtonPushed(); + void onFolderEdited(); + void storeFolderHistory(); + void clearHistory(); + + void renameAsToonzLevel(); + void updateAndEditVersionControl(); + void editVersionControl(); + void unlockVersionControl(); + + void editFrameRangeVersionControl(); + void unlockFrameRangeVersionControl(); + + void putFrameRangeVersionControl(); + void revertFrameRangeVersionControl(); + + void showLockInformation(); + void showFrameRangeLockInfo(); + + void putVersionControl(); + void revertVersionControl(); + void deleteVersionControl(); + void getVersionControl(); + void getRevisionVersionControl(); + void getRevisionHistory(); + + void onVersionControlCommandDone(const QStringList &files); + + void onFileSystemChanged(const QString &folderPath); + + // If filePath is a valid scene file, open it. Otherwise do nothing. + void tryToOpenScene(const TFilePath &filePath); + +signals: + + void filePathClicked(const TFilePath &); + void filePathDoubleClicked(const TFilePath &); + // reuse the list of TFrameId in order to skip loadInfo() when loading the + // level with sequencial frames. + void filePathsSelected(const std::set &, + const std::list> &); + void treeFolderChanged(const TFilePath &); + + // for activating/deactivating the folder history buttons( back button & + // forward button ) + void historyChanged(bool, bool); + +private: + struct Item { + QString m_name; + qlonglong m_fileSize; + QDateTime m_creationDate; + QDateTime m_modifiedDate; + int m_frameCount; + QString m_fileType; + TFilePath m_path; + bool m_validInfo; + + bool m_isFolder; + bool m_isLink; + // calling loadInfo to the level with sequencial frames is time consuming. + // so keep the list of frameIds at the first time and try to reuse it. + std::vector m_frameIds; + + Item() : m_frameCount(0), m_validInfo(false), m_fileSize(0) {} + Item(const TFilePath &path, bool folder = false, bool link = false, + QString name = QString("")) + : m_path(path) + , m_frameCount(0) + , m_validInfo(false) + , m_fileSize(0) + , m_isFolder(folder) + , m_isLink(link) + , m_name(name) {} + }; + +private: + DvDirTreeView *m_folderTreeView; + QSplitter *m_mainSplitter; + QLineEdit *m_folderName; + DvItemViewer *m_itemViewer; + FrameCountReader m_frameCountReader; + + // folder history + QList m_indexHistoryList; + int m_currentPosition; + int m_currentScroll; + + std::vector m_items; + TFilePath m_folder; + std::string m_dayDateString; + QStringList m_filter; + std::map m_multiFileItemMap; + +private: + void readFrameCount(Item &item); + void readInfo(Item &item); + + void refreshCurrentFolderItems(); + + DvItemListModel::Status getItemVersionControlStatus( + const SceneBrowser::Item &item); +}; + +//----------------------------------------------------------- + +#endif diff --git a/toonz/sources/toonz/scenebrowserversioncontrol.cpp b/toonz/sources/toonz/scenebrowserversioncontrol.cpp new file mode 100644 index 0000000..f21c892 --- /dev/null +++ b/toonz/sources/toonz/scenebrowserversioncontrol.cpp @@ -0,0 +1,594 @@ + + +#include "scenebrowser.h" +#include "filebrowsermodel.h" +#include "versioncontrolgui.h" +#include "versioncontroltimeline.h" +#include "fileselection.h" +#include "dvdirtreeview.h" +#include "tsystem.h" +#include "tapp.h" + +#include "toonz/tscenehandle.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/toonzscene.h" +#include "toonz/levelset.h" +#include "toonzqt/gutil.h" +#include "toonzqt/icongenerator.h" + +namespace { +//--------------------------------------------------------------------------- + +QStringList getLevelFileNames(TFilePath path) { + TFilePath dir = path.getParentDir(); + QDir qDir(QString::fromStdWString(dir.getWideString())); + QString levelName = + QRegExp::escape(QString::fromStdWString(path.getWideName())); + QString levelType = QString::fromStdString(path.getType()); + QString exp(levelName + ".[0-9]{1,4}." + levelType); + QRegExp regExp(exp); + QStringList list = qDir.entryList(QDir::Files); + return list.filter(regExp); +} +} // namespace + +//----------------------------------------------------------------------------- + +DvItemListModel::Status SceneBrowser::getItemVersionControlStatus( + const SceneBrowser::Item &item) { + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + return m_folderTreeView->getItemVersionControlStatus(node, item.m_path); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setupVersionControlCommand(QString &file, QString &path) { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + DvDirVersionControlRootNode *rootNode = node->getVersionControlRootNode(); + + VersionControl *vc = VersionControl::instance(); + if (rootNode) { + vc->setUserName(QString::fromStdWString(rootNode->getUserName())); + vc->setPassword(QString::fromStdWString(rootNode->getPassword())); + } + + path = toQString(node->getPath()); + file = toQString(filePaths[0].withoutParentDir()); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::setupVersionControlCommand(QStringList &files, QString &path, + int &sceneIconsCount) { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + int fileCount = filePaths.size(); + for (int i = 0; i < fileCount; i++) { + TFilePath fp = filePaths[i]; + if (fp.getDots() == "..") { + QStringList levelNames = getLevelFileNames(fp); + + if (levelNames.isEmpty()) { + QString levelName = + QRegExp::escape(QString::fromStdWString(fp.getWideName())); + QString levelType = QString::fromStdString(fp.getType()); + QString exp(levelName + ".[0-9]{1,4}." + levelType); + QRegExp regExp(exp); + levelNames = node->getMissingFiles(regExp); + } + + int count = levelNames.size(); + for (int i = 0; i < count; i++) files.append(levelNames.at(i)); + } else { + QFileInfo fi(toQString(fp)); + files.append(fi.fileName()); + } + + // Add also auxiliary files + if (fp.getDots() == ".." || fp.getType() == "tlv" || + fp.getType() == "pli") { + TFilePathSet fpset; + TXshSimpleLevel::getFiles(fp, fpset); + + TFilePathSet::iterator it; + for (it = fpset.begin(); it != fpset.end(); ++it) + files.append(QFileInfo(toQString(*it)).fileName()); + } + // Add sceneIcon (only for scene files) + else if (fp.getType() == "tnz") { + TFilePath iconPath = ToonzScene::getIconPath(fp); + if (TFileStatus(iconPath).doesExist()) { + QDir dir(toQString(fp.getParentDir())); + +#ifdef MACOSX + files.append(dir.relativeFilePath(toQString(iconPath))); +#else + files.append( + dir.relativeFilePath(toQString(iconPath)).replace("/", "\\")); +#endif + sceneIconsCount++; + } + } + } + + DvDirVersionControlRootNode *rootNode = node->getVersionControlRootNode(); + + if (rootNode) { + VersionControl *vc = VersionControl::instance(); + vc->setUserName(QString::fromStdWString(rootNode->getUserName())); + vc->setPassword(QString::fromStdWString(rootNode->getPassword())); + } + + path = toQString(node->getPath()); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::editVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + + VersionControl *vc = VersionControl::instance(); + + bool hasCurrentSceneFile = false; + + // Check the scene file + TFilePath fp = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getScenePath() + .withoutParentDir(); + if (files.contains(toQString(fp))) hasCurrentSceneFile = true; + + // Check the scene resource files + if (!hasCurrentSceneFile) { + QStringList currentSceneContents = vc->getCurrentSceneContents(); + int fileSize = files.size(); + for (int i = 0; i < fileSize; i++) { +#ifdef MACOSX + QString fp = path + "/" + files.at(i); +#else + QString fp = path + "\\" + files.at(i); +#endif + if (currentSceneContents.contains(fp)) { + hasCurrentSceneFile = true; + break; + } + } + } + + if (hasCurrentSceneFile) { + DVGui::warning( + tr("Some files that you want to edit are currently opened. Close them " + "first.")); + return; + } + + vc->lock(this, path, files, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::editFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + TFilePath fp(TFilePath(path.toStdWString()) + file.toStdWString()); + if (fp.getDots() == "..") { + QStringList list = getLevelFileNames(fp); + VersionControl::instance()->lockFrameRange(this, path, list); + } else { + const std::set &indices = m_itemViewer->getSelectedIndices(); + int frameCount = + m_itemViewer->getModel() + ->getItemData(*indices.begin(), DvItemListModel::FrameCount) + .toInt(); + + VersionControl::instance()->lockFrameRange(this, path, file, frameCount); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::unlockFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + TFilePath fp(TFilePath(path.toStdWString()) + file.toStdWString()); + if (fp.getDots() == "..") { + QStringList list = getLevelFileNames(fp); + VersionControl::instance()->unlockFrameRange(this, path, list); + } else + VersionControl::instance()->unlockFrameRange(this, path, file); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::putFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + VersionControl::instance()->commitFrameRange(this, path, file); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::revertFrameRangeVersionControl() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + TFilePath fp = TFilePath(path.toStdWString()) + file.toStdWString(); + if (fp.getDots() != "..") { + SVNStatus s = node->getVersionControlStatus(file); + + QString from = QString::number(s.m_editFrom); + QString to = QString::number(s.m_editTo); + QString userName = VersionControl::instance()->getUserName(); + QString hostName = TSystem::getHostName(); + + TFilePath fp(s.m_path.toStdWString()); + QString tempFileName = QString::fromStdWString(fp.getWideName()) + "_" + + userName + "_" + hostName + "_" + from + "-" + to + + "." + QString::fromStdString(fp.getType()); + + TFilePath fullPath = node->getPath() + tempFileName.toStdWString(); + + if (TFileStatus(fullPath).doesExist()) + VersionControl::instance()->revertFrameRange(this, path, file, + toQString(fullPath)); + } else { + // Use the hook file, if exist, as a tempFileName + QString tempFile; + + QDir dir(path); + dir.setNameFilters(QStringList("*.xml")); + QStringList list = dir.entryList(QDir::Files | QDir::Hidden); + int listCount = list.size(); + + if (listCount > 0) { + QString prefix = QString::fromStdWString(fp.getWideName()) + "_" + + VersionControl::instance()->getUserName() + "_" + + TSystem::getHostName(); + + for (int i = 0; i < listCount; i++) { + QString str = list.at(i); + if (str.startsWith(prefix)) { + tempFile = str; + tempFile.remove("_hooks"); + tempFile = path + "/" + tempFile; + break; + } + } + } + VersionControl::instance()->revertFrameRange(this, path, file, tempFile); + } +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::updateAndEditVersionControl() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + QStringList files; + int sceneIconsCount = 0; + + TFilePath fp = filePaths[0]; + QFileInfo fi(toQString(fp)); + if (fp.getDots() == "..") { + QStringList levelNames = getLevelFileNames(fp); + int count = levelNames.size(); + for (int i = 0; i < count; i++) files.append(levelNames.at(i)); + } else + files.append(fi.fileName()); + + SVNStatus status = node->getVersionControlStatus(fi.fileName()); + int workingRevision = status.m_workingRevision.toInt(); + + // Add also auxiliary files + if (fp.getDots() == ".." || fp.getType() == "tlv" || fp.getType() == "pli") { + TFilePathSet fpset; + TXshSimpleLevel::getFiles(fp, fpset); + + TFilePathSet::iterator it; + for (it = fpset.begin(); it != fpset.end(); ++it) + files.append(QFileInfo(toQString(*it)).fileName()); + } + // Add sceneIcon (only for scene files) + else if (fp.getType() == "tnz") { + TFilePath iconPath = ToonzScene::getIconPath(fp); + if (TFileStatus(iconPath).doesExist()) { + QDir dir(toQString(fp.getParentDir())); + +#ifdef MACOSX + files.append(dir.relativeFilePath(toQString(iconPath))); +#else + files.append( + dir.relativeFilePath(toQString(iconPath)).replace("/", "\\")); +#endif + sceneIconsCount++; + } + } + + DvDirVersionControlRootNode *rootNode = node->getVersionControlRootNode(); + + VersionControl *vc = VersionControl::instance(); + if (rootNode) { + vc->setUserName(QString::fromStdWString(rootNode->getUserName())); + vc->setPassword(QString::fromStdWString(rootNode->getPassword())); + } + + QString path = toQString(node->getPath()); + + vc->updateAndLock(this, path, files, workingRevision, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::unlockVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + + VersionControl *vc = VersionControl::instance(); + + bool hasCurrentSceneFile = false; + + // Check the scene file + TFilePath fp = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getScenePath() + .withoutParentDir(); + if (files.contains(toQString(fp))) hasCurrentSceneFile = true; + + // Check the scene resource files + if (!hasCurrentSceneFile) { + QStringList currentSceneContents = vc->getCurrentSceneContents(); + int fileSize = files.size(); + for (int i = 0; i < fileSize; i++) { +#ifdef MACOSX + QString fp = path + "/" + files.at(i); +#else + QString fp = path + "\\" + files.at(i); +#endif + if (currentSceneContents.contains(fp)) { + hasCurrentSceneFile = true; + break; + } + } + } + + if (hasCurrentSceneFile) { + DVGui::warning( + tr("Some files that you want to unlock are currently opened. Close " + "them first.")); + return; + } + vc->unlock(this, path, files, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::putVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->commit(this, path, files, false, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::revertVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->revert(this, path, files, false, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::deleteVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->deleteFiles(this, path, files, sceneIconsCount); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->update(this, path, files, sceneIconsCount, false, + false, false); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getRevisionVersionControl() { + QStringList files; + QString path; + + int sceneIconsCount = 0; + setupVersionControlCommand(files, path, sceneIconsCount); + if (files.isEmpty() || path.isEmpty()) return; + VersionControl::instance()->update(this, path, files, sceneIconsCount, false, + true, false); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::getRevisionHistory() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + QStringList files; + int iconAdded; + setupVersionControlCommand(files, path, iconAdded); + + files.removeAt(files.indexOf(file)); + + SVNTimeline *timelineDialog = new SVNTimeline(this, path, file, files); + connect(timelineDialog, SIGNAL(commandDone(const QStringList &)), this, + SLOT(onVersionControlCommandDone(const QStringList &))); + + timelineDialog->show(); + timelineDialog->raise(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::onVersionControlCommandDone(const QStringList &files) { + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + // Refresh the tree view and hence the version control status of each item + m_folderTreeView->refreshVersionControl(node); + + // Get the current scene + ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); + std::vector levels; + scene->getLevelSet()->listLevels(levels); + QMap sceneLevelPaths; + for (int i = 0; i < levels.size(); i++) + sceneLevelPaths[scene->decodeFilePath(levels[i]->getPath())] = levels[i]; + + TLevelSet *levelSetToCheck = new TLevelSet; + + for (int i = 0; i < files.size(); i++) { + TFilePath path = TFilePath(files.at(i).toStdWString()); + if (!path.isAbsolute()) { + TFilePath tempPath = TFilePath(node->getPath()) + path; + if (!TFileStatus(path).doesExist()) { + DvDirVersionControlNode *parent = + dynamic_cast(node->getParent()); + while (parent && !TFileStatus(tempPath).doesExist()) { + tempPath = TFilePath(node->getPath()) + path; + parent = dynamic_cast(parent->getParent()); + } + path = tempPath; + } + } + + if (!TFileStatus(path).doesExist()) continue; + + // Invalidate icons + IconGenerator::instance()->invalidate(path); + + // TODO: Scene checking (could be useful) + + // Level check + if (!sceneLevelPaths.isEmpty() && sceneLevelPaths.contains(path)) { + TXshLevel *level = sceneLevelPaths.value(path); + TXshSimpleLevel *sl = level->getSimpleLevel(); + if (sl) { + levelSetToCheck->insertLevel(sl); + sl->updateReadOnly(); + } + } + } + + VersionControlManager::instance()->setFrameRange(levelSetToCheck, true); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::showLockInformation() { + std::vector filePaths; + FileSelection *fs = + dynamic_cast(m_itemViewer->getPanel()->getSelection()); + if (!fs) return; + fs->getSelectedFiles(filePaths); + if (filePaths.empty()) return; + + DvDirVersionControlNode *node = dynamic_cast( + m_folderTreeView->getCurrentNode()); + if (!node) return; + + QFileInfo fi(toQString(filePaths[0])); + SVNStatus status = node->getVersionControlStatus(fi.fileName()); + + SVNLockInfoDialog *dialog = new SVNLockInfoDialog(this, status); + dialog->show(); + dialog->raise(); +} + +//----------------------------------------------------------------------------- + +void SceneBrowser::showFrameRangeLockInfo() { + QString file; + QString path; + setupVersionControlCommand(file, path); + if (file.isEmpty() || path.isEmpty()) return; + + TFilePath fp(TFilePath(path.toStdWString()) + file.toStdWString()); + if (fp.getDots() == "..") { + QStringList list = getLevelFileNames(fp); + VersionControl::instance()->showFrameRangeLockInfo(this, path, list); + } else + VersionControl::instance()->showFrameRangeLockInfo(this, path, file); +} + +//----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/tpanels.cpp b/toonz/sources/toonz/tpanels.cpp index 70d1082..f3e677b 100644 --- a/toonz/sources/toonz/tpanels.cpp +++ b/toonz/sources/toonz/tpanels.cpp @@ -18,6 +18,7 @@ #include "flipbook.h" #include "castviewer.h" #include "filebrowser.h" +#include "scenebrowser.h" #include "filmstrip.h" #include "previewfxmanager.h" #include "comboviewerpane.h" @@ -1208,6 +1209,23 @@ public: } browserFactory; //============================================================================= +// PreproductionBoardFactory +//----------------------------------------------------------------------------- +class PreproductionBoardFactory final : public TPanelFactory { +public: + PreproductionBoardFactory() : TPanelFactory("PreproductionBoard") {} + void initialize(TPanel *panel) override { + SceneBrowser *browser = new SceneBrowser(panel, 0, false, true); + panel->setWidget(browser); + panel->setWindowTitle(QObject::tr("Preproduction Board")); + TFilePath scenesFolder = + TProjectManager::instance()->getCurrentProject()->getScenesPath(); + browser->setFolder(scenesFolder, true); + browser->enableSingleClickToOpenScenes(); + } +} PreproductionBoardFactory; + +//============================================================================= // CastViewerFactory //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/icongenerator.cpp b/toonz/sources/toonzqt/icongenerator.cpp index d0c4c5e..fc9a039 100644 --- a/toonz/sources/toonzqt/icongenerator.cpp +++ b/toonz/sources/toonzqt/icongenerator.cpp @@ -1086,9 +1086,9 @@ Qt::transparent) bbox = (bbox * icon->getBounds()) .enlarge(-1); // Add a 1 pixel transparent margin - this - if (bbox.getLx() > 0 && - bbox.getLy() > 0) // way the actual content doesn't look trimmed. - ::makeChessBackground(icon->extract(bbox)); + //if (bbox.getLx() > 0 && + // bbox.getLy() > 0) // way the actual content doesn't look trimmed. + // ::makeChessBackground(icon->extract(bbox)); } else icon->fill(TPixel32(255, 0, 0));