/* SampleHive * Copyright (C) 2021 Apoorv Singh * A simple, modern audio sample browser/manager for GNU/Linux. * * This file is a part of SampleHive * * SampleHive is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SampleHive is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "GUI/MainFrame.hpp" #include "GUI/Dialogs/Settings.hpp" #include "Database/Database.hpp" #include "Utility/ControlIDs.hpp" #include "Utility/HiveData.hpp" #include "Utility/Log.hpp" #include "Utility/Paths.hpp" #include "Utility/Utils.hpp" #include "SampleHiveConfig.hpp" #include #include #include #include #include #include #include #include #include #include cMainFrame::cMainFrame() : wxFrame(NULL, wxID_ANY, "SampleHive", wxDefaultPosition) { // Initialize statusbar with 4 sections m_pStatusBar = CreateStatusBar(4); // Set width for each section of the statusbar int status_width[4] = { 300, -6, -1, -2 }; m_pStatusBar->SetStatusWidths(4, status_width); m_pHiveBitmap = new wxStaticBitmap(m_pStatusBar, wxID_ANY, wxBitmap(ICON_HIVE_24px)); // Initialize menubar and menus m_pMenuBar = new wxMenuBar(); m_pFileMenu = new wxMenu(); m_pEditMenu = new wxMenu(); m_pViewMenu = new wxMenu(); m_pHelpMenu = new wxMenu(); // File menu items m_pAddFile = new wxMenuItem(m_pFileMenu, SampleHive::ID::MN_AddFile, _("Add a file\tCtrl+F"), _("Add a file")); m_pAddFile->SetBitmap(wxArtProvider::GetBitmap(wxART_NORMAL_FILE)); m_pFileMenu->Append(m_pAddFile); m_pAddDirectory = new wxMenuItem(m_pFileMenu, SampleHive::ID::MN_AddDirectory, _("Add a directory\tCtrl+D"), _("Add a directory")); m_pAddDirectory->SetBitmap(wxArtProvider::GetBitmap(wxART_FOLDER)); m_pFileMenu->Append(m_pAddDirectory); m_pFileMenu->AppendSeparator(); m_pFileMenu->Append(wxID_EXIT, wxEmptyString, _("Exits the application")); // Edit menu items m_pEditMenu->Append(wxID_PREFERENCES, _("Preferences\tCtrl+P"), _("Open preferences dialog")); // View menu items m_pToggleExtension = new wxMenuItem(m_pViewMenu, SampleHive::ID::MN_ToggleExtension, _("Toggle Extension\tCtrl+E"), _("Show/Hide Extension"), wxITEM_CHECK); m_pToggleMenuBar = new wxMenuItem(m_pViewMenu, SampleHive::ID::MN_ToggleMenuBar, _("Toggle Menu Bar\tCtrl+M"), _("Show/Hide Menu Bar"), wxITEM_CHECK); m_pToggleStatusBar = new wxMenuItem(m_pViewMenu, SampleHive::ID::MN_ToggleStatusBar, _("Toggle Status Bar\tCtrl+B"), _("Show/Hide Status Bar"), wxITEM_CHECK); m_pViewMenu->Append(m_pToggleExtension)->Check(true); m_pViewMenu->Append(m_pToggleMenuBar)->Check(m_pMenuBar->IsShown()); m_pViewMenu->Append(m_pToggleStatusBar)->Check(m_pStatusBar->IsShown()); // Help menu items m_pHelpMenu->Append(wxID_REFRESH, _("Reset app data"), _("Clear the application data revert to default configuration")); m_pHelpMenu->Append(wxID_ABOUT, wxEmptyString, _("Show about the application")); // Append all menus to menubar m_pMenuBar->Append(m_pFileMenu, _("&File")); m_pMenuBar->Append(m_pEditMenu, _("&Edit")); m_pMenuBar->Append(m_pViewMenu, _("&View")); m_pMenuBar->Append(m_pHelpMenu, _("&Help")); // Set the menu bar to use SetMenuBar(m_pMenuBar); // Load default yaml config file. LoadConfigFile(); // Initialize the database InitDatabase(); // Initializing Sizers m_pMainSizer = new wxBoxSizer(wxVERTICAL); m_pTopPanelMainSizer = new wxBoxSizer(wxVERTICAL); // Creating top splitter window m_pTopSplitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE | wxSP_THIN_SASH); m_pTopSplitter->SetMinimumPaneSize(200); m_pTopSplitter->SetSashGravity(0); // Top half of TopSplitter window m_pTopPanel = new wxPanel(m_pTopSplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); // Bottom half of the TopSplitter window m_pBottomSplitter = new wxSplitterWindow(m_pTopSplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE | wxSP_THIN_SASH); m_pBottomSplitter->SetMinimumPaneSize(300); m_pBottomSplitter->SetSashGravity(0); m_pNotebook = new cNotebook(m_pBottomSplitter); m_pLibrary = new cLibrary(m_pBottomSplitter); SampleHive::cHiveData::Get().InitHiveData(*m_pLibrary->GetListCtrlObject(), *m_pNotebook->GetHivesPanel()->GetHivesObject(), m_pNotebook->GetHivesPanel()->GetFavoritesHive(), *m_pNotebook->GetTrashPanel()->GetTrashObject(), m_pNotebook->GetTrashPanel()->GetTrashRoot()); // Set split direction m_pTopSplitter->SplitHorizontally(m_pTopPanel, m_pBottomSplitter); m_pBottomSplitter->SplitVertically(m_pNotebook, m_pLibrary); // Initializing wxMediaCtrl. m_pMediaCtrl = new wxMediaCtrl(this, SampleHive::ID::BC_MediaCtrl, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxEmptyString); // Intializing wxTimer m_pTimer = new wxTimer(this); m_pTransportControls = new cTransportControls(m_pTopPanel, *m_pMediaCtrl); m_pWaveformViewer = new cWaveformViewer(m_pTopPanel, *m_pMediaCtrl, *m_pDatabase); // Binding events. Bind(wxEVT_MENU, &cMainFrame::OnSelectAddFile, this, SampleHive::ID::MN_AddFile); Bind(wxEVT_MENU, &cMainFrame::OnSelectAddDirectory, this, SampleHive::ID::MN_AddDirectory); Bind(wxEVT_MENU, &cMainFrame::OnSelectToggleExtension, this, SampleHive::ID::MN_ToggleExtension); Bind(wxEVT_MENU, &cMainFrame::OnSelectToggleMenuBar, this, SampleHive::ID::MN_ToggleMenuBar); Bind(wxEVT_MENU, &cMainFrame::OnSelectToggleStatusBar, this, SampleHive::ID::MN_ToggleStatusBar); Bind(wxEVT_MENU, &cMainFrame::OnSelectExit, this, wxID_EXIT); Bind(wxEVT_MENU, &cMainFrame::OnSelectPreferences, this, wxID_PREFERENCES); Bind(wxEVT_MENU, &cMainFrame::OnSelectResetAppData, this, wxID_REFRESH); Bind(wxEVT_MENU, &cMainFrame::OnSelectAbout, this, wxID_ABOUT); Bind(wxEVT_SPLITTER_SASH_POS_CHANGING, &cMainFrame::OnTopSplitterSashPosChanged, this, m_pTopSplitter->GetId()); Bind(wxEVT_SPLITTER_SASH_POS_CHANGING, &cMainFrame::OnBottomSplitterSashPosChanged, this, m_pBottomSplitter->GetId()); this->Connect(wxEVT_SIZE, wxSizeEventHandler(cMainFrame::OnResizeFrame), NULL, this); m_pStatusBar->Connect(wxEVT_SIZE, wxSizeEventHandler(cMainFrame::OnResizeStatusBar), NULL, this); Bind(wxEVT_MEDIA_FINISHED, &cMainFrame::OnMediaFinished, this, SampleHive::ID::BC_MediaCtrl); Bind(wxEVT_TIMER, &cMainFrame::UpdateElapsedTime, this); Bind(SampleHive::SH_EVT_LOOP_POINTS_UPDATED, &cMainFrame::OnRecieveLoopPoints, this); Bind(SampleHive::SH_EVT_LOOP_POINTS_CLEAR, &cMainFrame::OnRecieveClearLoopPointsStatus, this); Bind(SampleHive::SH_EVT_STATUSBAR_STATUS_PUSH, &cMainFrame::OnRecievePushStatusBarStatus, this); Bind(SampleHive::SH_EVT_STATUSBAR_STATUS_POP, &cMainFrame::OnRecievePopStatusBarStatus, this); Bind(SampleHive::SH_EVT_STATUSBAR_STATUS_SET, &cMainFrame::OnRecieveSetStatusBarStatus, this); Bind(SampleHive::SH_EVT_INFOBAR_MESSAGE_SHOW, &cMainFrame::OnRecieveInfoBarStatus, this); Bind(SampleHive::SH_EVT_TIMER_STOP, &cMainFrame::OnRecieveTimerStopStatus, this); Bind(SampleHive::SH_EVT_UPDATE_WAVEFORM, &cMainFrame::OnRecieveWaveformUpdateStatus, this); Bind(SampleHive::SH_EVT_CALL_FUNC_PLAY, &cMainFrame::OnRecieveCallFunctionPlay, this); Bind(SampleHive::SH_EVT_LOOP_AB_BUTTON_VALUE_CHANGE, &cMainFrame::OnRecieveLoopABButtonValueChange, this); // Adding widgets to their sizers m_pMainSizer->Add(m_pTopSplitter, 1, wxALL | wxEXPAND, 0); m_pTopPanelMainSizer->Add(m_pWaveformViewer, 1, wxALL | wxEXPAND, 2); m_pTopPanelMainSizer->Add(m_pTransportControls, 0, wxALL | wxEXPAND, 2); // Sizer for the frame this->SetSizer(m_pMainSizer); this->Layout(); this->Center(wxBOTH); // Sizer for TopPanel m_pTopPanel->SetSizer(m_pTopPanelMainSizer); m_pTopPanelMainSizer->Fit(m_pTopPanel); m_pTopPanelMainSizer->SetSizeHints(m_pTopPanel); m_pTopPanelMainSizer->Layout(); // Restore the data previously added to Library LoadDatabase(); // Set some properites after the frame has been created CallAfter(&cMainFrame::SetAfterFrameCreate); } void cMainFrame::OnMediaFinished(wxMediaEvent& event) { if (m_pTransportControls->CanLoop()) { if (!m_pMediaCtrl->Play()) { wxMessageDialog msgDialog(NULL, _("Error! Cannot loop media."), _("Error"), wxOK | wxICON_ERROR); msgDialog.ShowModal(); } else m_pMediaCtrl->Play(); } else { if (m_pTimer->IsRunning()) { m_pTimer->Stop(); SH_LOG_DEBUG("TIMER STOPPED"); } m_pTransportControls->SetSamplePositionText("--:--/--:--"); PopStatusText(1); this->SetStatusText(_("Stopped"), 1); } } void cMainFrame::UpdateElapsedTime(wxTimerEvent& event) { SH_LOG_DEBUG("TIMER IS RUNNING.."); wxString duration, position; wxLongLong llLength, llTell; llLength = m_pMediaCtrl->Length(); int total_min = static_cast((llLength / 60000).GetValue()); int total_sec = static_cast(((llLength % 60000) / 1000).GetValue()); llTell = m_pMediaCtrl->Tell(); int current_min = static_cast((llTell / 60000).GetValue()); int current_sec = static_cast(((llTell % 60000) / 1000).GetValue()); duration.Printf(wxT("%2i:%02i"), total_min, total_sec); position.Printf(wxT("%2i:%02i"), current_min, current_sec); m_pTransportControls->SetSamplePositionText(wxString::Format(wxT("%s/%s"), position.c_str(), duration.c_str())); this->Refresh(); m_pTransportControls->Refresh(); m_pWaveformViewer->Refresh(); if (m_bLoopPointsSet && m_pTransportControls->IsLoopABOn()) if (static_cast(m_pMediaCtrl->Tell()) >= m_LoopB.ToDouble()) m_pMediaCtrl->Seek(m_LoopA.ToDouble(), wxFromStart); } void cMainFrame::LoadDatabase() { SampleHive::cSerializer serializer; try { const auto dataset = m_pDatabase->LoadSamplesDatabase(*m_pNotebook->GetHivesPanel()->GetHivesObject(), m_pNotebook->GetHivesPanel()->GetFavoritesHive(), *m_pNotebook->GetTrashPanel()->GetTrashObject(), m_pNotebook->GetTrashPanel()->GetTrashRoot(), serializer.DeserializeShowFileExtension(), ICON_STAR_FILLED_16px, ICON_STAR_EMPTY_16px); if (dataset.empty()) SH_LOG_INFO("Error! Database is empty."); else { for (auto data : dataset) SampleHive::cHiveData::Get().ListCtrlAppendItem(data); } m_pDatabase->LoadHivesDatabase(*m_pNotebook->GetHivesPanel()->GetHivesObject()); } catch (std::exception& e) { SH_LOG_ERROR("Error loading data. {}", e.what()); } } void cMainFrame::LoadConfigFile() { // Check if SampleHive configuration directory exist and create it if not if (!wxDirExists(APP_CONFIG_DIR)) { SH_LOG_WARN("Configuration directory not found. Creating directory at {}", APP_CONFIG_DIR); if (wxFileName::Mkdir(APP_CONFIG_DIR, wxPOSIX_USER_READ | wxPOSIX_USER_WRITE | wxPOSIX_USER_EXECUTE | wxPOSIX_GROUP_READ | wxPOSIX_GROUP_EXECUTE | wxPOSIX_OTHERS_READ | wxPOSIX_OTHERS_EXECUTE, wxPATH_MKDIR_FULL)) { SH_LOG_INFO("Successfully created configuration directory at {}", APP_CONFIG_DIR); } else { wxMessageBox(wxString::Format(_("Error! Could not create configuration directory %s"), APP_CONFIG_DIR), _("Error!"), wxOK | wxCENTRE, this); } } else SH_LOG_INFO("Found {} directory.", APP_CONFIG_DIR); // Check if SampleHive data directory exist and create it if not if (!wxDirExists(APP_DATA_DIR)) { SH_LOG_WARN("Data directory not found. Creating directory at {}", APP_DATA_DIR); if (wxFileName::Mkdir(APP_DATA_DIR, wxPOSIX_USER_READ | wxPOSIX_USER_WRITE | wxPOSIX_USER_EXECUTE | wxPOSIX_GROUP_READ | wxPOSIX_GROUP_EXECUTE | wxPOSIX_OTHERS_READ | wxPOSIX_OTHERS_EXECUTE, wxPATH_MKDIR_FULL)) { SH_LOG_INFO("Successfully created data directory at {}", APP_DATA_DIR); } else { wxMessageBox(wxString::Format(_("Error! Could not create data directory %s"), APP_DATA_DIR), _("Error!"), wxOK | wxCENTRE, this); } } else SH_LOG_INFO("Found {} directory.", APP_DATA_DIR); SampleHive::cSerializer serializer; SH_LOG_INFO("Reading configuration file.."); int height = 600, width = 800; width = serializer.DeserializeWinSize().first; height = serializer.DeserializeWinSize().second; int min_width = 960, min_height = 540; m_bShowMenuBar = serializer.DeserializeShowMenuAndStatusBar("menubar"); m_bShowStatusBar = serializer.DeserializeShowMenuAndStatusBar("statusbar"); m_pToggleMenuBar->Check(m_bShowMenuBar); m_pMenuBar->Show(m_bShowMenuBar); m_pToggleStatusBar->Check(m_bShowStatusBar); m_pStatusBar->Show(m_bShowStatusBar); m_pToggleExtension->Check(serializer.DeserializeShowFileExtension()); this->SetFont(serializer.DeserializeFontSettings()); this->SetSize(width, height); this->SetMinSize(wxSize(min_width, min_height)); this->CenterOnScreen(wxBOTH); this->SetIcon(wxIcon(ICON_HIVE_256px, wxICON_DEFAULT_TYPE, -1, -1)); this->SetTitle(PROJECT_NAME); this->SetStatusText(wxString::Format("%s %s", PROJECT_NAME, PROJECT_VERSION), 3); this->SetStatusText(_("Stopped"), 1); } // void cMainFrame::RefreshDatabase() // { // m_pLibrary->GetListCtrlObject()->DeleteAllItems(); // if (m_pNotebook->GetHivesPanel()->GetHivesObject()->GetChildCount(wxDataViewItem(wxNullPtr)) <= 1 && // m_pNotebook->GetHivesPanel()->GetHivesObject()->GetItemText(wxDataViewItem(wxNullPtr)) == // m_pNotebook->GetHivesPanel()->GetHivesObject()->GetItemText(m_pNotebook->GetHivesPanel()->GetFavoritesHive())) // return; // else // m_pNotebook->GetHivesPanel()->GetHivesObject()->DeleteAllItems(); // m_pNotebook->GetTrashPanel()->GetTrashObject()->DeleteAllItems(); // LoadDatabase(); // } bool cMainFrame::CreateWatcherIfNecessary() { if (m_pFsWatcher) return false; CreateWatcher(); Bind(wxEVT_FSWATCHER, &cMainFrame::OnFileSystemEvent, this); return true; } void cMainFrame::CreateWatcher() { SampleHive::cSerializer serializer; wxCHECK_RET(!m_pFsWatcher, _("Watcher already initialized")); m_pFsWatcher = new wxFileSystemWatcher(); m_pFsWatcher->SetOwner(this); wxString path = serializer.DeserializeAutoImport().second; if (serializer.DeserializeAutoImport().first) { SH_LOG_INFO("Adding watch entry: {}", path); if (serializer.DeserializeRecursiveImport()) AddWatchEntry(wxFSWPath_Tree, path.ToStdString()); else AddWatchEntry(wxFSWPath_Dir, path.ToStdString()); } } void cMainFrame::AddWatchEntry(wxFSWPathType type, std::string path) { SampleHive::cSerializer serializer; if (path.empty()) { path = wxDirSelector(_("Choose a directory to watch"), "", wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); if (path.empty()) return; } wxCHECK_RET(m_pFsWatcher, _("Watcher not initialized")); SH_LOG_INFO("Adding {}: '{}'", path, type == wxFSWPath_Dir ? "directory" : "directory tree"); wxFileName filename = wxFileName::DirName(path); if (!filename.IsOk() || !filename.IsDir() || !filename.IsDirReadable()) { SH_LOG_ERROR("Error! Something wrong with {} path", filename.GetFullPath()); return; } if (!serializer.DeserializeFollowSymLink()) { filename.DontFollowLink(); } bool ok = false; switch (type) { case wxFSWPath_Dir: ok = m_pFsWatcher->Add(filename); break; case wxFSWPath_Tree: ok = m_pFsWatcher->AddTree(filename); break; case wxFSWPath_File: break; case wxFSWPath_None: wxFAIL_MSG(_("Error! Unexpected path type.")); } if (!ok) { SH_LOG_ERROR("Error! Cannot add '{}' to watched paths", filename.GetPath()); return; } } void cMainFrame::OnFileSystemEvent(wxFileSystemWatcherEvent& event) { wxLogTrace(wxTRACE_FSWATCHER, "*** %s ***", event.ToString()); int type = event.GetChangeType(); wxString path = event.GetPath().GetFullPath(); wxArrayString files; files.push_back(path); SH_LOG_DEBUG("{} {}", path, event.ToString()); switch (type) { case wxFSW_EVENT_CREATE: SH_LOG_INFO("NEW FILES DETECTED, ADDING: {}", path); SampleHive::cUtils::Get().AddSamples(files, this); break; case wxFSW_EVENT_ACCESS: SH_LOG_INFO("ACCESSING DIRECTORY: {}", path); break; case wxFSW_EVENT_DELETE: SH_LOG_INFO("FILES DELETED IN DIRECTORY: {}", path); break; case wxFSW_EVENT_MODIFY: SH_LOG_INFO("DIRECTORY MODIFIED: {}", path); break; case wxFSW_EVENT_RENAME: SH_LOG_INFO("FILES RENAMED IN DIRECTORY: {}", event.GetNewPath().GetFullPath()); break; case wxFSW_EVENT_WARNING: SH_LOG_INFO("Filesystem watcher warning: {}", event.GetWarningType()); break; case wxFSW_EVENT_ERROR: SH_LOG_INFO("Error! Filesystem watcher: {}", event.GetErrorDescription()); break; default: break; } } void cMainFrame::OnSelectAddFile(wxCommandEvent& event) { wxFileDialog file_dialog(this, wxFileSelectorPromptStr, USER_HOME_DIR, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_DEFAULT_STYLE | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | wxFD_PREVIEW, wxDefaultPosition, wxDefaultSize); switch (file_dialog.ShowModal()) { case wxID_OK: { wxArrayString paths; file_dialog.GetPaths(paths); for (size_t i = 0; i < (size_t)paths.size(); i++) SampleHive::cUtils::Get().AddSamples(paths, this); } break; default: break; } } void cMainFrame::OnSelectAddDirectory(wxCommandEvent& event) { wxDirDialog dir_dialog(this, wxDirSelectorPromptStr, USER_HOME_DIR, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST, wxDefaultPosition, wxDefaultSize); switch (dir_dialog.ShowModal()) { case wxID_OK: { wxString path = dir_dialog.GetPath(); SampleHive::cUtils::Get().OnAutoImportDir(path, this); } break; default: break; } } void cMainFrame::OnSelectToggleExtension(wxCommandEvent& event) { SampleHive::cSerializer serializer; if (m_pToggleExtension->IsChecked()) { serializer.SerializeShowFileExtension(true); m_pLibrary->GetInfoBarObject()->ShowMessage(_("Extension showing, restart the application to view changes " "or press CTRL+E to toggle show/hide."), wxICON_INFORMATION); } else { serializer.SerializeShowFileExtension(false); m_pLibrary->GetInfoBarObject()->ShowMessage(_("Extension hidden, restart the application to view changes " "or press CTRL+E to toggle show/hide."), wxICON_INFORMATION); } } void cMainFrame::OnSelectToggleMenuBar(wxCommandEvent& event) { SampleHive::cSerializer serializer; if (m_pToggleMenuBar->IsChecked()) { m_pMenuBar->Show(); m_pLibrary->GetInfoBarObject()->ShowMessage(_("MenuBar showing, press CTRL+M to toggle show/hide."), wxICON_INFORMATION); m_bShowMenuBar = true; serializer.SerializeShowMenuAndStatusBar("menubar", m_bShowMenuBar); } else { m_pMenuBar->Hide(); m_pLibrary->GetInfoBarObject()->ShowMessage(_("MenuBar hidden, press CTRL+M to toggle show/hide."), wxICON_INFORMATION); m_bShowMenuBar = false; serializer.SerializeShowMenuAndStatusBar("menubar", m_bShowMenuBar); } } void cMainFrame::OnSelectToggleStatusBar(wxCommandEvent& event) { SampleHive::cSerializer serializer; if (m_pToggleStatusBar->IsChecked()) { m_pStatusBar->Show(); m_pLibrary->GetInfoBarObject()->ShowMessage(_("StatusBar showing, press CTRL+B to toggle show/hide."), wxICON_INFORMATION); m_bShowStatusBar = true; serializer.SerializeShowMenuAndStatusBar("statusbar", m_bShowStatusBar); } else { m_pStatusBar->Hide(); m_pLibrary->GetInfoBarObject()->ShowMessage(_("StatusBar hidden, press CTRL+B to toggle show/hide."), wxICON_INFORMATION); m_bShowStatusBar = false; serializer.SerializeShowMenuAndStatusBar("statusbar", m_bShowStatusBar); } } void cMainFrame::OnSelectExit(wxCommandEvent& event) { Close(); } void cMainFrame::OnSelectPreferences(wxCommandEvent& event) { cSettings* settings = new cSettings(this); switch (settings->ShowModal()) { case wxID_OK: if (settings->CanAutoImport()) { SampleHive::cUtils::Get().OnAutoImportDir(settings->GetImportDirPath(), this); // RefreshDatabase(); } if (settings->IsWaveformColourChanged()) { m_pWaveformViewer->ResetDC(); } break; case wxID_CANCEL: break; default: return; } } void cMainFrame::OnSelectResetAppData(wxCommandEvent& event) { wxMessageDialog clearDataDialog(this, wxString::Format(_("Warning! This will delete configuration file " "\"%s\" and database file \"%s\" permanently, " "are you sure you want to delete these files?"), CONFIG_FILEPATH, DATABASE_FILEPATH), _("Clear app data?"), wxYES_NO | wxNO_DEFAULT | wxCENTRE, wxDefaultPosition); bool remove = false; switch (clearDataDialog.ShowModal()) { case wxID_YES: remove = true; if (remove) { if (!wxFileExists(CONFIG_FILEPATH)) { SH_LOG_ERROR("Error! File {} doesn't exist.", CONFIG_FILEPATH); return; } bool config_is_deleted = wxRemoveFile(CONFIG_FILEPATH); if (config_is_deleted) SH_LOG_INFO("Deleted {}", CONFIG_FILEPATH); else SH_LOG_ERROR("Could not delete {}", CONFIG_FILEPATH); if (!wxFileExists(DATABASE_FILEPATH)) { SH_LOG_ERROR("Error! File {} doesn't exist.", DATABASE_FILEPATH); return; } bool db_is_deleted = wxRemoveFile(DATABASE_FILEPATH); if (db_is_deleted) SH_LOG_INFO("Deleted {}", DATABASE_FILEPATH); else SH_LOG_ERROR("Could not delete {}", DATABASE_FILEPATH); if (config_is_deleted && db_is_deleted) { m_pLibrary->GetInfoBarObject()->ShowMessage(_("Successfully cleared app data"), wxICON_INFORMATION); SH_LOG_INFO("Cleared app data successfully"); } else wxMessageBox(_("Error! Could not clear app data"), _("Error!"), wxOK | wxCENTRE | wxICON_ERROR, this); } break; case wxID_NO: break; default: break; } } void cMainFrame::OnSelectAbout(wxCommandEvent& event) { wxAboutDialogInfo aboutInfo; aboutInfo.SetName(PROJECT_NAME); aboutInfo.SetIcon(wxIcon(ICON_HIVE_64px)); aboutInfo.AddArtist(PROJECT_AUTHOR); aboutInfo.SetVersion(PROJECT_VERSION, _("Version 0.9.0_alpha.1")); aboutInfo.SetDescription(_(PROJECT_DESCRIPTION)); aboutInfo.SetCopyright("(C)" PROJECT_COPYRIGHT_YEARS); aboutInfo.SetWebSite(PROJECT_WEBSITE); aboutInfo.AddDeveloper(PROJECT_AUTHOR); aboutInfo.SetLicence(wxString::Format(wxString::FromAscii( "%s %s\n" "Copyright (C) %s Apoorv Singh\n" "\n" "%s is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, either version 3 of the License, or\n" "(at your option) any later version.\n" "\n" "%s is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see .\n" ), PROJECT_NAME, PROJECT_VERSION, PROJECT_COPYRIGHT_YEARS, PROJECT_NAME, PROJECT_NAME)); wxAboutBox(aboutInfo); } void cMainFrame::OnResizeStatusBar(wxSizeEvent& event) { wxRect rect; m_pStatusBar->GetFieldRect(2, rect); wxSize bitmap_size = m_pHiveBitmap->GetSize(); m_pHiveBitmap->Move(rect.x + (rect.width - bitmap_size.x), rect.y + (rect.height - bitmap_size.y)); event.Skip(); } void cMainFrame::OnResizeFrame(wxSizeEvent& event) { SampleHive::cSerializer serializer; SH_LOG_DEBUG("Frame resized to {}, {}", GetSize().GetWidth(), GetSize().GetHeight()); serializer.SerializeWinSize(GetSize().GetWidth(), GetSize().GetHeight()); event.Skip(); } void cMainFrame::OnTopSplitterSashPosChanged(wxSplitterEvent& event) { SampleHive::cSerializer serializer; SH_LOG_DEBUG("TopSplitter at {}", m_pTopSplitter->GetSashPosition()); serializer.SerializeSplitterSashPos("top", m_pTopSplitter->GetSashPosition()); } void cMainFrame::OnBottomSplitterSashPosChanged(wxSplitterEvent& event) { SampleHive::cSerializer serializer; SH_LOG_DEBUG("BottomSplitter at {}", m_pBottomSplitter->GetSashPosition()); serializer.SerializeSplitterSashPos("bottom", m_pBottomSplitter->GetSashPosition()); } void cMainFrame::SetAfterFrameCreate() { SampleHive::cSerializer serializer; m_pTopSplitter->SetSashPosition(serializer.DeserializeSplitterSashPos("top")); m_pBottomSplitter->SetSashPosition(serializer.DeserializeSplitterSashPos("bottom")); } void cMainFrame::OnRecieveLoopPoints(SampleHive::cLoopPointsEvent& event) { std::pair loop_points = event.GetLoopPoints(); m_LoopA = wxLongLong(loop_points.first); m_LoopB = wxLongLong(loop_points.second); int loopA_min = static_cast((m_LoopA / 60000).GetValue()); int loopA_sec = static_cast(((m_LoopA % 60000) / 1000).GetValue()); int loopB_min = static_cast((m_LoopB / 60000).GetValue()); int loopB_sec = static_cast(((m_LoopB % 60000) / 1000).GetValue()); SH_LOG_INFO(wxString::Format(_("Loop points set: A: %2i:%02i, B: %2i:%02i"), loopA_min, loopA_sec, loopB_min, loopB_sec)); m_pTransportControls->SetLoopABValue(true); m_bLoopPointsSet = true; } void cMainFrame::OnRecievePushStatusBarStatus(SampleHive::cStatusBarStatusEvent& event) { std::pair status = event.GetPushMessageAndSection(); m_pStatusBar->PushStatusText(status.first, status.second); } void cMainFrame::OnRecievePopStatusBarStatus(SampleHive::cStatusBarStatusEvent& event) { m_pStatusBar->PopStatusText(event.GetPopMessageSection()); } void cMainFrame::OnRecieveSetStatusBarStatus(SampleHive::cStatusBarStatusEvent& event) { std::pair status = event.GetStatusTextAndSection(); m_pStatusBar->SetStatusText(status.first, status.second); } void cMainFrame::OnRecieveInfoBarStatus(SampleHive::cInfoBarMessageEvent& event) { std::pair info = event.GetInfoBarMessage(); m_pLibrary->GetInfoBarObject()->ShowMessage(info.first, info.second); } void cMainFrame::OnRecieveTimerStopStatus(SampleHive::cTimerEvent& event) { if (m_pTimer->IsRunning()) m_pTimer->Stop(); } void cMainFrame::OnRecieveCallFunctionPlay(SampleHive::cCallFunctionEvent& event) { SampleHive::cSerializer serializer; wxString selection = event.GetSlection(); bool checkAutoplay = event.GetAutoplayValue(); wxString sample_path = serializer.DeserializeShowFileExtension() ? m_pDatabase->GetSamplePathByFilename(selection.BeforeLast('.').ToStdString()) : m_pDatabase->GetSamplePathByFilename(selection.ToStdString()); if (checkAutoplay) { if (m_pTransportControls->CanAutoplay()) { if (m_bLoopPointsSet && m_pTransportControls->IsLoopABOn()) PlaySample(sample_path.ToStdString(), selection.ToStdString(), true, m_LoopA.ToDouble(), wxFromStart); else PlaySample(sample_path.ToStdString(), selection.ToStdString()); } else m_pMediaCtrl->Stop(); } else { if (m_bLoopPointsSet && m_pTransportControls->IsLoopABOn()) PlaySample(sample_path.ToStdString(), selection.ToStdString(), true, m_LoopA.ToDouble(), wxFromStart); else PlaySample(sample_path.ToStdString(), selection.ToStdString()); } } void cMainFrame::OnRecieveWaveformUpdateStatus(SampleHive::cWaveformUpdateEvent& event) { m_pWaveformViewer->ResetDC(); } void cMainFrame::OnRecieveClearLoopPointsStatus(SampleHive::cLoopPointsEvent& event) { ClearLoopPoints(); } void cMainFrame::OnRecieveLoopABButtonValueChange(SampleHive::cLoopPointsEvent& event) { m_pTransportControls->SetLoopABValue(false); } void cMainFrame::ClearLoopPoints() { m_LoopA = 0; m_LoopB = 0; m_bLoopPointsSet = false; } void cMainFrame::PlaySample(const std::string& filepath, const std::string& sample, bool seek, wxFileOffset where, wxSeekMode mode) { if (m_pMediaCtrl->Load(filepath)) { if (seek) m_pMediaCtrl->Seek(where, mode); if (!m_pMediaCtrl->Play()) SH_LOG_ERROR("Error! Cannot play sample."); PushStatusText(wxString::Format(_("Now playing: %s"), sample), 1); if (!m_pTimer->IsRunning()) { SH_LOG_INFO("Starting timer.."); m_pTimer->Start(20, wxTIMER_CONTINUOUS); } } else SH_LOG_ERROR("Error! Cannot load sample."); } void cMainFrame::InitDatabase() { // Initialize the database try { m_pDatabase = std::make_unique(); m_pDatabase->CreateTableSamples(); m_pDatabase->CreateTableHives(); } catch (std::exception& e) { SH_LOG_ERROR("Error! Cannot initialize database {}", e.what()); } } cMainFrame::~cMainFrame() { // Delete wxTimer delete m_pTimer; // Delete wxFilesystemWatcher delete m_pFsWatcher; }