diff --git a/CHANGELOG b/CHANGELOG index fa54913b..5bc106bc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,15 @@ +1.6.0-bacon6 + Delete project from SelectProjectDialog + * Press A + B on a project in the project selection screen to delete it + * A confirmation dialog will appear asking to confirm deletion + * The entire project directory and all contents will be removed + * List position is preserved after deletion + 1.6.0-bacon5 Auto-load last project on startup * Application can now load the last project automatically on startup * Disable by setting AUTO_LOAD_LAST=NO in config.xml + 1.6.0-bacon4 QWERTY keyboard name entry for new projects * Press A on the project name field to enter QWERTY keyboard mode diff --git a/docs/wiki/quick_start_guide.md b/docs/wiki/quick_start_guide.md index 465b5d31..728492cb 100644 --- a/docs/wiki/quick_start_guide.md +++ b/docs/wiki/quick_start_guide.md @@ -168,6 +168,23 @@ to save, piggy playback must be stopped (so hit `start` if need be). navigate to don't forget to come back later and keep butchering the pig :) +## managing your projects + +### deleting a project + +If you ever need to delete an entire project, you can do so from the Project Selection screen. + +To delete a project: + +1. from the Project Selection screen, position your cursor on the project you wish to delete +2. press `A + B` +3. a confirmation dialog will appear showing the project name and asking you to confirm the deletion +4. select `yes` to delete the project permanently, or `no` to cancel + +**be careful!** deletion is permanent and cannot be undone. make sure you have backed up your work if you wish to keep it. the entire project directory will be removed, including all songs, chains, phrases, and any associated data. + +it's always a good idea to keep backups of important projects before deleting anything! + ## live mode & muting ![](https://web.archive.org/web/20190513041654im_/http://wiki.littlegptracker.com/lib/exe/fetch.php?media=quick:start14_1.1f.png) diff --git a/docs/wiki/tips_and_tricks.md b/docs/wiki/tips_and_tricks.md index 15d84b16..3f531fb9 100644 --- a/docs/wiki/tips_and_tricks.md +++ b/docs/wiki/tips_and_tricks.md @@ -15,6 +15,20 @@ The on-screen keyboard is organized like this: - Lowercase (a-q + extra characters) - Special [Space] [Erase] [Done] +# Project Management + +## Safely Deleting Projects + +Since version 1.6.0, you can delete projects directly from the Project Selection screen by pressing `A+B`. This is a convenient way to clean up old or unwanted projects. + +**Best Practices:** +- **Always back up important projects** before deleting. Use your operating system's file manager to copy project directories to an external drive or backup location. +- The deletion will remove the entire project directory permanently - this cannot be undone! +- A confirmation dialog will appear showing the project name before deletion proceeds, giving you a chance to cancel if you selected the wrong project. +- After deletion, the project list will refresh automatically and the cursor will stay near its previous position for your convenience. + +**Pro Tip:** Use Save As to store a duplicate of the project if you're uncertain if you should delete something + # Delays and Echoes ## Simulating LSDj's D command diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index fd0c2bc3..c4b8b938 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -20,7 +20,7 @@ #define PROJECT_NUMBER "1" #define PROJECT_RELEASE "6" -#define BUILD_COUNT "0-bacon5" +#define BUILD_COUNT "0-bacon6" #define MAX_TAP 3 diff --git a/sources/Application/Views/ModalDialogs/SelectProjectDialog.cpp b/sources/Application/Views/ModalDialogs/SelectProjectDialog.cpp index 7c0dc506..6a1b7ab6 100644 --- a/sources/Application/Views/ModalDialogs/SelectProjectDialog.cpp +++ b/sources/Application/Views/ModalDialogs/SelectProjectDialog.cpp @@ -25,11 +25,61 @@ static void NewProjectCallback(View &v,ModalView &dialog) { Trace::Error(result.GetDescription().c_str()); } } -}; +} -SelectProjectDialog::SelectProjectDialog(View &view):ModalView(view),content_(true) { +static void DeleteProjectCallback(View &v, ModalView &dialog) { + SelectProjectDialog &spd = (SelectProjectDialog&) v; + if (dialog.GetReturnCode() == MBL_YES) { + Path projectPath = spd.GetCurrentProjectPath(); + Result result = spd.OnDeleteProject(projectPath); + if (result.Failed()) { + Trace::Error(result.GetDescription().c_str()); + } + } } +// Recursive helper to delete directory and all contents +static void RecursiveDeleteDirectory(const Path &dirPath) { + FileSystem *fs = FileSystem::GetInstance(); + FileType type = fs->GetFileType(dirPath.GetPath().c_str()); + + if (type == FT_DIR) { + I_Dir *dir = fs->Open(dirPath.GetPath().c_str()); + if (dir) { + dir->GetContent("*"); + + // Collect all items first to avoid iterator invalidation + T_SimpleList itemsToDelete(false); + IteratorPtr it(dir->GetIterator()); + + for (it->Begin(); !it->IsDone(); it->Next()) { + Path itemCopy = it->CurrentItem(); + std::string name = itemCopy.GetName(); + + // Skip . and .. entries + if (name != "." && name != "..") { + Path *ptrCopy = new Path(itemCopy); + itemsToDelete.Insert(ptrCopy); + } + } + + delete dir; + + // Now delete all collected items + IteratorPtr deleteIt(itemsToDelete.GetIterator()); + for (deleteIt->Begin(); !deleteIt->IsDone(); deleteIt->Next()) { + const Path &item = deleteIt->CurrentItem(); + RecursiveDeleteDirectory(item); + } + } + } + + fs->Delete(dirPath.GetPath().c_str()); +} + +SelectProjectDialog::SelectProjectDialog(View &view) + : ModalView(view), content_(true) {} + SelectProjectDialog::~SelectProjectDialog() { } @@ -118,14 +168,37 @@ void SelectProjectDialog::OnPlayerUpdate(PlayerEventType, void SelectProjectDialog::OnFocus() { setCurrentFolder(lastFolder_) ; - currentProject_=lastProject_ ; - + currentProject_ = lastProject_; }; void SelectProjectDialog::ProcessButtonMask(unsigned short mask,bool pressed) { if (!pressed) return ; if (mask&EPBM_B) { + // Handle A + B combination for delete + if (mask & EPBM_A) { + int count = 0; + Path *current = 0; + + IteratorPtr it(content_.GetIterator()); + for (it->Begin(); !it->IsDone(); it->Next()) { + if (count == currentProject_) { + current = &it->CurrentItem(); + break; + } + count++; + } + + if (current != 0) { + std::string message = + "Delete project '" + current->GetName() + "' ?"; + MessageBox *mb = + new MessageBox(*this, message.c_str(), MBBF_YES | MBBF_NO); + DoModal(mb, DeleteProjectCallback); + DrawView(); + } + return; + } if (mask & EPBM_UP) warpToNextProject(-LIST_SIZE); if (mask&EPBM_DOWN) warpToNextProject(LIST_SIZE) ; @@ -250,6 +323,46 @@ Result SelectProjectDialog::OnNewProject(std::string &name) { return Result::NoError; } ; +Result SelectProjectDialog::OnDeleteProject(const Path &projectPath) { + + Trace::Log("SelectProjectDialog:OnDelProj","deleting project at %s", projectPath.GetPath().c_str()); + + // Make a non-const copy to check existence + Path pathCopy = projectPath; + + // Check if project exists before deletion + if (!pathCopy.Exists()) { + std::string errMsg = "Project not found"; + View::SetNotification(errMsg.c_str(), 0); + return Result("Project not found"); + } + + // Recursively delete the project directory and all contents + RecursiveDeleteDirectory(projectPath); + + // Project deleted successfully, refresh the project list + std::string successMsg = "Project deleted: " + pathCopy.GetName(); + View::SetNotification(successMsg.c_str(), 0); + + // Refresh current folder to update the list while preserving position + int savedProject = currentProject_; + int savedTopIndex = topIndex_; + Path currentPathCopy = currentPath_; + setCurrentFolder(currentPathCopy); + + // Restore position (adjust if necessary if we're at the end of list) + int listSize = content_.Size(); + if (savedProject >= listSize && listSize > 0) { + currentProject_ = listSize - 1; + } else if (listSize > 0) { + currentProject_ = savedProject; + } + topIndex_ = savedTopIndex; + isDirty_ = true; + + return Result::NoError; +}; + //copy-paste-mutilate'd from ImportSampleDialog void SelectProjectDialog::setCurrentFolder(Path &path) { @@ -293,3 +406,15 @@ void SelectProjectDialog::setCurrentFolder(Path &path) { currentProject_=0 ; isDirty_ = true; } + +Path SelectProjectDialog::GetCurrentProjectPath() { + int count = 0; + IteratorPtr it(content_.GetIterator()); + for (it->Begin(); !it->IsDone(); it->Next()) { + if (count == currentProject_) { + return it->CurrentItem(); + } + count++; + } + return Path(); +} diff --git a/sources/Application/Views/ModalDialogs/SelectProjectDialog.h b/sources/Application/Views/ModalDialogs/SelectProjectDialog.h index 437b227f..2582ff3a 100644 --- a/sources/Application/Views/ModalDialogs/SelectProjectDialog.h +++ b/sources/Application/Views/ModalDialogs/SelectProjectDialog.h @@ -16,22 +16,24 @@ class SelectProjectDialog:public ModalView { virtual void ProcessButtonMask(unsigned short mask,bool pressed) ; Result OnNewProject(std::string &name) ; + Result OnDeleteProject(const Path &projectPath); - Path GetSelection() ; + Path GetSelection(); + Path GetCurrentProjectPath(); -protected: - void warpToNextProject(int amount) ; + protected: + void warpToNextProject(int amount) ; void setCurrentFolder(Path &path) ; private: - T_SimpleList content_ ; - int topIndex_ ; - int currentProject_ ; - int selected_ ; - Path currentPath_ ; - Path selection_ ; - static Path lastFolder_ ; - static int lastProject_ ; + T_SimpleList content_; + int topIndex_; + int currentProject_; + int selected_; + Path currentPath_; + Path selection_; + static Path lastFolder_; + static int lastProject_; } ; #endif