diff --git a/src/Database.cpp b/src/Database.cpp index f10bd05..9368e0f 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -18,10 +19,10 @@ Database::~Database() } -void Database::CreateDatabase() +void Database::CreateTableSamples() { /* Create SQL statement */ - std::string sample = "CREATE TABLE IF NOT EXISTS SAMPLES(" + std::string samples = "CREATE TABLE IF NOT EXISTS SAMPLES(" "FAVORITE INT NOT NULL," "FILENAME TEXT NOT NULL," "EXTENSION TEXT NOT NULL," @@ -47,11 +48,55 @@ void Database::CreateDatabase() wxLogDebug("Opening DB.."); } - rc = sqlite3_exec(m_Database, sample.c_str(), NULL, 0, &m_ErrMsg); + rc = sqlite3_exec(m_Database, samples.c_str(), NULL, 0, &m_ErrMsg); if (rc != SQLITE_OK) { - wxMessageDialog msgDialog(NULL, "Error! Cannot create table.", + wxMessageDialog msgDialog(NULL, "Error! Cannot create table samples.", + "Error", wxOK | wxICON_ERROR); + msgDialog.ShowModal(); + sqlite3_free(m_ErrMsg); + } + else + { + wxLogDebug("Table created successfully."); + } + + rc = sqlite3_close(m_Database); + + if (rc == SQLITE_OK) + wxLogDebug("DB Closed.."); + else + wxLogDebug("Error! Cannot close DB, Error code: %d, Error message: %s", rc, m_ErrMsg); + } + catch (const std::exception &exception) + { + wxLogDebug(exception.what()); + } +} + +void Database::CreateTableCollections() +{ + /* Create SQL statement */ + std::string collections = "CREATE TABLE IF NOT EXISTS COLLECTIONS(FOLDERNAME TEXT NOT NULL);"; + + try + { + if (sqlite3_open("sample.hive", &m_Database) != SQLITE_OK) + { + wxLogDebug("Error opening DB"); + throw sqlite3_errmsg(m_Database); + } + else + { + wxLogDebug("Opening DB.."); + } + + rc = sqlite3_exec(m_Database, collections.c_str(), NULL, 0, &m_ErrMsg); + + if (rc != SQLITE_OK) + { + wxMessageDialog msgDialog(NULL, "Error! Cannot create table collections.", "Error", wxOK | wxICON_ERROR); msgDialog.ShowModal(); sqlite3_free(m_ErrMsg); @@ -75,7 +120,7 @@ void Database::CreateDatabase() } //Loops through a Sample array and adds them to the database -void Database::InsertSamples(std::vector samples) +void Database::InsertIntoSamples(std::vector samples) { try { @@ -186,6 +231,104 @@ void Database::InsertSamples(std::vector samples) } } +void Database::InsertIntoCollections(const std::string& folderName) +{ + try + { + if (sqlite3_open("sample.hive", &m_Database) != SQLITE_OK) + { + wxLogDebug("Error opening DB"); + throw sqlite3_errmsg(m_Database); + } + else + { + wxLogDebug("Opening DB.."); + } + + std::string insert = "INSERT INTO COLLECTIONS(FOLDERNAME) VALUES(?);"; + + rc = sqlite3_prepare_v2(m_Database, insert.c_str(), insert.size(), &m_Stmt, NULL); + + // rc = sqlite3_exec(m_Database, "BEGIN TRANSACTION", NULL, NULL, &m_ErrMsg); + + if (rc != SQLITE_OK) + wxLogDebug("Cannot prepare sql statement.."); + + // Sample sample; + + // std::string filename; + // std::string file_extension; + // std::string sample_pack; + // std::string type; + // std::string path; + + // for(unsigned int i = 0; i < samples.size(); i++) + // { + // sample = samples[i]; + + // filename = sample.GetFilename(); + // file_extension = sample.GetFileExtension(); + // sample_pack = sample.GetSamplePack(); + // type = sample.GetType(); + // path = sample.GetPath(); + + // std::string folder = "Favourites"; + + // rc = sqlite3_bind_int(m_Stmt, 1, sample.GetFavorite()); + rc = sqlite3_bind_text(m_Stmt, 1, folderName.c_str(), folderName.size(), SQLITE_STATIC); + + rc = sqlite3_step(m_Stmt); + // rc = sqlite3_clear_bindings(m_Stmt); + // rc = sqlite3_reset(m_Stmt); + // } + + // rc = sqlite3_exec(m_Database, "END TRANSACTION", NULL, NULL, &m_ErrMsg); + + rc = sqlite3_finalize(m_Stmt); + + if (rc != SQLITE_OK) + { + wxLogDebug("Error! Cannot insert data into table. Error code: %d: Msg: %s", rc , sqlite3_errmsg(m_Database)); + } + else + { + wxLogDebug("Data inserted successfully."); + } + + if (rc == SQLITE_BUSY) + wxLogDebug("SQLITE_BUSY"); + if (rc == SQLITE_ABORT) + wxLogDebug("SQLITE_ABORT"); + if (rc == SQLITE_NOMEM) + wxLogDebug("SQLITE_NOMEM"); + if (rc == SQLITE_LOCKED) + wxLogDebug("SQLITE_LOCKED"); + if (rc == SQLITE_IOERR) + wxLogDebug("SQLITE_IOERR"); + if (rc == SQLITE_CORRUPT) + wxLogDebug("SQLITE_CORRUPT"); + if (rc == SQLITE_READONLY) + wxLogDebug("SQLITE_READONLY"); + if (rc == SQLITE_ERROR) + wxLogDebug("SQLITE_ERROR"); + if (rc == SQLITE_PERM) + wxLogDebug("SQLITE_PERM"); + if (rc == SQLITE_INTERNAL) + wxLogDebug("SQLITE_INTERNAL"); + + rc = sqlite3_close(m_Database); + + if (rc == SQLITE_OK) + wxLogDebug("DB Closed.."); + else + wxLogDebug("Error! Cannot close DB, Error code: %d, Error message: %s", rc, m_ErrMsg); + } + catch (const std::exception &exception) + { + wxLogDebug(exception.what()); + } +} + void Database::UpdateFolder(const std::string& folderName) { try @@ -555,6 +698,45 @@ void Database::RemoveSampleFromDatabase(const std::string& filename) } } +void Database::RemoveFolderFromCollections(const std::string& folderName) +{ + try + { + rc = sqlite3_open("sample.hive", &m_Database); + + std::string remove = "DELETE FROM COLLECTIONS WHERE FOLDERNAME = ?;"; + + rc = sqlite3_prepare_v2(m_Database, remove.c_str(), remove.size(), &m_Stmt, NULL); + + rc = sqlite3_bind_text(m_Stmt, 1, folderName.c_str(), folderName.size(), SQLITE_STATIC); + + if (sqlite3_step(m_Stmt) == SQLITE_DONE) + { + wxLogDebug("Record found, Deleting.."); + } + + rc = sqlite3_finalize(m_Stmt); + + if (rc != SQLITE_OK) + { + wxMessageDialog msgDialog(NULL, "Error! Cannot delete data from table.", + "Error", wxOK | wxICON_ERROR); + msgDialog.ShowModal(); + sqlite3_free(m_ErrMsg); + } + else + { + wxLogDebug("Deleted data from table successfully."); + } + + sqlite3_close(m_Database); + } + catch (const std::exception &exception) + { + wxLogDebug(exception.what()); + } +} + std::string Database::GetSamplePathByFilename(const std::string& filename) { std::string path; @@ -660,7 +842,7 @@ Database::LoadDatabase(wxVector>& vecSet, std::string load = "SELECT FAVORITE, FILENAME, EXTENSION, SAMPLEPACK, \ TYPE, CHANNELS, LENGTH, SAMPLERATE, BITRATE, PATH, \ - TRASHED FROM SAMPLES;"; + TRASHED, FOLDER FROM SAMPLES;"; rc = sqlite3_prepare_v2(m_Database, load.c_str(), load.size(), &m_Stmt, NULL); @@ -681,6 +863,7 @@ Database::LoadDatabase(wxVector>& vecSet, int bitrate = sqlite3_column_int(m_Stmt, 8); wxString path = std::string(reinterpret_cast(sqlite3_column_text(m_Stmt, 9))); int trashed = sqlite3_column_int(m_Stmt, 10); + wxString folder_name = std::string(reinterpret_cast(sqlite3_column_text(m_Stmt, 11))); wxVector vec; @@ -692,7 +875,52 @@ Database::LoadDatabase(wxVector>& vecSet, { vec.push_back(true); - favorite_tree.AppendItem(favorite_item, filename); + wxLogDebug("Loading collection items.."); + + std::deque nodes; + nodes.push_back(favorite_tree.GetNthChild(wxDataViewItem(wxNullPtr), 0)); + + wxDataViewItem current_item, found_item; + + int row = 0; + int folder_count = favorite_tree.GetChildCount(wxDataViewItem(wxNullPtr)); + + while(!nodes.empty()) + { + current_item = nodes.front(); + nodes.pop_front(); + + if (favorite_tree.GetItemText(current_item) == folder_name) + { + found_item = current_item; + wxLogDebug("Loading, folder name: %s", folder_name); + break; + } + + wxDataViewItem child = favorite_tree.GetNthChild(wxDataViewItem(wxNullPtr), 0); + + while (row < (folder_count - 1)) + { + row ++; + + child = favorite_tree.GetNthChild(wxDataViewItem(wxNullPtr), row); + nodes.push_back(child); + } + } + + nodes.clear(); + + if (found_item.IsOk()) + { + // wxLogDebug("Another folder by the name %s already exist. Please try with a different name.", + // folder_name); + favorite_tree.AppendItem(found_item, filename); + } + // else + // { + // favorite_tree.AppendItem(wxDataViewItem(wxNullPtr), folder_name); + // } + } else vec.push_back(false); @@ -814,6 +1042,123 @@ Database::FilterDatabaseBySampleName(wxVector>& sampleVec, c return sampleVec; } +wxVector> +Database::FilterDatabaseByFolderName(wxVector>& sampleVec, const std::string& folderName) +{ + try + { + if (sqlite3_open("sample.hive", &m_Database) != SQLITE_OK) + { + wxLogDebug("Error opening DB"); + throw sqlite3_errmsg(m_Database); + } + + std::string filter = "SELECT FAVORITE, FILENAME, SAMPLEPACK, TYPE, \ + CHANNELS, LENGTH, SAMPLERATE, BITRATE \ + FROM SAMPLES WHERE FOLDER = ? AND FAVORITE = 1;"; + + rc = sqlite3_prepare_v2(m_Database, filter.c_str(), filter.size(), &m_Stmt, NULL); + + rc = sqlite3_bind_text(m_Stmt, 1, folderName.c_str(), folderName.size(), SQLITE_STATIC); + + if (rc == SQLITE_OK) + { + int row = 0; + + while (SQLITE_ROW == sqlite3_step(m_Stmt)) + { + wxLogInfo("Record found, fetching.."); + int favorite = sqlite3_column_int(m_Stmt, 0); + wxString filename = wxString(std::string(reinterpret_cast(sqlite3_column_text(m_Stmt, 1)))); + wxString sample_pack = wxString(std::string(reinterpret_cast(sqlite3_column_text(m_Stmt, 2)))); + wxString sample_type = std::string(reinterpret_cast(sqlite3_column_text(m_Stmt, 3))); + int channels = sqlite3_column_int(m_Stmt, 4); + int length = sqlite3_column_int(m_Stmt, 5); + int sample_rate = sqlite3_column_int(m_Stmt, 6); + int bitrate = sqlite3_column_int(m_Stmt, 7); + + wxVector vec; + + if (favorite == 1) + vec.push_back(true); + else + vec.push_back(false); + + vec.push_back(filename); + vec.push_back(sample_pack); + vec.push_back(sample_type); + vec.push_back(wxString::Format("%d", channels)); + vec.push_back(wxString::Format("%d", length)); + vec.push_back(wxString::Format("%d", sample_rate)); + vec.push_back(wxString::Format("%d", bitrate)); + + sampleVec.push_back(vec); + + row++; + } + } + else + { + wxMessageDialog msgDialog(NULL, "Error! Cannot filter data from table.", + "Error", wxOK | wxICON_ERROR); + msgDialog.ShowModal(); + sqlite3_free(m_ErrMsg); + } + + rc = sqlite3_finalize(m_Stmt); + + sqlite3_close(m_Database); + } + catch (const std::exception &exception) + { + wxLogDebug(exception.what()); + } + + return sampleVec; +} + +void Database::LoadCollectionFolder(wxDataViewTreeCtrl& treeCtrl) +{ + try + { + if (sqlite3_open("sample.hive", &m_Database) != SQLITE_OK) + { + wxLogDebug("Error opening DB"); + throw sqlite3_errmsg(m_Database); + } + + std::string select = "SELECT FOLDERNAME FROM COLLECTIONS;"; + + rc = sqlite3_prepare_v2(m_Database, select.c_str(), select.size(), &m_Stmt, NULL); + + if (rc == SQLITE_OK) + { + while (SQLITE_ROW == sqlite3_step(m_Stmt)) + { + wxLogInfo("Record found, fetching.."); + wxString folder = wxString(std::string(reinterpret_cast(sqlite3_column_text(m_Stmt, 0)))); + + treeCtrl.AppendContainer(wxDataViewItem(wxNullPtr), folder); + } + } + else + { + wxMessageDialog msgDialog(NULL, "Error! Cannot load foldername from collection table.", + "Error", wxOK | wxICON_ERROR); + msgDialog.ShowModal(); + sqlite3_free(m_ErrMsg); + } + + rc = sqlite3_finalize(m_Stmt); + + sqlite3_close(m_Database); + } + catch (const std::exception &exception) + { + wxLogDebug(exception.what()); + } +} + //Compares the input array with the database and removes duplicates. wxArrayString Database::CheckDuplicates(const wxArrayString& files) { diff --git a/src/Database.hpp b/src/Database.hpp index 910fa84..cd9af0f 100644 --- a/src/Database.hpp +++ b/src/Database.hpp @@ -32,11 +32,13 @@ class Database public: // ------------------------------------------------------------------- // Create the table - void CreateDatabase(); + void CreateTableSamples(); + void CreateTableCollections(); // ------------------------------------------------------------------- // Insert into database - void InsertSamples(std::vector); + void InsertIntoSamples(std::vector); + void InsertIntoCollections(const std::string& folderName); // ------------------------------------------------------------------- // Update database @@ -64,16 +66,20 @@ class Database // ------------------------------------------------------------------- // Remove from database void RemoveSampleFromDatabase(const std::string& filename); + void RemoveFolderFromCollections(const std::string& folderName); // ------------------------------------------------------------------- wxVector> // LoadDatabase(wxVector> &vecSet, // wxTreeCtrl& favorite_tree, wxTreeItemId& favorite_item, // wxTreeCtrl& trash_tree, wxTreeItemId& trash_item, bool show_extension); - LoadDatabase(wxVector> &vecSet, + LoadDatabase(wxVector>& vecSet, wxDataViewTreeCtrl& favorite_tree, wxDataViewItem& favorite_item, wxTreeCtrl& trash_tree, wxTreeItemId& trash_item, bool show_extension); + void LoadCollectionFolder(wxDataViewTreeCtrl& favorite_tree); wxVector> - FilterDatabaseBySampleName(wxVector> &sampleVec, + FilterDatabaseBySampleName(wxVector>& sampleVec, const std::string& sampleName); + wxVector> + FilterDatabaseByFolderName(wxVector>& sampleVec, const std::string& folderName); }; diff --git a/src/MainFrame.cpp b/src/MainFrame.cpp index d5e184b..b9cd75b 100644 --- a/src/MainFrame.cpp +++ b/src/MainFrame.cpp @@ -118,7 +118,6 @@ MainFrame::MainFrame() // Adding root to CollectionView // rootNode = m_CollectionView->AddRoot("ROOT"); favorites_folder = m_CollectionView->AppendContainer(wxDataViewItem(wxNullPtr), "Favourites"); - m_CollectionView->AppendItem(favorites_folder, "sample.xyz"); // Addubg root to TrashedItems trash_root_node = m_TrashedItems->AddRoot("ROOT"); @@ -220,8 +219,7 @@ MainFrame::MainFrame() Bind(wxEVT_SEARCHCTRL_SEARCH_BTN, &MainFrame::OnDoSearch, this, BC_Search); Bind(wxEVT_SEARCHCTRL_CANCEL_BTN, &MainFrame::OnCancelSearch, this, BC_Search); - // Bind(wxEVT_TREE_ITEM_ACTIVATED, &MainFrame::OnClickCollectionView, this, - // BC_CollectionView); + Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &MainFrame::OnClickCollectionView, this, BC_CollectionView); m_CollectionView->Connect(wxEVT_DROP_FILES, wxDropFilesEventHandler(MainFrame::OnDragAndDropToCollectionView), NULL, this); Bind(wxEVT_BUTTON, &MainFrame::OnClickCollectionAdd, this, BC_CollectionViewAdd); Bind(wxEVT_BUTTON, &MainFrame::OnClickCollectionRemove, this, BC_CollectionViewRemove); @@ -325,7 +323,10 @@ MainFrame::MainFrame() // Initialize the database Database db(*m_InfoBar); - db.CreateDatabase(); + db.CreateTableSamples(); + db.CreateTableCollections(); + + // db.InsertIntoCollections(m_CollectionView->GetItemText(favorites_folder).ToStdString()); // Restore the data previously added to SampleListView LoadDatabase(); @@ -370,7 +371,7 @@ void MainFrame::AddSamples(wxArrayString& files) wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_CAN_ABORT | wxPD_AUTO_HIDE); progressDialog->CenterOnParent(wxBOTH); - + std::vector sample_array; std::string path; @@ -457,8 +458,8 @@ void MainFrame::AddSamples(wxArrayString& files) progressDialog->Pulse("Updating Database..",NULL); - db.InsertSamples(sample_array); - + db.InsertIntoSamples(sample_array); + progressDialog->Destroy(); } @@ -570,10 +571,10 @@ void MainFrame::OnDragAndDropToCollectionView(wxDropFilesEvent& event) { m_SampleListView->SetToggleValue(true, row, 0); - // m_CollectionView->AppendItem(drop_target, files[i]); + m_CollectionView->AppendItem(drop_target, files[i]); - // db.UpdateFavoriteColumn(name.ToStdString(), 1); - // db.UpdateFavoriteFolder(name.ToStdString(), folder_name.ToStdString()); + db.UpdateFavoriteColumn(files[i].ToStdString(), 1); + db.UpdateFavoriteFolder(files[i].ToStdString(), folder_name.ToStdString()); } else wxLogDebug("%s is not a folder. Try dropping on folder.", @@ -1086,6 +1087,8 @@ void MainFrame::LoadDatabase() try { + db.LoadCollectionFolder(*m_CollectionView); + wxVector> dataset; if (db.LoadDatabase(dataset, *m_CollectionView, favorites_folder, @@ -1197,16 +1200,23 @@ void MainFrame::OnCheckFavorite(wxDataViewEvent& event) wxDataViewItem selected = m_CollectionView->GetSelection(); wxString folder; + // wxDataViewItem selected = event.GetItem(); + if(selected.IsOk() && m_CollectionView->IsContainer(selected)) { folder = m_CollectionView->GetItemText(selected); m_CollectionView->AppendItem(selected, selection); + + // db.UpdateFavoriteColumn(selection.ToStdString(), 1); + // db.UpdateFavoriteFolder(selection.ToStdString(), folder.ToStdString()); } else { - // msg = "Not a folder."; - // folder = m_CollectionView->GetItemText(wxDataViewItem(wxNullPtr)); + folder = m_CollectionView->GetItemText(favorites_folder); m_CollectionView->AppendItem(favorites_folder, selection); + + // db.UpdateFavoriteColumn(selection.ToStdString(), 1); + // db.UpdateFavoriteFolder(selection.ToStdString(), folder.ToStdString()); } db.UpdateFavoriteColumn(selection.ToStdString(), 1); @@ -1328,6 +1338,7 @@ void MainFrame::OnExpandTrash(wxCollapsiblePaneEvent& event) void MainFrame::OnClickCollectionAdd(wxCommandEvent& event) { // wxMessageBox("// TODO", "Add item", wxOK | wxCENTER, this, wxDefaultCoord, wxDefaultCoord); + Database db(*m_InfoBar); std::deque nodes; nodes.push_back(m_CollectionView->GetNthChild(wxDataViewItem(wxNullPtr), 0)); @@ -1386,6 +1397,8 @@ void MainFrame::OnClickCollectionAdd(wxCommandEvent& event) { msg = wxString::Format("Folder %s added to colletions.", folder_name); m_CollectionView->AppendContainer(wxDataViewItem(wxNullPtr), folder_name); + + db.InsertIntoCollections(folder_name.ToStdString()); } break; } @@ -1401,6 +1414,7 @@ void MainFrame::OnClickCollectionAdd(wxCommandEvent& event) void MainFrame::OnClickCollectionRemove(wxCommandEvent& event) { // wxMessageBox("// TODO", "Remove item", wxOK | wxCENTER, this, wxDefaultCoord, wxDefaultCoord); + Database db(*m_InfoBar); wxDataViewItem selected = m_CollectionView->GetSelection(); wxString folder_name = m_CollectionView->GetItemText(selected); @@ -1431,12 +1445,14 @@ void MainFrame::OnClickCollectionRemove(wxCommandEvent& event) if (selected.IsOk() && m_CollectionView->IsContainer(selected) && folder_name != "Favourites") { m_CollectionView->DeleteItem(selected); + + db.RemoveFolderFromCollections(folder_name.ToStdString()); msg = wxString::Format("%s deleted from collections successfully.", folder_name); } else - if(folder_name == "Favourites") - msg = wxString::Format("Error! Default folder %s cannot be deleted.", folder_name); - else + if(folder_name == "Favourites") + msg = wxString::Format("Error! Default folder %s cannot be deleted.", folder_name); + else msg = wxString::Format("Error! %s is not a folder, cannot delete from collections.", folder_name); break; case wxID_NO: @@ -1454,13 +1470,15 @@ void MainFrame::OnClickCollectionRemove(wxCommandEvent& event) { m_CollectionView->DeleteChildren(selected); m_CollectionView->DeleteItem(selected); + + db.RemoveFolderFromCollections(folder_name.ToStdString()); msg = wxString::Format("%s and all samples inside %s have been deleted from collections successfully.", folder_name, folder_name); } else - if(folder_name == "Favourites") - msg = wxString::Format("Error! Default folder %s cannot be deleted.", folder_name); - else + if(folder_name == "Favourites") + msg = wxString::Format("Error! Default folder %s cannot be deleted.", folder_name); + else msg = wxString::Format("Error! %s is not a folder, cannot delete from collections.", folder_name); break; case wxID_NO: @@ -1473,6 +1491,43 @@ void MainFrame::OnClickCollectionRemove(wxCommandEvent& event) m_InfoBar->ShowMessage(msg, wxICON_INFORMATION); } +void MainFrame::OnClickCollectionView(wxDataViewEvent &event) +{ + Database db(*m_InfoBar); + + // wxDataViewItem selected = m_CollectionView->GetSelection(); + wxDataViewItem selected = event.GetItem(); + + wxString folder_name = m_CollectionView->GetItemText(selected); + + wxLogDebug("Folder name: %s", folder_name); + + try + { + wxVector> dataset; + + if (db.FilterDatabaseByFolderName(dataset, folder_name.ToStdString()).empty()) + { + wxLogDebug("Error! Database is empty."); + } + else + { + m_SampleListView->DeleteAllItems(); + + std::cout << folder_name << std::endl; + + for (auto data : dataset) + { + m_SampleListView->AppendItem(data); + } + } + } + catch (...) + { + std::cerr << "Error loading data." << std::endl; + } +} + void MainFrame::OnClickRestoreTrashItem(wxCommandEvent& event) { Database db(*m_InfoBar); @@ -1486,7 +1541,7 @@ void MainFrame::OnClickRestoreTrashItem(wxCommandEvent& event) db.UpdateTrashColumn(filename, 0); - RefreshDatabase(); + // RefreshDatabase(); // TODO: Don't let other trashed items re-added again m_TrashedItems->Delete(selection_id); diff --git a/src/MainFrame.hpp b/src/MainFrame.hpp index 3255a1d..d46cc7d 100644 --- a/src/MainFrame.hpp +++ b/src/MainFrame.hpp @@ -159,7 +159,7 @@ class MainFrame : public wxFrame // ------------------------------------------------------------------- // CollectionViewPanel button event handlers - // void OnClickCollectionView(wxTreeEvent& event); + void OnClickCollectionView(wxDataViewEvent& event); void OnDragAndDropToCollectionView(wxDropFilesEvent& event); void OnClickCollectionAdd(wxCommandEvent& event); void OnClickCollectionRemove(wxCommandEvent& event);