From 2471c483bc1404cd7fb76d5c4e675791ef617a2b Mon Sep 17 00:00:00 2001 From: "avihal@gmail.com" Date: Sat, 12 Mar 2011 22:58:28 +0000 Subject: [PATCH] Memory cards manager: allow custom file names (create/rename/copy), still without assigning arbitrary MCD files at the folder. TODO: 1. allow assigning arbitrary files at the folder. 2. GUI cleanups (text alignment on some messages, MCD list vertical stretch..) 3. code cleanups (remove all commented/unused code). This is a relatively big change of the MCD manager, so I'd appreciate feedback (functionality, translations, bugs, etc). git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4423 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/CDVD/IsoFileFormats.cpp | 1 + pcsx2/gui/Dialogs/ConfigurationDialog.h | 10 +- pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp | 70 +++-- pcsx2/gui/Dialogs/McdConfigDialog.cpp | 8 +- pcsx2/gui/MemoryCardFile.cpp | 52 +++- pcsx2/gui/MemoryCardFile.h | 3 +- pcsx2/gui/Panels/MemoryCardListPanel.cpp | 308 +++++++++++++++---- pcsx2/gui/Panels/MemoryCardListView.cpp | 51 +-- pcsx2/gui/Panels/MemoryCardPanels.h | 48 +-- 9 files changed, 423 insertions(+), 128 deletions(-) diff --git a/pcsx2/CDVD/IsoFileFormats.cpp b/pcsx2/CDVD/IsoFileFormats.cpp index 0a0b6f142d..acae94b710 100644 --- a/pcsx2/CDVD/IsoFileFormats.cpp +++ b/pcsx2/CDVD/IsoFileFormats.cpp @@ -129,6 +129,7 @@ bool isoFile::Detect( bool readType ) m_blockofs = 0; m_type = ISOTYPE_AUDIO; + //BUG: This also detects a memory-card-file as a valid Audio-CD ISO... -avih return true; } diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.h b/pcsx2/gui/Dialogs/ConfigurationDialog.h index 4afb11ac0f..7038f003d3 100644 --- a/pcsx2/gui/Dialogs/ConfigurationDialog.h +++ b/pcsx2/gui/Dialogs/ConfigurationDialog.h @@ -212,8 +212,9 @@ namespace Dialogs uint m_slot; wxDirName m_mcdpath; wxString m_mcdfile; + wxTextCtrl* m_text_filenameInput; - wxFilePickerCtrl* m_filepicker; + //wxFilePickerCtrl* m_filepicker; pxRadioPanel* m_radio_CardSize; #ifdef __WXMSW__ @@ -223,9 +224,10 @@ namespace Dialogs public: virtual ~CreateMemoryCardDialog() throw() {} //CreateMemoryCardDialog( wxWindow* parent, uint port, uint slot, const wxString& filepath=wxEmptyString ); - CreateMemoryCardDialog( wxWindow* parent, uint slot, const wxDirName& mcdpath, const wxString& mcdfile=wxEmptyString ); - - wxDirName GetPathToMcds() const; + CreateMemoryCardDialog( wxWindow* parent, uint slot, const wxDirName& mcdpath, const wxString& suggested_mcdfileName); + + wxString result_createdMcdFilename; + //wxDirName GetPathToMcds() const; protected: void CreateControls(); diff --git a/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp b/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp index 01c237930e..d43f88125f 100644 --- a/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp +++ b/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp @@ -18,14 +18,13 @@ #include "System.h" #include "MemoryCardFile.h" - -#include +//#include #include using namespace pxSizerFlags; extern wxString GetMsg_McdNtfsCompress(); - +/* wxFilePickerCtrl* CreateMemoryCardFilePicker( wxWindow* parent, uint portidx, uint slotidx, const wxString& filename=wxEmptyString ) { return new wxFilePickerCtrl( parent, wxID_ANY, filename, @@ -36,14 +35,15 @@ wxFilePickerCtrl* CreateMemoryCardFilePicker( wxWindow* parent, uint portidx, ui ); } - -Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, uint slot, const wxDirName& mcdpath, const wxString& mcdfile ) - : wxDialogWithHelpers( parent, _("Create new memory card") ) +*/ +Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, uint slot, const wxDirName& mcdpath, const wxString& suggested_mcdfileName) + : wxDialogWithHelpers( parent, _("Create a new memory card file") ) , m_mcdpath( mcdpath.IsOk() ? mcdpath : (wxDirName)g_Conf->Mcd[slot].Filename.GetPath() ) - , m_mcdfile( mcdfile.IsEmpty() ? g_Conf->Mcd[slot].Filename.GetFullName() : mcdfile ) + , m_mcdfile( suggested_mcdfileName )//suggested_and_result_mcdfileName.IsEmpty() ? g_Conf->Mcd[slot].Filename.GetFullName() { + SetMinWidth( 472 ); - m_filepicker = NULL; + //m_filepicker = NULL; m_slot = slot; CreateControls(); @@ -65,12 +65,22 @@ Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, uint //s_padding += Heading(_("Select the size for your new memory card.")); - if( m_filepicker ) - s_padding += m_filepicker | StdExpand(); - else +// if( m_filepicker ) +// s_padding += m_filepicker | StdExpand(); +// else { - s_padding += Heading( _( "New card will be saved to:" ) ) | StdExpand(); - s_padding += Heading( (m_mcdpath + m_mcdfile).GetFullPath() ).Unwrapped() | StdExpand(); + s_padding += Heading( _( "New memory card file:" ) ) | StdExpand(); + s_padding += Heading( wxString(_("At folder: ")) + (m_mcdpath + m_mcdfile).GetPath() ).Unwrapped() | StdExpand(); + + wxBoxSizer& s_filename( *new wxBoxSizer(wxHORIZONTAL) ); + s_filename += Heading( _("Select file name: ")).SetMinWidth(150); + m_text_filenameInput->SetMinSize(wxSize(150,20)); + m_text_filenameInput->SetValue ((m_mcdpath + m_mcdfile).GetName()); + s_filename += m_text_filenameInput; + s_filename += Heading( L".ps2" ); + + s_padding += s_filename | wxALIGN_LEFT; + } s_padding += m_radio_CardSize | StdExpand(); @@ -85,13 +95,16 @@ Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, uint *this += s_padding | StdExpand(); Connect( wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CreateMemoryCardDialog::OnOk_Click ) ); -} + m_text_filenameInput->SetFocus(); + m_text_filenameInput->SelectAll(); +} +/* wxDirName Dialogs::CreateMemoryCardDialog::GetPathToMcds() const { return m_filepicker ? (wxDirName)m_filepicker->GetPath() : m_mcdpath; } - +*/ // When this GUI is moved into the FileMemoryCard plugin (where it eventually belongs), // this function will be removed and the MemoryCardFile::Create() function will be used // instead. @@ -102,7 +115,7 @@ static bool CreateIt( const wxString& mcdFile, uint sizeInMB ) u8 m_effeffs[528*16]; memset8<0xff>( m_effeffs ); - Console.WriteLn( L"(FileMcd) Creating new %uMB memory card: " + mcdFile, sizeInMB ); + Console.WriteLn( L"(FileMcd) Creating new %uMB memory card file: '%s'", sizeInMB, mcdFile.c_str() ); wxFFile fp( mcdFile, L"wb" ); if( !fp.IsOpened() ) return false; @@ -114,6 +127,7 @@ static bool CreateIt( const wxString& mcdFile, uint sizeInMB ) if( fp.Write( m_effeffs, sizeof(m_effeffs) ) == 0 ) return false; } + return true; } @@ -125,18 +139,34 @@ void Dialogs::CreateMemoryCardDialog::OnOk_Click( wxCommandEvent& evt ) #ifdef __WXMSW__ g_Conf->McdCompressNTFS = m_check_CompressNTFS->GetValue(); #endif + result_createdMcdFilename=L"_INVALID_FILE_NAME_"; + wxString composedName = m_text_filenameInput->GetValue().Trim() + L".ps2"; + wxString errMsg; + if( !isValidNewFilename(composedName, m_mcdpath, errMsg, 5) ) + { + wxString message; + message.Printf(_("Error (%s)"), errMsg.c_str()); + Msgbox::Alert( message, _("Create memory card file") ); + m_text_filenameInput->SetFocus(); + m_text_filenameInput->SelectAll(); + return; + } + + wxString fullPath=(m_mcdpath + composedName).GetFullPath(); if( !CreateIt( - m_filepicker ? m_filepicker->GetPath() : (m_mcdpath + m_mcdfile).GetFullPath(), + fullPath, m_radio_CardSize ? m_radio_CardSize->SelectedItem().SomeInt : 8 ) ) { Msgbox::Alert( - _("Error: The memory card could not be created."), - _("memory card creation error") + _("Error: The memory card file could not be created."), + _("Create memory card file") ); return; } + + result_createdMcdFilename = composedName; EndModal( wxID_OK ); } @@ -153,6 +183,8 @@ void Dialogs::CreateMemoryCardDialog::CreateControls() ) ); + m_text_filenameInput = new wxTextCtrl( this, wxID_ANY); + // Initial value of the checkbox is saved between calls to the dialog box. If the user checks // the option, it remains checked for future dialog. If the user unchecks it, ditto. m_check_CompressNTFS->SetValue( g_Conf->McdCompressNTFS ); diff --git a/pcsx2/gui/Dialogs/McdConfigDialog.cpp b/pcsx2/gui/Dialogs/McdConfigDialog.cpp index a95b66b3ab..02f5b67c0e 100644 --- a/pcsx2/gui/Dialogs/McdConfigDialog.cpp +++ b/pcsx2/gui/Dialogs/McdConfigDialog.cpp @@ -48,7 +48,7 @@ Panels::McdConfigPanel_Toggles::McdConfigPanel_Toggles(wxWindow *parent) for( uint i=0; i<2; ++i ) { - m_check_Multitap[i] = new pxCheckBox( this, pxsFmt(_("Enable Multitap on Port %u"), i+1) ); + m_check_Multitap[i] = new pxCheckBox( this, pxsFmt(_("Use Multitap on Port %u"), i+1) ); m_check_Multitap[i]->SetClientData( (void*)i ); m_check_Multitap[i]->SetName(pxsFmt( L"CheckBox::Multitap%u", i )); } @@ -100,8 +100,10 @@ Dialogs::McdConfigDialog::McdConfigDialog( wxWindow* parent ) // [TODO] : Plan here is to add an advanced tab which gives the user the ability // to configure the names of each memory card slot. - - *this += Heading(_("Drag items over other items in the list to swap or copy memory cards.")) | StdExpand(); + wxString title=_("Drag card files in the list to swap or copy between ports."); + title+=_("\n\nNote: 'Rename' and 'Delete' require to apply pending changes first."); + title+=_("\nAlso, 'Create', 'Rename' and 'Delete' will NOT be reverted when clicking 'Cancel'."); + *this += Heading(title) | StdExpand(); *this += StdPadding; *this += m_panel_mcdlist | StdExpand(); diff --git a/pcsx2/gui/MemoryCardFile.cpp b/pcsx2/gui/MemoryCardFile.cpp index e627c6432f..eb0056f3bb 100644 --- a/pcsx2/gui/MemoryCardFile.cpp +++ b/pcsx2/gui/MemoryCardFile.cpp @@ -15,6 +15,7 @@ #include "PrecompiledHeader.h" #include "Utilities/SafeArray.inl" +#include #include "MemoryCardFile.h" @@ -75,9 +76,9 @@ protected: wxString GetDisabledMessage( uint slot ) const { return pxE( "!Notice:Mcd:HasBeenDisabled", wxsFormat( - L"The memory card in slot %d has been automatically disabled. You can correct the problem\n" - L"and re-enable the memory card at any time using Config:Memory cards from the main menu.", - slot + L"The PS2-slot %d has been automatically disabled. You can correct the problem\n" + L"and re-enable it at any time using Config:Memory cards from the main menu.", + slot//TODO: translate internal slot index to human-readable slot description ) ); } }; @@ -272,7 +273,7 @@ s32 FileMemoryCard::Read( uint slot, u8 *dest, u32 adr, int size ) wxFFile& mcfp( m_file[slot] ); if( !mcfp.IsOpened() ) { - DevCon.Error( "(FileMcd) Ignoring attempted read from disabled card." ); + DevCon.Error( "(FileMcd) Ignoring attempted read from disabled slot." ); memset(dest, 0, size); return 1; } @@ -286,7 +287,7 @@ s32 FileMemoryCard::Save( uint slot, const u8 *src, u32 adr, int size ) if( !mcfp.IsOpened() ) { - DevCon.Error( "(FileMcd) Ignoring attempted save/write to disabled card." ); + DevCon.Error( "(FileMcd) Ignoring attempted save/write to disabled slot." ); return 1; } @@ -311,7 +312,7 @@ s32 FileMemoryCard::EraseBlock( uint slot, u32 adr ) if( !mcfp.IsOpened() ) { - DevCon.Error( "MemoryCard: Ignoring erase for disabled card." ); + DevCon.Error( "MemoryCard: Ignoring erase for disabled slot." ); return 1; } @@ -509,3 +510,42 @@ struct superblock u8 card_type; // 0x150 u8 card_flags; // 0x151 }; + + +//Tests if a string is a valid name for a new file within a specified directory. +//returns true if: +// - the file name has a minimum length of minNumCharacters chars (default is 5 chars: at least 1 char + '.' + 3-chars extension) +// and - the file name is within the basepath directory (doesn't contain .. , / , \ , etc) +// and - file name doesn't already exist +// and - can be created on current system (it is actually created and deleted for this test). +bool isValidNewFilename( wxString filenameStringToTest, wxDirName atBasePath, wxString& out_errorMessage, uint minNumCharacters) +{ + if ( filenameStringToTest.Length()<1 || filenameStringToTest.Length()= 0, "memory card Index is uninitialized (invalid drag&drop object state)" ); + pxAssumeDev( m_viewIndex >= 0, "memory card view-Index is uninitialized (invalid drag&drop object state)" ); return (uint)m_viewIndex; } @@ -349,15 +353,13 @@ public: m_listview->GetMcdProvider().RefreshMcds(); return result; } - + + virtual wxDragResult OnDropMcd( McdSlotItem& src, McdSlotItem& dest, wxDragResult def ) { if( src.Slot == dest.Slot ) return wxDragNone; if( !pxAssert( (src.Slot >= 0) && (dest.Slot >= 0) ) ) return wxDragNone; - const wxDirName basepath( m_listview->GetMcdProvider().GetMcdPath() ); - wxFileName srcfile( basepath + g_Conf->Mcd[src.Slot].Filename ); - wxFileName destfile( basepath + g_Conf->Mcd[dest.Slot].Filename ); bool result = true; @@ -365,19 +367,56 @@ public: { // user is force invoking copy mode, which means we need to check the destination // and prompt if it looks valuable (formatted). + if( !src.IsPresent ) + { + Msgbox::Alert(_("Failed. Can only copy an existing card file."), _("Copy memory card file")); + return wxDragNone; + } + + if ( !dest.IsPresent ) + { + while (1){ + wxString newFilename=L""; + newFilename = wxGetTextFromUser(_("Select a name for the new memory card file copy\n( '.ps2' will be added automatically)"), _("Copy memory card file")); + if( newFilename==L"" ) + { + Msgbox::Alert( _("Copy canceled"), _("Copy memory card file") ); + return wxDragNone; + } + newFilename += L".ps2"; + + //check that the name is valid for a new file + wxString errMsg; + if( !isValidNewFilename( newFilename, basepath, errMsg, 5 ) ) + { + wxString message; + message.Printf(_("Error (%s)"), errMsg.c_str()); + Msgbox::Alert( message, _("Copy memory card file") ); + continue; + } + + dest.Filename = newFilename; + break; + } + + } + + wxFileName srcfile( basepath + src.Filename);//g_Conf->Mcd[src.Slot].Filename ); + wxFileName destfile( basepath + dest.Filename);//g_Conf->Mcd[dest.Slot].Filename ); + if( dest.IsPresent && dest.IsFormatted ) { wxString content; content.Printf( pxE( "!Notice:Mcd:Overwrite", - L"This will copy the entire contents of the memory card file '%s' (=slot %u) to the memory card file '%s' (=slot %u). " - L"All previous data on the memory card file '%s' will be lost. Are you sure?" ), + L"This will copy the entire contents of memory card file '%s' [=slot %u] to the memory card file '%s' [=slot %u]. " + L"All previous data on memory card file '%s' will be lost. Are you sure?" ), src.Filename.GetFullName().c_str(), src.Slot, dest.Filename.GetFullName().c_str(), dest.Slot, dest.Filename.GetFullName().c_str(), dest.Slot ); - result = Msgbox::YesNo( content, _("Overwrite memory card?") ); + result = Msgbox::YesNo( content, _("Overwrite memory card file?") ); if (!result) return wxDragNone; @@ -388,7 +427,7 @@ public: { wxString heading; heading.Printf( pxE( "!Notice:Mcd:Copy Failed", - L"Error! Could not copy the memory card into file '%s' (=slot %u). The destination file is in use." ),//xxx + L"Error! Copy failed. Destination memory card file '%s' [=slot %u] is in use." ), dest.Filename.GetFullName().c_str(), dest.Slot ); @@ -399,10 +438,19 @@ public: } // Destination memcard isEnabled state is the same now as the source's - dest.IsEnabled = src.IsEnabled; + wxString success; + success.Printf(_("Memory card file '%s' copied to '%s'.\n\nBoth card files are now identical."), + src.Filename.GetFullName().c_str(), + dest.Filename.GetFullName().c_str() + ); + Msgbox::Alert(success, _("Success")); + dest.IsPresent=true; + dest.IsEnabled = true;//src.IsEnabled; } else if( wxDragMove == def ) { + /* + // Move always performs a swap :) const bool srcExists( srcfile.FileExists() ); @@ -436,10 +484,32 @@ public: { // TODO : Popup an error to the user. - Console.Error( "(FileMcd) memory card swap failed." ); + Console.Error( "(FileMcd) memory card files swap failed." ); Console.Indent().WriteLn( L"Src : " + srcfile.GetFullPath() ); Console.Indent().WriteLn( L"Dest: " + destfile.GetFullPath() ); } + */ + + // avih: old implementation above was swapping file contents (by actually switching the file names of 2 files) + // New implementation just swaps the assigned file names at the slots. + + //Note: each slot has 2 important properties: IsPresent (with Filename) and IsEnabled. + // For the sake of usability, when draggind src to dest, if src IsPresent, automatically enable dest. + // However, src slot keeps its old IsEnabled regardless of what happened. + if (src.IsPresent || dest.IsPresent) + { + //swap file names (along with IsPresent) + wxFileName tmpFilename = dest.Filename; + bool tmpPresent = dest.IsPresent; + + dest.Filename = src.Filename; + dest.IsPresent = src.IsPresent; + if( src.IsPresent ) + dest.IsEnabled = true; + + src.Filename = tmpFilename; + src.IsPresent = tmpPresent; + } } return def; @@ -450,13 +520,20 @@ enum McdMenuId { McdMenuId_Create = 0x888, McdMenuId_Mount, - McdMenuId_Relocate, + McdMenuId_Rename, McdMenuId_RefreshList }; // ===================================================================================================== // MemoryCardListPanel_Simple (implementations) // ===================================================================================================== +/* some code from cotton to enumerate files at a folder: +[21:07] ScopedPtr memcardList(new wxArrayString()); +[21:07] wxDir::GetAllFiles(m_FolderPicker->GetPath().ToString(), memcardList, L"*.ps2*", wxDIR_FILES); +[21:07] for(uint i = 0; i < memcardList->size(); i++) { +[21:07] DevCon.WriteLn(L"hey - " + memcardList[0][i]); +[21:07] } +*/ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent ) : _parent( parent ) { @@ -467,8 +544,9 @@ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent m_listview->SetMinSize(wxSize(m_listview->GetMinWidth(), m_listview->GetCharHeight() * 13)); m_listview->SetDropTarget( new McdDropTarget(m_listview) ); - m_button_Create = new wxButton(this, wxID_ANY, _("Create")); - m_button_Mount = new wxButton(this, wxID_ANY, _("Mount")); + m_button_Create = new wxButton(this, wxID_ANY, _("Create card file")); + m_button_Mount = new wxButton(this, wxID_ANY, _("Enable port")); + m_button_Rename = new wxButton(this, wxID_ANY, _("Rename card file")); // ------------------------------------ // Sizer / Layout Section @@ -476,28 +554,33 @@ Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple( wxWindow* parent CreateLayout(); - *s_leftside_buttons += m_button_Create; - *s_leftside_buttons += 2; + *s_leftside_buttons += 20; *s_leftside_buttons += m_button_Mount; - //*s_leftside_buttons += 2; + *s_leftside_buttons += 20; + *s_leftside_buttons += m_button_Rename; + *s_leftside_buttons += 2; + *s_leftside_buttons += m_button_Create; + parent->SetWindowStyle(parent->GetWindowStyle() | wxRESIZE_BORDER); - Connect( m_listview->GetId(), wxEVT_COMMAND_LIST_BEGIN_DRAG, wxListEventHandler(MemoryCardListPanel_Simple::OnListDrag)); Connect( m_listview->GetId(), wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(MemoryCardListPanel_Simple::OnListSelectionChanged)); + + //Deselected is not working for some reason (e.g. when clicking an empty row at the table) - avih Connect( m_listview->GetId(), wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler(MemoryCardListPanel_Simple::OnListSelectionChanged)); Connect( m_listview->GetId(), wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, wxListEventHandler(MemoryCardListPanel_Simple::OnOpenItemContextMenu) ); Connect( m_button_Mount->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnMountCard)); Connect( m_button_Create->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnCreateCard)); + Connect( m_button_Rename->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnRenameFile)); // Popup Menu Connections! Connect( McdMenuId_Create, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnCreateCard) ); Connect( McdMenuId_Mount, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnMountCard) ); - Connect( McdMenuId_Relocate, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnRelocateCard) ); + Connect( McdMenuId_Rename, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnRenameFile) ); Connect( McdMenuId_RefreshList, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemoryCardListPanel_Simple::OnRefreshSelections) ); } @@ -511,25 +594,34 @@ void Panels::MemoryCardListPanel_Simple::UpdateUI() { m_button_Create->Disable(); m_button_Mount->Disable(); + m_button_Rename->Disable(); return; } - const McdSlotItem& item( GetCardForViewIndex(sel) ); + const McdSlotItem& card( GetCardForViewIndex(sel) ); + + m_button_Rename->Enable( card.IsPresent ); + wxString renameTip = _("Rename this memory card file."); + renameTip += wxString(L"\n") + _("Note: Port needs to be disabled first, and the change then needs to be applied." ); + pxSetToolTip( m_button_Rename, renameTip ); m_button_Create->Enable(); - m_button_Create->SetLabel( item.IsPresent ? _("Delete") : _("Create") ); + m_button_Create->SetLabel( card.IsPresent ? _("Delete card file") : _("Create card file") ); + wxString deleteTip = _("Permanently delete this memory card file from disk (all contents are lost)"); + deleteTip += wxString(L"\n") + _("Note: Port needs to be disabled first, and the change then needs to be applied." ); + pxSetToolTip( m_button_Create, - item.IsPresent - ? _("Deletes the existing memory card from disk (all contents are lost)." ) - : _("Creates a new memory card in the empty slot." ) + card.IsPresent + ? deleteTip + : _("Create a new memory card file and assign it to the selected PS2-Port." ) ); - m_button_Mount->Enable( item.IsPresent ); - m_button_Mount->SetLabel( item.IsEnabled ? _("Disable") : _("Enable") ); + m_button_Mount->Enable( card.IsPresent ); + m_button_Mount->SetLabel( card.IsEnabled ? _("Disable Port") : _("Enable Port") ); pxSetToolTip( m_button_Mount, - item.IsEnabled - ? _("Disables the selected memory card, so that it will not be seen by games or BIOS.") - : _("Mounts the selected memory card, so that games can see it again.") + card.IsEnabled + ? _("Disable the selected PS2-Port (this memory card will be invisible to games/BIOS).") + : _("Enable the selected PS2-Port (games/BIOS will see this memory card).") ); } @@ -544,6 +636,10 @@ void Panels::MemoryCardListPanel_Simple::Apply() for( uint slot=0; slot<8; ++slot ) { g_Conf->Mcd[slot].Enabled = m_Cards[slot].IsEnabled && m_Cards[slot].IsPresent; + if (m_Cards[slot].IsPresent) + g_Conf->Mcd[slot].Filename = m_Cards[slot].Filename; + else + g_Conf->Mcd[slot].Filename = L""; } } @@ -552,11 +648,15 @@ void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied() for( uint slot=0; slot<8; ++slot ) { m_Cards[slot].IsEnabled = g_Conf->Mcd[slot].Enabled; + m_Cards[slot].Filename = g_Conf->Mcd[slot].Filename; } _parent::AppStatusEvent_OnSettingsApplied(); } + +//BUG: the next function is never reached because, for some reason, IsoDropTarget::OnDropFiles is called instead. +// Interestingly, IsoDropTarget::OnDropFiles actually "detects" a memory card file as a valid Audio-CD ISO... - avih bool Panels::MemoryCardListPanel_Simple::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) { if( filenames.GetCount() == 1 && wxFileName(filenames[0]).IsDir() ) @@ -577,10 +677,13 @@ void Panels::MemoryCardListPanel_Simple::DoRefresh() { for( uint slot=0; slot<8; ++slot ) { - if( FileMcd_IsMultitapSlot(slot) && !m_MultitapEnabled[FileMcd_GetMtapPort(slot)] ) continue; + if( FileMcd_IsMultitapSlot(slot) && !m_MultitapEnabled[FileMcd_GetMtapPort(slot)] ) + continue; + + //wxFileName fullpath( m_FolderPicker->GetPath() + g_Conf->Mcd[slot].Filename.GetFullName() ); + wxFileName fullpath = m_FolderPicker->GetPath() + m_Cards[slot].Filename.GetFullName(); - wxFileName fullpath( m_FolderPicker->GetPath() + g_Conf->Mcd[slot].Filename.GetFullName() ); - EnumerateMemoryCard( m_Cards[slot], fullpath ); + EnumerateMemoryCard( m_Cards[slot], fullpath, m_FolderPicker->GetPath()); m_Cards[slot].Slot = slot; } @@ -598,34 +701,61 @@ void Panels::MemoryCardListPanel_Simple::OnCreateCard(wxCommandEvent& evt) McdSlotItem& card( GetCardForViewIndex(selectedViewIndex) ); if( card.IsPresent ) - { + {// called for "delete memory card" + + if ( 0<=card.Slot && card.Slot<=7 && card.Filename != g_Conf->Mcd[card.Slot].Filename) + { + Msgbox::Alert( _("Error: Please apply pending changes before proceeding."), _("Delete memory card file") ); + return; + } + + if ( 0<=card.Slot && card.Slot<=7 && g_Conf->Mcd[card.Slot].Enabled ) + { + Msgbox::Alert( _("Error: Memory card file currently in use.\n\n1. Disable this port (if not already disabled).\n2. Click 'Apply'.\n3. Proceed with Delete."), _("Delete memory card file") ); + return; + } + bool result = true; if( card.IsFormatted ) { wxString content; content.Printf( pxE( "!Notice:Mcd:Delete", - L"You are about to delete the formatted memory card file '%s' (=slot %u). " + L"You are about to delete the formatted memory card file '%s' [=slot %u]. " L"All data on this card will be lost! Are you absolutely and quite positively sure?" ), card.Filename.GetFullName().c_str() , card.Slot ); - result = Msgbox::YesNo( content, _("Delete memory card?") ); + result = Msgbox::YesNo( content, _("Delete memory card file?") ); } if( result ) { - wxFileName fullpath( m_FolderPicker->GetPath() + g_Conf->Mcd[GetSlotIndexForViewIndex( selectedViewIndex )].Filename.GetFullName() ); + wxFileName fullpath( m_FolderPicker->GetPath() + card.Filename.GetFullName());//g_Conf->Mcd[GetSlotIndexForViewIndex( selectedViewIndex )].Filename.GetFullName() ); wxRemoveFile( fullpath.GetFullPath() ); } } else - { - wxWindowID result = Dialogs::CreateMemoryCardDialog( this, GetSlotIndexForViewIndex( selectedViewIndex ), m_FolderPicker->GetPath() ).ShowModal(); - card.IsEnabled = (result != wxID_CANCEL); + {// actually create a file + Dialogs::CreateMemoryCardDialog dialog( this, GetSlotIndexForViewIndex( selectedViewIndex ), m_FolderPicker->GetPath(), L"my memory card" ); + wxWindowID result = dialog.ShowModal(); + //card.IsEnabled = (result != wxID_CANCEL); + if (result != wxID_CANCEL) + { + card.IsEnabled = true; + card.Filename = dialog.result_createdMcdFilename; + card.IsPresent = true; + Console.WriteLn(L"setting new card to slot %u: '%s'", card.Slot, card.Filename.GetFullName().c_str()); + } + else + { + card.IsEnabled=false; + } } + this->AddPendingEvent(wxCommandEvent(pxEvt_SomethingChanged));//enable the apply button (the auto-trigger doesn't get triggered here...) + RefreshSelections(); closed_core.AllowResume(); } @@ -648,7 +778,8 @@ void Panels::MemoryCardListPanel_Simple::OnMountCard(wxCommandEvent& evt) m_listview->RefreshItem(selectedViewIndex); UpdateUI(); } - +/* +//text dialog: can be used for rename: wxGetTextFromUser - avih void Panels::MemoryCardListPanel_Simple::OnRelocateCard(wxCommandEvent& evt) { evt.Skip(); @@ -664,6 +795,69 @@ void Panels::MemoryCardListPanel_Simple::OnRelocateCard(wxCommandEvent& evt) m_listview->RefreshItem(slot); UpdateUI(); } +*/ + +void Panels::MemoryCardListPanel_Simple::OnRenameFile(wxCommandEvent& evt) +{ + //evt.Skip();//what is it used for? - avih + const wxDirName basepath( m_listview->GetMcdProvider().GetMcdPath() ); + + const int viewIndex = m_listview->GetFirstSelected(); + if( wxNOT_FOUND == viewIndex ) + return; + + McdSlotItem& card( GetCardForViewIndex(viewIndex) ); + if( !card.IsPresent ){ + Console.WriteLn("Internal Error: rename mcd invoked but no file is associated. Aborting."); + return; + } + + if ( 0<=card.Slot && card.Slot<=7 && card.Filename != g_Conf->Mcd[card.Slot].Filename) + { + Msgbox::Alert( _("Error: Please apply pending changes before proceeding."), _("Rename memory card file") ); + return; + } + + if ( 0<=card.Slot && card.Slot<=7 && g_Conf->Mcd[card.Slot].Enabled) + { + Msgbox::Alert( _("Error: Memory card file currently in use.\n\n1. Disable this port (if not already disabled).\n2. Click 'Apply'.\n3. Proceed with rename.\n4. Don't forget to re-enable this port."), _("Rename memory card file") ); + return; + } + + wxString newFilename; + while (1){ + newFilename = wxGetTextFromUser(_("Select a new name for the memory card file\n( '.ps2' will be added automatically)"), _("Rename memory card file")); + if( newFilename==L"" ) + { + Msgbox::Alert( _("Rename canceled"), _("Rename memory card file") ); + return; + } + newFilename += L".ps2"; + + //check that the name is valid for a new file + wxString errMsg; + if( !isValidNewFilename( newFilename, basepath, errMsg, 5 ) ) + { + wxString message; + message.Printf(_("Error (%s)"), errMsg.c_str()); + Msgbox::Alert( message, _("Rename memory card file") ); + continue; + } + + break; + } + + if( !wxRenameFile( (basepath + card.Filename).GetFullPath(), (basepath + wxFileName(newFilename)).GetFullPath(), false ) ) + { + Msgbox::Alert( _("Error: Rename could not be completed.\n"), _("Rename memory card file") ); + return; + } + + card.Filename = newFilename; + this->DoRefresh(); + + this->AddPendingEvent(wxCommandEvent(pxEvt_SomethingChanged));//enable the apply button (the auto-trigger doesn't get triggered here...) +} void Panels::MemoryCardListPanel_Simple::OnListDrag(wxListEvent& evt) { @@ -692,9 +886,12 @@ void Panels::MemoryCardListPanel_Simple::OnOpenItemContextMenu(wxListEvent& evt) { const McdSlotItem& card( GetCardForViewIndex(idx) ); - junk->Append( McdMenuId_Create, card.IsPresent ? _("Delete") : _("Create new...") ); - junk->Append( McdMenuId_Mount, card.IsEnabled ? _("Disable") : _("Enable") ); - junk->Append( McdMenuId_Relocate, _("Relocate file...") ); + if (card.IsPresent){ + junk->Append( McdMenuId_Mount, card.IsEnabled ? _("Disable Port") : _("Enable Port") ); + junk->Append( McdMenuId_Rename, _("Rename card file...") ); + } + + junk->Append( McdMenuId_Create, card.IsPresent ? _("Delete card file") : _("Create a new card file...") ); junk->AppendSeparator(); } @@ -732,8 +929,10 @@ int Panels::MemoryCardListPanel_Simple::GetSlotIndexForViewIndex( int listViewIn { int targetSlot=-1; - //this list-view arrangement is mostly kept aligned with the slots indexes, and only takes care +/* + //this list-view arrangement is mostly kept aligned with the internal slots indexes, and only takes care // of the case where MT1 is disabled (hence the MT2 slots 2,3,4 "move backwards" 3 places on the view-index) + // However, this arrangement it's not very intuitive to use... if (!m_MultitapEnabled[0] && listViewIndex>=2) { //we got an MT2 slot. @@ -744,31 +943,30 @@ int Panels::MemoryCardListPanel_Simple::GetSlotIndexForViewIndex( int listViewIn { targetSlot=listViewIndex;//identical view-index and card slot. } +*/ - -/* //alternative arrangement of list-view: - //mcd1(=MT1 slot 1) - //[MT1 slots 2,3,4 if MT1 is enabled] + //This arrangement of list-view is as follows: + //mcd1(=MT1 port 1) + //[MT1 port 2,3,4 if MT1 is enabled] //mcd2(=MT2 slot 1) - //[MT2 slots 2,3,4 if MT2 is enabled] + //[MT2 port 2,3,4 if MT2 is enabled] if (m_MultitapEnabled[0]){ //MT1 enabled: - if (1<=listViewIndex && idx<=3){//MT1 slots 2/3/4 move one place backwards + if (1<=listViewIndex && listViewIndex<=3){//MT1 ports 2/3/4 move one place backwards targetSlot=listViewIndex+1; - }else if (listViewIndex==4){//mcd2 (=MT2 slot 1) moves 3 places forward + }else if (listViewIndex==4){//mcd2 (=MT2 port 1) moves 3 places forward targetSlot=1; - } else {//mcd1 keeps it's pos as first, MT2 slots keep their pos at the end of the list. + } else {//mcd1 keeps it's pos as first, MT2 ports keep their pos at the end of the list. targetSlot=listViewIndex; } } else { - //MT1 disabled: mcd1 and mcd2 stay put, MT2 slots 2,3,4 come next (move backwards 3 places) + //MT1 disabled: mcd1 and mcd2 stay put, MT2 ports 2,3,4 come next (move backwards 3 places) if (2<=listViewIndex && listViewIndex<=4) targetSlot=listViewIndex+3; else targetSlot=listViewIndex; } -*/ assert(targetSlot>=0); return targetSlot; diff --git a/pcsx2/gui/Panels/MemoryCardListView.cpp b/pcsx2/gui/Panels/MemoryCardListView.cpp index 1fda5287c7..8e3b783403 100644 --- a/pcsx2/gui/Panels/MemoryCardListView.cpp +++ b/pcsx2/gui/Panels/MemoryCardListView.cpp @@ -73,11 +73,11 @@ enum McdColumnType_Simple { McdColS_PortSlot, // port and slot of the card McdColS_Status, // either Enabled/Disabled, or Missing (no card). + McdColS_Filename, McdColS_Size, McdColS_Formatted, McdColS_DateModified, McdColS_DateCreated, - McdColS_Filename, McdColS_Count }; @@ -102,13 +102,13 @@ const ListViewColumnInfo& MemoryCardListView_Simple::GetDefaultColumnInfo( uint { static const ListViewColumnInfo columns[] = { - { _("PS2 Location") , 140 , wxLIST_FORMAT_CENTER }, - { _("Status") , 96 , wxLIST_FORMAT_CENTER }, - { _("Size") , 72 , wxLIST_FORMAT_LEFT }, - { _("Formatted") , 96 , wxLIST_FORMAT_CENTER }, - { _("Modified") , 120 , wxLIST_FORMAT_LEFT }, - { _("Created") , 120 , wxLIST_FORMAT_LEFT }, - { _("Filename") , 256 , wxLIST_FORMAT_LEFT }, + { _("PS2 Port") , 140 , wxLIST_FORMAT_LEFT }, + { _("Port status") , 96 , wxLIST_FORMAT_LEFT }, + { _("File name") , 256 , wxLIST_FORMAT_LEFT }, + { _("File size") , 72 , wxLIST_FORMAT_LEFT }, + { _("Formatted") , 96 , wxLIST_FORMAT_LEFT }, + { _("Last Modified"), 120 , wxLIST_FORMAT_LEFT }, + { _("Created on") , 120 , wxLIST_FORMAT_LEFT }, }; pxAssumeDev( idx < ArraySize(columns), "ListView column index is out of bounds." ); @@ -128,29 +128,38 @@ wxString MemoryCardListView_Simple::OnGetItemText(long item, long column) const { if( !m_CardProvider ) return _parent::OnGetItemText(item, column); const McdSlotItem& it( m_CardProvider->GetCardForViewIndex(item) ); + wxString prefix=L" "; switch( column ) { -// case McdColS_PortSlot: return pxsFmt( L"%u", item+1); case McdColS_PortSlot: if (!it.IsMultitapSlot()) - return pxsFmt(L"Port-%u or Multitap-%u-Port-1", it.GetMtapPort()+1, it.GetMtapPort()+1); - return pxsFmt(L" Multitap-%u-Port-%u", it.GetMtapPort()+1, it.GetMtapSlot()+1); + return pxsFmt(wxString(L" ") + _("Port-%u / Multitap-%u--Port-1"), it.GetMtapPort()+1, it.GetMtapPort()+1); + return pxsFmt(wxString(L" ") + _("Multitap-%u--Port-%u"), it.GetMtapPort()+1, it.GetMtapSlot()+1); - case McdColS_Status: return it.IsPresent ? ( it.IsEnabled ? _("Enabled") : _("Disabled")) : _("Missing"); - case McdColS_Size: return it.IsPresent ? pxsFmt( L"%u MB", it.SizeInMB ) : (wxString)_("N/A"); - case McdColS_Formatted: return it.IsFormatted ? _("Yes") : _("No"); - case McdColS_DateModified: return it.IsPresent ? it.DateModified.FormatDate() : (wxString)_("N/A"); - case McdColS_DateCreated: return it.IsPresent ? it.DateCreated.FormatDate() : (wxString)_("N/A"); + case McdColS_Status: + { + wxString res = prefix + (it.IsEnabled ? _("Enabled") : _("Disabled")); + if( !it.IsPresent ) + res = prefix + _("Empty"); + return prefix + res; + } + + case McdColS_Size: return prefix + ( !it.IsPresent ? L"" : pxsFmt( L"%u MB", it.SizeInMB ) ); + case McdColS_Formatted: return prefix + ( !it.IsPresent ? L"" : ( it.IsFormatted ? _("Yes") : _("No")) ); + case McdColS_DateModified: return prefix + ( !it.IsPresent ? L"" : it.DateModified.FormatDate() ); + case McdColS_DateCreated: return prefix + ( !it.IsPresent ? L"" : it.DateCreated.FormatDate() ); case McdColS_Filename: { + if (!it.IsPresent) return L""; + wxDirName filepath( it.Filename.GetPath() ); if (filepath.SameAs(g_Conf->Folders.MemoryCards)) - return it.Filename.GetFullName(); + return prefix + it.Filename.GetFullName(); else - return it.Filename.GetFullPath(); + return prefix + it.Filename.GetFullPath(); } } @@ -185,11 +194,11 @@ wxListItemAttr* MemoryCardListView_Simple::OnGetItemAttr(long item) const m_ItemAttr = wxListItemAttr(); // Wipe it clean! - if( !it.IsPresent ) + if( !it.IsPresent || !it.IsEnabled) m_ItemAttr.SetTextColour( *wxLIGHT_GREY ); - +/* if( m_TargetedItem == item ) m_ItemAttr.SetBackgroundColour( wxColour(L"Wheat") ); - +*/ return &m_ItemAttr; } diff --git a/pcsx2/gui/Panels/MemoryCardPanels.h b/pcsx2/gui/Panels/MemoryCardPanels.h index 67cac78232..f326c2b715 100644 --- a/pcsx2/gui/Panels/MemoryCardPanels.h +++ b/pcsx2/gui/Panels/MemoryCardPanels.h @@ -29,36 +29,43 @@ struct ListViewColumnInfo // -------------------------------------------------------------------------------------- // McdListItem / IMcdList +// +// These are the items at the list-view. +// Each item holds: +// - An internal slot association (or -1 if it isn't associated with an internal slot) +// - Properties of the internal slot (when associated with one) +// - Properties of the file associated with this list item (when associated with one) // -------------------------------------------------------------------------------------- struct McdSlotItem { - bool IsPresent; - bool IsEnabled; + int Slot; //0-7: internal slot. -1: unrelated to an internal slot (the rest of the files at the folder). + bool IsPresent; //Whether or not a file is associated with this item (true/false when 0<=Slot<=7. Always true when Slot==-1) + + //Only meaningful when IsPresent==true (a file exists for this item): + wxFileName Filename; // full pathname bool IsFormatted; - uint SizeInMB; // size, in megabytes! wxDateTime DateCreated; wxDateTime DateModified; - int Slot; - - wxFileName Filename; // full pathname (optional) - - McdSlotItem() - { - //Port = -1; - Slot = -1; - - IsPresent = false; - IsEnabled = false; - } - + //Only meaningful when 0<=Slot<=7 (associated with an internal slot): + // Properties of an internal slot, and translation from internal slot index to ps2 physical port (0-based-indexes). + bool IsEnabled; //This slot is enabled/disabled bool IsMultitapSlot() const; uint GetMtapPort() const; uint GetMtapSlot() const; bool operator==( const McdSlotItem& right ) const; bool operator!=( const McdSlotItem& right ) const; + + McdSlotItem() + { + Slot = -1; + + IsPresent = false; + IsEnabled = false; + } + }; typedef std::vector McdList; @@ -84,7 +91,7 @@ protected: IMcdList* m_CardProvider; // specifies the target of a drag&drop operation - int m_TargetedItem; + int m_TargetedItem; public: virtual ~BaseMcdListView() throw() { } @@ -183,7 +190,9 @@ namespace Panels typedef BaseMcdListPanel _parent; protected: - McdSlotItem m_Cards[8]; + McdSlotItem m_Cards[8]; + + wxButton* m_button_Rename; // Doubles as Create and Delete buttons wxButton* m_button_Create; @@ -206,7 +215,8 @@ namespace Panels protected: void OnCreateCard(wxCommandEvent& evt); void OnMountCard(wxCommandEvent& evt); - void OnRelocateCard(wxCommandEvent& evt); +// void OnRelocateCard(wxCommandEvent& evt); + void OnRenameFile(wxCommandEvent& evt); void OnListDrag(wxListEvent& evt); void OnListSelectionChanged(wxListEvent& evt);