From 062c3a84d597b0a52c240a513537b1c6ba1c0987 Mon Sep 17 00:00:00 2001 From: Krolock Date: Sun, 4 Mar 2012 19:09:41 +0100 Subject: [PATCH] Added: creating new bnp files Fixed: some bugs caused crashes during drag&drop --- .../bnp_manager/bnp_dirtree_dialog.cpp | 4 +- .../plugins/bnp_manager/bnp_dirtree_form.ui | 3 + .../src/plugins/bnp_manager/bnp_file.cpp | 15 +- .../src/plugins/bnp_manager/bnp_file.h | 6 + .../bnp_manager/bnp_filelist_dialog.cpp | 29 +--- .../plugins/bnp_manager/bnp_filelist_dialog.h | 8 +- .../bnp_manager/bnp_manager_constants.h | 1 + .../bnp_manager/bnp_manager_window.cpp | 157 +++++++++++++++++- .../plugins/bnp_manager/bnp_manager_window.h | 13 +- .../plugins/bnp_manager/images/ic_nel_new.png | Bin 0 -> 4035 bytes 10 files changed, 205 insertions(+), 31 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_new.png diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp index 78cc2f3cd..a19f4550e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp @@ -40,11 +40,12 @@ CBnpDirTreeDialog::CBnpDirTreeDialog(QString bnpPath, QWidget *parent) // Bnp file: opened and displayed // all other files: added to the currently opened bnp file QStringList filter; - //filter << tr("*.bnp"); + filter << tr("*.bnp"); // Setup the directory tree model m_dirModel= new BNPFileSystemModel(); m_proxyModel = new BNPSortProxyModel(); + m_ui.dirTree->setSortingEnabled(true); m_dirModel->setRootPath(m_DataPath); m_dirModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::AllEntries); m_dirModel->setNameFilters(filter); @@ -55,7 +56,6 @@ CBnpDirTreeDialog::CBnpDirTreeDialog(QString bnpPath, QWidget *parent) m_ui.dirTree->setModel(m_proxyModel); m_ui.dirTree->setRootIndex( m_proxyModel->mapFromSource (m_dirModel->index(m_DataPath) ) ); - m_ui.dirTree->setSortingEnabled(true); // Trigger if one filename is activated // In future drag&drop should be also possible diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui index 751c4f055..44b39dc54 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui @@ -44,6 +44,9 @@ 0 + + true + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp index d9b2f45c1..e00f777ba 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -62,6 +62,15 @@ void BNPFileHandle::releaseInstance() } } // *************************************************************************** +void BNPFileHandle::createFile(string filePath) +{ + // Only set the filepath. Header will be created after files have been added + m_openedBNPFile = filePath; + m_packedFiles.clear(); + + nlinfo("Created file %s.", filePath.c_str() ); +} +// *************************************************************************** bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) { CIFile bnp; @@ -231,8 +240,10 @@ void BNPFileHandle::addFiles( const vector &filePathes) } writeHeader(m_openedBNPFile + ".tmp", OffsetFromBegining); - - CFile::deleteFile( m_openedBNPFile ); + + // Delete any previous existing file + if (CFile::fileExists( m_openedBNPFile )) + CFile::deleteFile( m_openedBNPFile ); string src = m_openedBNPFile + ".tmp"; CFile::moveFile( m_openedBNPFile.c_str(), src.c_str() ); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h index e03d0e664..7527b5dbd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h @@ -88,6 +88,12 @@ public: */ void fileNames( std::vector& fileNames ); + /** + * Create a new bnp file + * \param string file path + */ + void createFile( std::string filePath ); + /** * Add files to the current aktive bnp file * \param vector of file pathes to add diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp index 418d7fa04..261175902 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp @@ -20,10 +20,6 @@ // Qt includes #include -#include -#include -#include -#include // NeL includes #include @@ -38,7 +34,6 @@ BnpFileListDialog::BnpFileListDialog(QString bnpPath, QWidget *parent) m_DataPath(bnpPath) { m_ui.setupUi(this); - setAcceptDrops(true); } // *************************************************************************** BnpFileListDialog::~BnpFileListDialog() @@ -111,6 +106,14 @@ bool BnpFileListDialog::loadTable(const QString filePath) return true; } // *************************************************************************** +void BnpFileListDialog::clearTable() +{ + // create emtpy table + setupTable(0); + + setWindowTitle("BNP File List"); +} +// *************************************************************************** void BnpFileListDialog::getSelections(TSelectionList& SelectionList) { QModelIndex index; @@ -125,21 +128,5 @@ void BnpFileListDialog::getSelections(TSelectionList& SelectionList) SelectionList.push_back( filename.toStdString() ); } } -// *************************************************************************** -void BnpFileListDialog::dragEnterEvent(QDragEnterEvent *event) -{ - // Accept only one file - // In the future a tabbed FileListDialog would accept more - if ( event->mimeData()->hasUrls() && event->mimeData()->urls().count() == 1) - event->acceptProposedAction(); -} -// *************************************************************************** -void BnpFileListDialog::dropEvent(QDropEvent *event) - { - // Excraft the local file url from the drop object and fill the table - const QMimeData *mimeData = event->mimeData(); - QList urlList = mimeData->urls(); - loadTable( urlList.first().toLocalFile() ); - } } // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h index 5b5491d8f..dbf007fc2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h @@ -59,6 +59,11 @@ public: */ void setupTable(int nbrows); + /** + * When BNP files is closed, clear the filelist table + */ + void clearTable(); + /** * Fill the files selected in the table view to * unpack them. @@ -67,9 +72,6 @@ public: */ void getSelections(TSelectionList& SelectionList); -protected: - void dragEnterEvent (QDragEnterEvent *event); - void dropEvent(QDropEvent *event); private: Ui::BnpFileListDialog m_ui; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h index 7ec5eecd0..609305b7e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h @@ -25,6 +25,7 @@ namespace Constants const char * const BNP_MANAGER_SECTION = "BNPManager"; //resources +const char *const ICON_NEW = ":/images/ic_nel_new.png"; const char *const ICON_ADD = ":/images/ic_nel_add_item.png"; const char *const ICON_DELETE = ":/images/ic_nel_delete_item.png"; const char *const ICON_UNPACK = ":/images/ic_nel_export.png"; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp index 4022931d1..3990e70ce 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp @@ -30,6 +30,10 @@ #include #include +// STL includes +#include +#include + // Qt includes #include #include @@ -37,6 +41,11 @@ #include #include #include +#include +#include +#include +#include +#include using namespace std; using namespace NLMISC; @@ -53,6 +62,8 @@ BNPManagerWindow::BNPManagerWindow(QWidget *parent) setCentralWidget(hideWidget); hideWidget->hide(); + setAcceptDrops(true); + // Read the settings readSettings(); @@ -98,6 +109,12 @@ void BNPManagerWindow::createDialogs() // *************************************************************************** void BNPManagerWindow::createActions() { + // new action + m_newAction = new QAction(tr("&New..."), this); + m_newAction->setIcon(QIcon(Core::Constants::ICON_NEW)); + m_newAction->setStatusTip(tr("New file")); + connect(m_newAction, SIGNAL(triggered()), this, SLOT( newFile() )); + // open action m_openAction = new QAction(tr("&Open..."), this); m_openAction->setIcon(QIcon(Core::Constants::ICON_OPEN)); @@ -132,6 +149,7 @@ void BNPManagerWindow::createActions() void BNPManagerWindow::createToolBars() { m_fileToolBar = addToolBar(tr("&File")); + m_fileToolBar->addAction(m_newAction); m_fileToolBar->addAction(m_openAction); m_fileToolBar->addAction(m_closeAction); @@ -143,10 +161,36 @@ void BNPManagerWindow::createToolBars() // *************************************************************************** bool BNPManagerWindow::loadFile(const QString fileName) { + // Store the filename for later use + m_openedBNPFile = fileName; m_BnpFileListDialog->loadTable(fileName); return true; } // *************************************************************************** +void BNPManagerWindow::newFile() +{ + // reference to the BNPFileHandle singletone instance + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + + m_openedBNPFile = ""; + m_BnpFileListDialog->clearTable(); + + QString filePath = QFileDialog::getSaveFileName(this, tr("Create File"),QDir::currentPath(), + tr("BNP File (*.bnp)")); + + if (filePath.isEmpty() ) + return; + + if ( !filePath.endsWith(".bnp", Qt::CaseInsensitive) ) + filePath.append(".bnp"); + + m_openedBNPFile = filePath; + m_BnpFileListDialog->setWindowTitle (filePath); + + myBNPFileHandle.createFile ( filePath.toStdString() ); + +} +// *************************************************************************** void BNPManagerWindow::open() { QString fileName; @@ -158,13 +202,13 @@ void BNPManagerWindow::open() if (fileName.isNull()) return; - m_openedBNPFile = fileName; loadFile(fileName); } // *************************************************************************** void BNPManagerWindow::close() { - //TODO + m_openedBNPFile = ""; + m_BnpFileListDialog->clearTable(); } // *************************************************************************** void BNPManagerWindow::addFiles() @@ -216,6 +260,49 @@ void BNPManagerWindow::addFiles() loadFile(m_openedBNPFile); } // *************************************************************************** +void BNPManagerWindow::addFiles( QStringList FileList ) +{ + // reference to the BNPFileHandle singletone instance + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + + // vector of all current packed filenames + vector currentFiles; + + // vector of files to add + vector addFiles; + + // get all current filenames from the opened bnp file + myBNPFileHandle.fileNames(currentFiles); + + QStringList::iterator it_list = FileList.begin(); + while (it_list != FileList.end() ) + { + string fileName = CFile::getFilename (it_list->toStdString() ); + if ( std::find(currentFiles.begin(), currentFiles.end(), fileName ) != currentFiles.end() ) + { + // Ask the user if he wants to override the existing file + // atm only warn the user and do not override + QMessageBox::warning(this, tr("BNP Manager"), + tr("File is already in the list!"), + QMessageBox::Ok, + QMessageBox::Ok); + } + else + { + addFiles.push_back( it_list->toStdString() ); + // log it + nlinfo("Add file %s", fileName.c_str() ); + } + it_list++; + } + + if ( !addFiles.empty() ) + { + myBNPFileHandle.addFiles( addFiles ); + } + loadFile(m_openedBNPFile); +} +// *************************************************************************** void BNPManagerWindow::deleteFiles() { QFileDialog filedialog(this); @@ -288,4 +375,70 @@ void BNPManagerWindow::readSettings() void BNPManagerWindow::writeSettings() { } + +// *************************************************************************** +void BNPManagerWindow::dragEnterEvent(QDragEnterEvent *event) +{ + // Accept only one file + // In the future a tabbed FileListDialog would accept more + if ( event->mimeData()->hasUrls() ) + event->acceptProposedAction(); +} +// *************************************************************************** +void BNPManagerWindow::dropEvent(QDropEvent *event) +{ + // reference to the BNPFileHandle singletone instance + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + + // Excraft the local file url from the drop object and fill the table + const QMimeData *mimeData = event->mimeData(); + QList urlList = mimeData->urls(); + QString filePath; + QStringList fileList; + + if ( urlList.count() == 1 ) + { + // If it is a bnp file, open it + // If it is not a bnp file add it + + filePath = urlList.first().toLocalFile(); + if ( filePath.endsWith(".bnp", Qt::CaseInsensitive) ) + { + loadFile(filePath); + } + else + { + if ( m_openedBNPFile == "") + newFile(); + // Create a QStringList and pass it to addfiles + fileList.push_back( filePath ); + addFiles( fileList ); + // Reload current bnp + loadFile(m_openedBNPFile); + } + } + else if ( urlList.count() > 1 ) + { + // Dont accept any bnp file + QList::iterator it = urlList.begin(); + while ( it != urlList.end() ) + { + filePath = it->toLocalFile(); + if ( filePath.endsWith(".bnp") ) + { + nlwarning("Could not add a bnp file!", filePath.toStdString().c_str() ); + } + else + { + fileList.push_back( filePath ); + } + ++it; + } + if ( m_openedBNPFile == "") + newFile(); + addFiles( fileList ); + // Reload current bnp + loadFile(m_openedBNPFile); + } +} } // namespace BNPManager diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h index b31d17a09..89bd68a16 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h @@ -59,9 +59,14 @@ public: public Q_SLOTS: /** - * Open a file dialog to choose which file should be opened. + * Create a new file * \return Filename string */ + void newFile(); + + /** + * Open a file dialog to choose which file should be opened. + */ void open(); /** @@ -81,6 +86,7 @@ public Q_SLOTS: * \param Filelist */ void addFiles(); + void addFiles( QStringList FileList ); /** * Unpack the files marked in the filelist dialog into user defined @@ -96,6 +102,10 @@ public Q_SLOTS: */ void deleteFiles(); +protected: + void dragEnterEvent (QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + private: /** @@ -126,6 +136,7 @@ private: QToolBar *m_fileToolBar; QToolBar *m_toolsBar; + QAction *m_newAction; QAction *m_openAction; QAction *m_closeAction; QAction *m_addFilesAction; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_new.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_new.png new file mode 100644 index 0000000000000000000000000000000000000000..d45dcb4674ac76de749509f45685a53954062ccf GIT binary patch literal 4035 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