2434 lines
80 KiB
C++
2434 lines
80 KiB
C++
|
|
|
|
#include "filebrowser.h"
|
|
|
|
// Tnz6 includes
|
|
#include "dvdirtreeview.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 <QDrag>
|
|
#include <QDragEnterEvent>
|
|
#include <QDragMoveEvent>
|
|
#include <QDropEvent>
|
|
#include <QDragLeaveEvent>
|
|
#include <QBoxLayout>
|
|
#include <QLabel>
|
|
#include <QByteArray>
|
|
#include <QMenu>
|
|
#include <QDateTime>
|
|
#include <QInputDialog>
|
|
#include <QDesktopServices>
|
|
#include <QDirModel>
|
|
#include <QDir>
|
|
#include <QPixmap>
|
|
#include <QUrl>
|
|
#include <QRegExp>
|
|
#include <QScrollBar>
|
|
#include <QMap>
|
|
#include <QPushButton>
|
|
#include <QPalette>
|
|
#include <QCheckBox>
|
|
#include <QMutex>
|
|
#include <QMutexLocker>
|
|
#include <QMessageBox>
|
|
#include <QApplication>
|
|
#include <QFormLayout>
|
|
#include <QMainWindow>
|
|
#include <QLineEdit>
|
|
#include <QTreeWidgetItem>
|
|
#include <QSplitter>
|
|
#include <QFileSystemWatcher>
|
|
|
|
// tcg includes
|
|
#include "tcg/boost/range_utility.h"
|
|
#include "tcg/boost/permuted_range.h"
|
|
|
|
// boost includes
|
|
#include <boost/bind.hpp>
|
|
#include <boost/iterator/counting_iterator.hpp>
|
|
#include <boost/range/adaptor/filtered.hpp>
|
|
#include <boost/range/adaptor/transformed.hpp>
|
|
|
|
namespace ba = boost::adaptors;
|
|
|
|
using namespace DVGui;
|
|
|
|
//=============================================================================
|
|
// Local declarations
|
|
//=============================================================================
|
|
|
|
//============================
|
|
// FrameCountTask class
|
|
//----------------------------
|
|
|
|
class FrameCountTask final : public TThread::Runnable {
|
|
bool m_started;
|
|
|
|
TFilePath m_path;
|
|
QDateTime m_modifiedDate;
|
|
|
|
public:
|
|
FrameCountTask(const TFilePath &path, const QDateTime &modifiedDate);
|
|
|
|
~FrameCountTask();
|
|
|
|
void run() override;
|
|
|
|
QThread::Priority runningPriority() override;
|
|
|
|
public slots:
|
|
|
|
void onStarted(TThread::RunnableP thisTask) override;
|
|
void onCanceled(TThread::RunnableP thisTask) override;
|
|
};
|
|
|
|
//============================
|
|
// FCData struct
|
|
//----------------------------
|
|
|
|
struct FCData {
|
|
QDateTime m_date;
|
|
int m_frameCount;
|
|
bool m_underProgress;
|
|
int m_retryCount;
|
|
|
|
FCData() {}
|
|
FCData(const QDateTime &date);
|
|
};
|
|
|
|
//=============================================================================
|
|
// Local namespace
|
|
//=============================================================================
|
|
|
|
namespace {
|
|
std::set<FileBrowser *> activeBrowsers;
|
|
std::map<TFilePath, FCData> frameCountMap;
|
|
QMutex frameCountMapMutex;
|
|
QMutex levelFileMutex;
|
|
|
|
} // namespace
|
|
|
|
inline bool isMultipleFrameType(std::string type) {
|
|
return (type == "tlv" || type == "tzl" || type == "pli" || type == "avi" ||
|
|
type == "gif" || type == "mp4" || type == "webm" || type == "mov");
|
|
}
|
|
|
|
//=============================================================================
|
|
// FileBrowser
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if QT_VERSION >= 0x050500
|
|
FileBrowser::FileBrowser(QWidget *parent, Qt::WindowFlags flags,
|
|
bool noContextMenu, bool multiSelectionEnabled)
|
|
#else
|
|
FileBrowser::FileBrowser(QWidget *parent, Qt::WFlags flags, bool noContextMenu,
|
|
bool multiSelectionEnabled)
|
|
#endif
|
|
: QFrame(parent), m_folderName(0), m_itemViewer(0) {
|
|
// style sheet
|
|
setObjectName("FileBrowser");
|
|
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);
|
|
DvItemViewerButtonBar *buttonBar =
|
|
new DvItemViewerButtonBar(m_itemViewer, box);
|
|
DvItemViewerPanel *viewerPanel = m_itemViewer->getPanel();
|
|
|
|
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("FileBrowserSplitter");
|
|
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(m_mainSplitter, 1);
|
|
}
|
|
setLayout(mainLayout);
|
|
|
|
m_mainSplitter->setSizes(QList<int>() << 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<int> &)),
|
|
this, SLOT(onSelectedItems(const std::set<int> &)));
|
|
ret = ret && connect(buttonBar, SIGNAL(folderUp()), this, SLOT(folderUp()));
|
|
ret = ret && connect(buttonBar, SIGNAL(newFolder()), this, SLOT(newFolder()));
|
|
|
|
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()));
|
|
ret = ret && connect(buttonBar, SIGNAL(folderBack()), this,
|
|
SLOT(onBackButtonPushed()));
|
|
ret = ret && connect(buttonBar, SIGNAL(folderFwd()), this,
|
|
SLOT(onFwdButtonPushed()));
|
|
// when the history changes, enable/disable the history buttons accordingly
|
|
ret = ret && connect(this, SIGNAL(historyChanged(bool, bool)), buttonBar,
|
|
SLOT(onHistoryChanged(bool, bool)));
|
|
|
|
// 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 &)));
|
|
|
|
// store the first item("Root") in the history
|
|
m_indexHistoryList.append(m_folderTreeView->currentIndex());
|
|
m_currentPosition = 0;
|
|
|
|
refreshHistoryButtons();
|
|
|
|
assert(ret);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
FileBrowser::~FileBrowser() {}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/*! when the m_folderName is edited, move the current folder accordingly
|
|
*/
|
|
void FileBrowser::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 FileBrowser::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 FileBrowser::refreshHistoryButtons() {
|
|
emit historyChanged((m_currentPosition != 0),
|
|
(m_currentPosition != m_indexHistoryList.size() - 1));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::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 FileBrowser::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 FileBrowser::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 FileBrowser::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 FileBrowser::sortByDataModel(DataType dataType, bool isDiscendent) {
|
|
struct locals {
|
|
static inline bool itemLess(int aIdx, int bIdx, FileBrowser &fb,
|
|
DataType dataType) {
|
|
return (fb.compareData(dataType, aIdx, bIdx) > 0);
|
|
}
|
|
|
|
static inline bool indexLess(int aIdx, int bIdx,
|
|
const std::vector<int> &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<int> 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<Item>(
|
|
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<FileSelection *>(m_itemViewer->getPanel()->getSelection());
|
|
|
|
if (!fs->isEmpty()) {
|
|
std::vector<int> 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<int> newSelectedIndices;
|
|
tcg::substitute(
|
|
newSelectedIndices,
|
|
tcg::permuted_range(old2NewIdx, fs->getSelectedIndices() |
|
|
ba::filtered(boost::bind(
|
|
std::less<int>(), _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<FileSelection *>(m_itemViewer->getPanel()->getSelection());
|
|
|
|
if (!fs->isEmpty()) {
|
|
int iCount = int(m_items.size()), lastIdx = iCount - 1;
|
|
|
|
std::vector<int> newSelectedIndices;
|
|
tcg::substitute(
|
|
newSelectedIndices,
|
|
fs->getSelectedIndices() |
|
|
ba::filtered(boost::bind(std::less<int>(), _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 FileBrowser::setFilterTypes(const QStringList &types) { m_filter = types; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::addFilterType(const QString &type) {
|
|
if (!m_filter.contains(type)) m_filter.push_back(type);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::removeFilterType(const QString &type) {
|
|
m_filter.removeAll(type);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::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 all folder items by using the folde tree model
|
|
DvDirModel *model = DvDirModel::instance();
|
|
QModelIndex currentIndex = model->getIndexByPath(m_folder);
|
|
if (currentIndex.isValid()) {
|
|
for (int i = 0; i < model->rowCount(currentIndex); i++) {
|
|
QModelIndex tmpIndex = model->index(i, 0, currentIndex);
|
|
if (tmpIndex.isValid()) {
|
|
DvDirModelFileFolderNode *node =
|
|
dynamic_cast<DvDirModelFileFolderNode *>(model->getNode(tmpIndex));
|
|
if (node) {
|
|
TFilePath childFolderPath = node->getPath();
|
|
if (TFileStatus(childFolderPath).isLink())
|
|
m_items.push_back(Item(childFolderPath, true, true,
|
|
QString::fromStdWString(node->getName())));
|
|
else
|
|
m_items.push_back(Item(childFolderPath, true, false,
|
|
QString::fromStdWString(node->getName())));
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
setUnregisteredFolder(m_folder);
|
|
|
|
// 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) {
|
|
#ifdef _WIN32
|
|
// include folder shortcut items
|
|
if (it->getType() == "lnk") {
|
|
TFileStatus info(*it);
|
|
if (info.isLink() && info.isDirectory()) {
|
|
m_items.push_back(
|
|
Item(*it, true, true, QString::fromStdString((*it).getName())));
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
// 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" &&
|
|
it->getType() != "macrofx" && it->getType() != "plugin" &&
|
|
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;
|
|
try {
|
|
tFrameId = it->getFrame();
|
|
} catch (TMalformedFrameException tmfe) {
|
|
// Incorrect frame name sequence. Warning to the user in the message
|
|
// center.
|
|
DVGui::warning(QString::fromStdWString(
|
|
tmfe.getMessage() + L": " +
|
|
QObject::tr("Skipping frame.").toStdWString()));
|
|
continue;
|
|
}
|
|
|
|
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<DvDirVersionControlNode *>(
|
|
m_folderTreeView->getCurrentNode());
|
|
if (node) {
|
|
QList<TFilePath> 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 FileBrowser::setFolder(const TFilePath &fp, bool expandNode,
|
|
bool forceUpdate, bool collapseAll) {
|
|
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 (collapseAll) m_folderTreeView->collapseAll();
|
|
if (!TFileStatus(fp).isLink())
|
|
m_folderTreeView->setCurrentNode(fp, expandNode);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/*! process when inputting the folder which is not registered in the folder tree
|
|
(e.g. UNC path in Windows)
|
|
*/
|
|
void FileBrowser::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) {
|
|
#ifdef _WIN32
|
|
// include folder shortcut items
|
|
if (it->getType() == "lnk") {
|
|
TFileStatus info(*it);
|
|
if (info.isLink() && info.isDirectory()) {
|
|
m_items.push_back(
|
|
Item(*it, true, true, QString::fromStdString((*it).getName())));
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
// 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 FileBrowser::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<TFilePath> files;
|
|
day->getFiles(files);
|
|
std::vector<TFilePath>::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 FileBrowser::refreshData() {
|
|
std::vector<Item>::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 FileBrowser::getItemCount() const { return m_items.size(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::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<TFilePath, Item>::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 FileBrowser::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 FileBrowser::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 parent 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(getIconThemePath("actions/60/folder_browser_up.svg"),
|
|
iconSize, Qt::KeepAspectRatio));
|
|
return folderUpPixmap;
|
|
}
|
|
// folder icons
|
|
else if (item.m_isFolder) {
|
|
if (item.m_isLink) {
|
|
static QPixmap folderLinkPixmap(
|
|
svgToPixmap(getIconThemePath("actions/60/folder_browser_link.svg"),
|
|
iconSize, Qt::KeepAspectRatio));
|
|
return folderLinkPixmap;
|
|
} else {
|
|
static QPixmap folderPixmap(
|
|
svgToPixmap(getIconThemePath("actions/60/folder_browser.svg"),
|
|
iconSize, Qt::KeepAspectRatio));
|
|
return folderPixmap;
|
|
}
|
|
}
|
|
|
|
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("<LINK>");
|
|
else if (item.m_isFolder)
|
|
return QString("<DIR>");
|
|
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 FileBrowser::isSceneItem(int index) const {
|
|
return 0 <= index && index < (int)m_items.size() &&
|
|
m_items[index].m_path.getType() == "tnz";
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool FileBrowser::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 FileBrowser::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 FileBrowser::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 FileBrowser::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 *FileBrowser::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<FileSelection *>(m_itemViewer->getPanel()->getSelection());
|
|
if (!fs) return 0;
|
|
std::vector<TFilePath> 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-Scene");
|
|
else
|
|
title = tr("Load");
|
|
QAction *action = new QAction(QIcon(createQIcon("import")), 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(QIcon(createQIcon("rename")), 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<DvDirVersionControlNode *>(
|
|
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 FileBrowser::startDragDrop() {
|
|
TRepetitionGuard guard;
|
|
if (!guard.hasLock()) return;
|
|
|
|
FileSelection *fs =
|
|
dynamic_cast<FileSelection *>(m_itemViewer->getPanel()->getSelection());
|
|
if (!fs) return;
|
|
std::vector<TFilePath> files;
|
|
fs->getSelectedFiles(files);
|
|
if (files.empty()) return;
|
|
|
|
QList<QUrl> 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 FileBrowser::dropMimeData(QTreeWidgetItem *parent, int index,
|
|
const QMimeData *data, Qt::DropAction action) {
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::onTreeFolderChanged() {
|
|
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<DvDirModelFileFolderNode *>(node);
|
|
if (fileFolderNode) emit treeFolderChanged(fileFolderNode->getPath());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::changeFolder(const QModelIndex &index) {}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::onDataChanged(const QModelIndex &topLeft,
|
|
const QModelIndex &bottomRight) {
|
|
onTreeFolderChanged();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool FileBrowser::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 FileBrowser::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<const CastItems *>(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<TFilmstripSelection *>(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<TFrameId> 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 FileBrowser::loadResources() {
|
|
FileSelection *fs =
|
|
dynamic_cast<FileSelection *>(m_itemViewer->getPanel()->getSelection());
|
|
if (!fs) return;
|
|
|
|
std::vector<TFilePath> filePaths;
|
|
fs->getSelectedFiles(filePaths);
|
|
|
|
if (filePaths.empty()) return;
|
|
|
|
IoCmd::LoadResourceArguments args;
|
|
args.resourceDatas.assign(filePaths.begin(), filePaths.end());
|
|
|
|
IoCmd::loadResources(args);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void RenameAsToonzPopup::onOk() {
|
|
if (!isValidFileName(m_name->text())) {
|
|
DVGui::error(
|
|
tr("The file name cannot be empty or contain any of the following "
|
|
"characters:(new line) \\ / : * ? \" |"));
|
|
return;
|
|
}
|
|
if (isReservedFileName_message(m_name->text())) return;
|
|
accept();
|
|
}
|
|
|
|
RenameAsToonzPopup::RenameAsToonzPopup(const QString name, int frames)
|
|
: Dialog(TApp::instance()->getMainWindow(), true, true, "RenameAsToonz") {
|
|
setWindowTitle(QString(tr("Rename")));
|
|
|
|
beginHLayout();
|
|
|
|
QLabel *lbl;
|
|
if (frames == -1)
|
|
lbl = new QLabel(QString(tr("Renaming File ")) + name);
|
|
else
|
|
lbl = new QLabel(
|
|
QString(tr("Creating an animation level of %1 frames").arg(frames)));
|
|
lbl->setFixedHeight(20);
|
|
lbl->setObjectName("TitleTxtLabel");
|
|
|
|
m_name = new LineEdit(frames == -1 ? "" : name);
|
|
m_name->setFixedHeight(20);
|
|
// connect(m_name, SIGNAL(editingFinished()), SLOT(onNameChanged()));
|
|
// addWidget(tr("Level Name:"),m_name);
|
|
|
|
m_overwrite = new QCheckBox(tr("Delete Original Files"));
|
|
m_overwrite->setFixedHeight(20);
|
|
// addWidget(m_overwrite, false);
|
|
|
|
QFormLayout *formLayout = new QFormLayout;
|
|
|
|
QHBoxLayout *labelLayout = new QHBoxLayout;
|
|
labelLayout->addStretch();
|
|
labelLayout->addWidget(lbl);
|
|
labelLayout->addStretch();
|
|
|
|
formLayout->addRow(labelLayout);
|
|
formLayout->addRow(tr("Level Name:"), m_name);
|
|
formLayout->addRow(m_overwrite);
|
|
|
|
addLayout(formLayout);
|
|
|
|
endHLayout();
|
|
|
|
m_okBtn = new QPushButton(tr("Rename"), this);
|
|
m_okBtn->setDefault(true);
|
|
m_cancelBtn = new QPushButton(tr("Cancel"), this);
|
|
connect(m_okBtn, SIGNAL(clicked()), this, SLOT(onOk()));
|
|
connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
|
|
addButtonBarWidget(m_okBtn, m_cancelBtn);
|
|
}
|
|
|
|
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 FileBrowser::renameAsToonzLevel() {
|
|
std::vector<TFilePath> filePaths;
|
|
FileSelection *fs =
|
|
dynamic_cast<FileSelection *>(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 FileBrowser::convertToUnpaintedTlv() {
|
|
std::vector<TFilePath> filePaths;
|
|
FileSelection *fs =
|
|
dynamic_cast<FileSelection *>(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<Convert2Tlv *> 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();
|
|
FileBrowser::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"));
|
|
|
|
FileBrowser::refreshFolder(filePaths[0].getParentDir());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::convertToPaintedTlv() {
|
|
std::vector<TFilePath> filePaths;
|
|
FileSelection *fs =
|
|
dynamic_cast<FileSelection *>(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();
|
|
FileBrowser::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();
|
|
FileBrowser::refreshFolder(filePaths[0].getParentDir());
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::onSelectedItems(const std::set<int> &indexes) {
|
|
std::set<TFilePath> filePaths;
|
|
std::set<int>::const_iterator it;
|
|
|
|
// pass the frameId list for reuse
|
|
std::list<std::vector<TFrameId>> frameIDs;
|
|
|
|
if (indexes.empty()) { // inform selection is released
|
|
emit filePathsSelected(filePaths, frameIDs);
|
|
return;
|
|
}
|
|
|
|
for (it = indexes.begin(); it != indexes.end(); ++it) {
|
|
if (*it < m_items.size()) {
|
|
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 sequential frames.
|
|
emit filePathsSelected(filePaths, frameIDs);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::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 FileBrowser::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 if (TFileType::isViewable(TFileType::getInfo(fp))) {
|
|
CommandManager::instance()->execute(MI_ViewFile);
|
|
} else
|
|
emit filePathDoubleClicked(fp);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::refreshFolder(const TFilePath &folderPath) {
|
|
std::set<FileBrowser *>::iterator it;
|
|
for (it = activeBrowsers.begin(); it != activeBrowsers.end(); ++it) {
|
|
FileBrowser *browser = *it;
|
|
DvDirModel::instance()->refreshFolder(folderPath);
|
|
if (browser->getFolder() == folderPath) {
|
|
browser->setFolder(folderPath, false, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::updateItemViewerPanel() {
|
|
std::set<FileBrowser *>::iterator it;
|
|
for (it = activeBrowsers.begin(); it != activeBrowsers.end(); ++it) {
|
|
FileBrowser *browser = *it;
|
|
browser->m_itemViewer->getPanel()->update();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::getExpandedFolders(DvDirModelNode *node,
|
|
QList<DvDirModelNode *> &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 FileBrowser::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<DvDirModelNode *> 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 FileBrowser::folderUp() {
|
|
QModelIndex index = m_folderTreeView->currentIndex();
|
|
if (!index.isValid() || !index.parent().isValid()) {
|
|
// cannot go up tree view, so try going to parent directory
|
|
TFilePath parentFp = m_folder.getParentDir();
|
|
if (parentFp != TFilePath("") && parentFp != m_folder) {
|
|
setFolder(parentFp, true);
|
|
}
|
|
return;
|
|
}
|
|
m_folderTreeView->setCurrentIndex(index.parent());
|
|
m_folderTreeView->scrollTo(index.parent());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::newFolder() {
|
|
TFilePath parentFolder = getFolder();
|
|
if (parentFolder == TFilePath() || !TFileStatus(parentFolder).isDirectory())
|
|
return;
|
|
QString tempName(tr("New Folder"));
|
|
std::wstring folderName = tempName.toStdWString();
|
|
TFilePath folderPath = parentFolder + folderName;
|
|
int i = 1;
|
|
while (TFileStatus(folderPath).doesExist())
|
|
folderPath = parentFolder + (folderName + L" " + std::to_wstring(++i));
|
|
|
|
try {
|
|
TSystem::mkDir(folderPath);
|
|
|
|
} catch (...) {
|
|
DVGui::error(tr("It is not possible to create the %1 folder.")
|
|
.arg(toQString(folderPath)));
|
|
return;
|
|
}
|
|
|
|
DvDirModel *model = DvDirModel::instance();
|
|
|
|
QModelIndex parentFolderIndex = m_folderTreeView->currentIndex();
|
|
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 FileBrowser::showEvent(QShowEvent *) {
|
|
activeBrowsers.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<DvDirVersionControlNode *>(
|
|
m_folderTreeView->getCurrentNode());
|
|
if (vcNode) m_folderTreeView->refreshVersionControl(vcNode);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::hideEvent(QHideEvent *) {
|
|
activeBrowsers.erase(this);
|
|
m_itemViewer->getPanel()->getItemViewPlayDelegate()->resetPlayWidget();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::makeCurrentProjectVisible() {}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::enableGlobalSelection(bool enabled) {
|
|
m_folderTreeView->enableGlobalSelection(enabled);
|
|
m_itemViewer->enableGlobalSelection(enabled);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::selectNone() { m_itemViewer->selectNone(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::enableDoubleClickToOpenScenes() {
|
|
// perhaps this should disconnect existing signal handlers first
|
|
connect(this, SIGNAL(filePathDoubleClicked(const TFilePath &)), this,
|
|
SLOT(tryToOpenScene(const TFilePath &)));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FileBrowser::tryToOpenScene(const TFilePath &filePath) {
|
|
if (filePath.getType() == "tnz") {
|
|
IoCmd::loadScene(filePath);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// FCData methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
FCData::FCData(const QDateTime &date)
|
|
: m_date(date), m_frameCount(0), m_underProgress(true), m_retryCount(1) {}
|
|
|
|
//=============================================================================
|
|
// FrameCountReader methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
FrameCountReader::FrameCountReader() : m_executor() {
|
|
m_executor.setMaxActiveTasks(2);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
FrameCountReader::~FrameCountReader() {}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int FrameCountReader::getFrameCount(const TFilePath &fp) {
|
|
QDateTime modifiedDate =
|
|
QFileInfo(QString::fromStdWString(fp.getWideString())).lastModified();
|
|
std::map<TFilePath, FCData>::iterator it;
|
|
|
|
{
|
|
// Access the static map to find an occurrence of the path.
|
|
QMutexLocker locker(&frameCountMapMutex);
|
|
it = frameCountMap.find(fp);
|
|
|
|
if (it != frameCountMap.end()) {
|
|
if (it->second.m_frameCount > 0 && it->second.m_date == modifiedDate) {
|
|
// Found an unmodified occurrence with correctly calculated frame count
|
|
return it->second.m_frameCount;
|
|
}
|
|
} else {
|
|
// First time this frame count is calculated - initialize FC data
|
|
frameCountMap[fp] = FCData(modifiedDate);
|
|
goto calculateTask;
|
|
}
|
|
|
|
if ((modifiedDate == it->second.m_date) &&
|
|
(it->second.m_underProgress || it->second.m_retryCount < 0))
|
|
return -1;
|
|
}
|
|
|
|
calculateTask:
|
|
|
|
// Now, we have to calculate the frame count; first, create a frame count
|
|
// calculation task and submit it.
|
|
FrameCountTask *task = new FrameCountTask(fp, modifiedDate);
|
|
connect(task, SIGNAL(finished(TThread::RunnableP)), this,
|
|
SIGNAL(calculatedFrameCount()));
|
|
connect(task, SIGNAL(exception(TThread::RunnableP)), this,
|
|
SIGNAL(calculatedFrameCount()));
|
|
|
|
m_executor.addTask(task);
|
|
|
|
return -1; // FrameCount has not yet been calculated
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline void FrameCountReader::stopReading() { m_executor.cancelAll(); }
|
|
|
|
//=============================================================================
|
|
// FrameCountTask methods
|
|
//-----------------------------------------------------------------------------
|
|
|
|
FrameCountTask::FrameCountTask(const TFilePath &path,
|
|
const QDateTime &modifiedDate)
|
|
: m_path(path), m_modifiedDate(modifiedDate), m_started(false) {
|
|
connect(this, SIGNAL(started(TThread::RunnableP)), this,
|
|
SLOT(onStarted(TThread::RunnableP)));
|
|
connect(this, SIGNAL(canceled(TThread::RunnableP)), this,
|
|
SLOT(onCanceled(TThread::RunnableP)));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
FrameCountTask::~FrameCountTask() {}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FrameCountTask::run() {
|
|
TLevelReaderP lr(m_path);
|
|
int frameCount = lr->loadInfo()->getFrameCount();
|
|
|
|
QMutexLocker fCMapMutex(&frameCountMapMutex);
|
|
|
|
std::map<TFilePath, FCData>::iterator it = frameCountMap.find(m_path);
|
|
|
|
if (it == frameCountMap.end()) return;
|
|
|
|
// Memorize the found frameCount into the frameCountMap
|
|
if (frameCount > 0) {
|
|
it->second.m_frameCount = frameCount;
|
|
it->second.m_date = m_modifiedDate;
|
|
} else {
|
|
// Seems that tlv reads sometimes may fail, returning invalid frame counts
|
|
// (typically 0).
|
|
// However, if no exception was thrown, we try to recover it
|
|
it->second.m_underProgress = false;
|
|
it->second.m_retryCount--;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
QThread::Priority FrameCountTask::runningPriority() {
|
|
return QThread::LowPriority;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// NOTE: onStarted and onCanceled are invoked on the same thread - so m_started
|
|
// operations are serialized, it can be non-thread-guarded.
|
|
void FrameCountTask::onStarted(TThread::RunnableP thisTask) {
|
|
m_started = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FrameCountTask::onCanceled(TThread::RunnableP thisTask) {
|
|
if (!m_started) {
|
|
QMutexLocker fCMapMutex(&frameCountMapMutex);
|
|
|
|
frameCountMap.erase(m_path);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
OpenFloatingPanel openBrowserPane(MI_OpenFileBrowser, "Browser",
|
|
QObject::tr("File Browser"));
|