2020-01-09 13:59:19 +01:00
// wxMASSManager
// Copyright (C) 2020 Guillaume Jacquemin
//
// This program 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.
//
// This program 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 <https://www.gnu.org/licenses/>.
# include <wx/filedlg.h>
# include <wx/msgdlg.h>
# include <wx/numdlg.h>
# include <wx/regex.h>
2020-01-12 14:54:23 +01:00
# include <Corrade/Containers/Optional.h>
2020-01-09 13:59:19 +01:00
# include <Corrade/Utility/Directory.h>
# include "EvtMainFrame.h"
using namespace Corrade ;
EvtMainFrame : : EvtMainFrame ( wxWindow * parent ) : MainFrame ( parent ) {
SetIcon ( wxIcon ( " MAINICON " ) ) ;
2020-01-12 14:54:23 +01:00
if ( ! _manager . ready ( ) ) {
errorMessage ( " There was an issue initialising the manager: \n \n " + _manager . lastError ( ) ) ;
return ;
}
2020-01-09 13:59:19 +01:00
initialiseListView ( ) ;
2020-01-10 12:23:25 +01:00
isGameRunning ( ) ;
2020-01-09 13:59:19 +01:00
_installedListView - > Connect ( wxEVT_LIST_ITEM_SELECTED , wxListEventHandler ( EvtMainFrame : : installedSelectionEvent ) , nullptr , this ) ;
_installedListView - > Connect ( wxEVT_LIST_ITEM_DESELECTED , wxListEventHandler ( EvtMainFrame : : installedSelectionEvent ) , nullptr , this ) ;
_installedListView - > Connect ( wxEVT_LIST_BEGIN_DRAG , wxListEventHandler ( EvtMainFrame : : listColumnDragEvent ) , nullptr , this ) ;
_installedListView - > Connect ( wxEVT_LIST_COL_BEGIN_DRAG , wxListEventHandler ( EvtMainFrame : : listColumnDragEvent ) , nullptr , this ) ;
warningMessage ( wxString : : FromUTF8 ( " Before you start using this app, a few things you should know: \n \n "
" For this application to work properly, Steam Cloud syncing needs to be disabled for the game. \n To disable it, right-click the game in your Steam library, click \" Properties \" , go to the \" Updates \" tab, and uncheck \" Enable Steam Cloud synchronization for M.A.S.S. Builder \" . \n \n "
" DISCLAIMER: The developer of this application cannot be held responsible for data loss or corruption. PLEASE USE AT YOUR OWN RISK! \n \n "
" Last but not least, this application is released under the terms of the GNU General Public Licence version 3. Please see the COPYING file for more details. " ) ) ;
2020-01-10 00:01:24 +01:00
_watcher . Connect ( wxEVT_FSWATCHER , wxFileSystemWatcherEventHandler ( EvtMainFrame : : fileUpdateEvent ) , nullptr , this ) ;
2020-01-12 14:54:23 +01:00
_watcher . AddTree ( wxFileName ( Utility : : Directory : : toNativeSeparators ( _manager . saveDirectory ( ) ) , wxPATH_WIN ) ,
wxFSW_EVENT_CREATE | wxFSW_EVENT_DELETE | wxFSW_EVENT_MODIFY | wxFSW_EVENT_RENAME , wxString : : Format ( " *%s.sav " , _manager . steamId ( ) ) ) ;
2020-01-10 12:23:25 +01:00
_gameCheckTimer . Start ( 3000 ) ;
2020-01-09 13:59:19 +01:00
}
EvtMainFrame : : ~ EvtMainFrame ( ) {
_installedListView - > Disconnect ( wxEVT_LIST_ITEM_SELECTED , wxListEventHandler ( EvtMainFrame : : installedSelectionEvent ) , nullptr , this ) ;
_installedListView - > Disconnect ( wxEVT_LIST_ITEM_DESELECTED , wxListEventHandler ( EvtMainFrame : : installedSelectionEvent ) , nullptr , this ) ;
_installedListView - > Disconnect ( wxEVT_LIST_BEGIN_DRAG , wxListEventHandler ( EvtMainFrame : : listColumnDragEvent ) , nullptr , this ) ;
_installedListView - > Disconnect ( wxEVT_LIST_COL_BEGIN_DRAG , wxListEventHandler ( EvtMainFrame : : listColumnDragEvent ) , nullptr , this ) ;
2020-01-10 00:01:24 +01:00
_watcher . Disconnect ( wxEVT_FSWATCHER , wxFileSystemWatcherEventHandler ( EvtMainFrame : : fileUpdateEvent ) , nullptr , this ) ;
2020-01-09 13:59:19 +01:00
}
2020-01-12 14:54:23 +01:00
bool EvtMainFrame : : ready ( ) {
return _manager . ready ( ) ;
}
2020-01-09 13:59:19 +01:00
void EvtMainFrame : : importEvent ( wxCommandEvent & ) {
2020-01-12 14:54:23 +01:00
const static std : : string error_prefix = " Importing failed: \n \n " ;
long selected_hangar = _installedListView - > GetFirstSelected ( ) ;
HangarState hangar_state = _manager . hangarState ( selected_hangar ) ;
2020-01-09 13:59:19 +01:00
2020-01-12 14:54:23 +01:00
if ( hangar_state = = HangarState : : Filled & &
wxMessageBox ( wxString : : Format ( " Hangar %.2d is already occupied by the M.A.S.S. named \" %s \" . Are you sure you want to import a M.A.S.S. to this hangar ? " ,
selected_hangar + 1 , * ( _manager . massName ( selected_hangar ) ) ) ,
2020-01-09 13:59:19 +01:00
" Question " , wxYES_NO | wxCENTRE | wxICON_QUESTION , this ) = = wxNO ) {
return ;
}
wxFileDialog dialog ( this , " Select a unit file " , wxEmptyString , wxEmptyString , " M.A.S.S. Builder unit files (*.sav) | * . sav " , wxFD_OPEN|wxFD_FILE_MUST_EXIST) ;
if ( dialog . ShowModal ( ) = = wxID_CANCEL ) {
return ;
}
const std : : string source_file = dialog . GetPath ( ) . ToUTF8 ( ) . data ( ) ;
2020-01-12 14:54:23 +01:00
Containers : : Optional < std : : string > mass_name = _manager . getMassName ( source_file ) ;
2020-01-09 13:59:19 +01:00
2020-01-12 14:54:23 +01:00
if ( ! mass_name ) {
errorMessage ( error_prefix + _manager . lastError ( ) ) ;
2020-01-09 13:59:19 +01:00
return ;
}
2020-01-12 14:54:23 +01:00
if ( wxMessageBox ( wxString : : Format ( " Are you sure you want to import the M.A.S.S. named \" %s \" to hangar %.2d ? " , * mass_name , selected_hangar + 1 ) ,
2020-01-09 13:59:19 +01:00
" Question " , wxYES_NO | wxCENTRE | wxICON_QUESTION , this ) = = wxNO ) {
return ;
}
2020-01-12 14:54:23 +01:00
switch ( _manager . gameState ( ) ) {
case GameState : : Unknown :
errorMessage ( error_prefix + " For security reasons, importing is disabled if the game's status is unknown. " ) ;
break ;
case GameState : : NotRunning :
if ( ! _manager . importMass ( source_file , selected_hangar ) ) {
errorMessage ( error_prefix + _manager . lastError ( ) ) ;
}
break ;
case GameState : : Running :
errorMessage ( error_prefix + " Importing a M.A.S.S. is disabled while the game is running. " ) ;
break ;
2020-01-09 13:59:19 +01:00
}
}
void EvtMainFrame : : moveEvent ( wxCommandEvent & ) {
2020-01-12 14:54:23 +01:00
const static std : : string error_prefix = " Move failed: \n \n " ;
2020-01-09 13:59:19 +01:00
long source_slot = _installedListView - > GetFirstSelected ( ) ;
2020-01-12 14:54:23 +01:00
long choice = wxGetNumberFromUser ( wxString : : Format ( " Which hangar do you want to move the M.A.S.S. named \" %s \" to ? \n Notes: \n "
" - If the destination hangar is the same as the source, nothing will happen. \n "
" - If the destination already contains a M.A.S.S., the two will be swapped. \n "
" - If the destination contains invalid data, it will be cleared first. " ,
* ( _manager . massName ( source_slot ) ) ) ,
2020-01-09 13:59:19 +01:00
" Slot " , " Choose a slot " , source_slot + 1 , 1 , 32 , this ) - 1 ;
if ( choice = = - 1 | | choice = = source_slot ) {
return ;
}
2020-01-12 14:54:23 +01:00
switch ( _manager . gameState ( ) ) {
case GameState : : Unknown :
errorMessage ( error_prefix + " For security reasons, moving a M.A.S.S. is disabled if the game's status is unknown. " ) ;
break ;
case GameState : : NotRunning :
if ( ! _manager . moveMass ( source_slot , choice ) ) {
errorMessage ( error_prefix + _manager . lastError ( ) ) ;
}
break ;
case GameState : : Running :
errorMessage ( error_prefix + " Moving a M.A.S.S. is disabled while the game is running. " ) ;
break ;
2020-01-09 13:59:19 +01:00
}
}
void EvtMainFrame : : deleteEvent ( wxCommandEvent & ) {
2020-01-12 14:54:23 +01:00
const static std : : string error_prefix = " Deletion failed: \n \n " ;
2020-01-09 13:59:19 +01:00
if ( wxMessageBox ( wxString : : Format ( " Are you sure you want to delete the data in hangar %.2d ? This operation cannot be undone. " , _installedListView - > GetFirstSelected ( ) + 1 ) ,
" Are you sure ? " , wxYES_NO | wxCENTRE | wxICON_QUESTION , this ) = = wxNO ) {
return ;
}
2020-01-12 14:54:23 +01:00
switch ( _manager . gameState ( ) ) {
case GameState : : Unknown :
errorMessage ( error_prefix + " For security reasons, deleting a M.A.S.S. is disabled if the game's status is unknown. " ) ;
break ;
case GameState : : NotRunning :
if ( ! _manager . deleteMass ( _installedListView - > GetFirstSelected ( ) ) ) {
errorMessage ( error_prefix + _manager . lastError ( ) ) ;
}
break ;
case GameState : : Running :
errorMessage ( error_prefix + " Deleting a M.A.S.S. is disabled while the game is running. " ) ;
break ;
2020-01-09 13:59:19 +01:00
}
}
2020-01-11 14:22:20 +01:00
void EvtMainFrame : : backupEvent ( wxCommandEvent & ) {
2020-01-12 14:54:23 +01:00
const static std : : string error_prefix = " Backup failed: \n \n " ;
2020-01-11 14:22:20 +01:00
wxString current_timestamp = wxDateTime : : Now ( ) . Format ( " %Y-%m-%d_%H-%M-%S " ) ;
2020-01-12 14:54:23 +01:00
wxFileDialog save_dialog { this , " Choose output location " , _manager . saveDirectory ( ) ,
wxString : : Format ( " backup_%s_%s.zip " , _manager . steamId ( ) , current_timestamp ) , " Zip archive (*zip)|*zip " ,
2020-01-11 14:22:20 +01:00
wxFD_SAVE | wxFD_OVERWRITE_PROMPT } ;
if ( save_dialog . ShowModal ( ) = = wxID_CANCEL ) {
return ;
}
2020-01-12 14:54:23 +01:00
if ( ! _manager . backupSaves ( save_dialog . GetPath ( ) . ToStdString ( ) ) ) {
errorMessage ( error_prefix + _manager . lastError ( ) ) ;
2020-01-11 14:22:20 +01:00
}
}
2020-01-09 13:59:19 +01:00
void EvtMainFrame : : openSaveDirEvent ( wxCommandEvent & ) {
2020-01-12 14:54:23 +01:00
wxExecute ( " explorer.exe " + Utility : : Directory : : toNativeSeparators ( _manager . saveDirectory ( ) ) ) ;
2020-01-09 13:59:19 +01:00
}
void EvtMainFrame : : installedSelectionEvent ( wxListEvent & ) {
updateCommandsState ( ) ;
}
void EvtMainFrame : : listColumnDragEvent ( wxListEvent & event ) {
event . Veto ( ) ;
}
2020-01-10 00:01:24 +01:00
void EvtMainFrame : : fileUpdateEvent ( wxFileSystemWatcherEvent & event ) {
int event_type = event . GetChangeType ( ) ;
2020-01-12 14:54:23 +01:00
wxString event_file = event . GetPath ( ) . GetFullName ( ) ;
2020-01-10 00:01:24 +01:00
if ( event_type = = wxFSW_EVENT_MODIFY & & _lastWatcherEventType = = wxFSW_EVENT_RENAME ) {
_lastWatcherEventType = event_type ;
return ;
}
2020-01-12 14:54:23 +01:00
wxMilliSleep ( 50 ) ;
2020-01-10 00:01:24 +01:00
2020-01-12 14:54:23 +01:00
wxRegEx regex ;
2020-01-10 00:01:24 +01:00
2020-01-12 14:54:23 +01:00
switch ( event_type ) {
case wxFSW_EVENT_CREATE :
case wxFSW_EVENT_DELETE :
regex . Compile ( wxString : : Format ( " Unit([0-3][0-9])%s \\ .sav " , _manager . steamId ( ) ) , wxRE_ADVANCED ) ;
if ( regex . Matches ( event_file ) ) {
long slot ;
2020-01-10 00:01:24 +01:00
2020-01-12 14:54:23 +01:00
if ( regex . GetMatch ( event_file , 1 ) . ToLong ( & slot ) & & slot > = 0 & & slot < 32 ) {
refreshHangar ( slot ) ;
}
}
break ;
case wxFSW_EVENT_MODIFY :
if ( _lastWatcherEventType = = wxFSW_EVENT_RENAME ) {
break ;
}
if ( event_file = = _manager . profileSaveName ( ) ) {
getActiveSlot ( ) ;
}
else {
regex . Compile ( wxString : : Format ( " Unit([0-3][0-9])%s \\ .sav " , _manager . steamId ( ) ) , wxRE_ADVANCED ) ;
if ( regex . Matches ( event_file ) ) {
long slot ;
if ( regex . GetMatch ( event_file , 1 ) . ToLong ( & slot ) & & slot > = 0 & & slot < 32 ) {
refreshHangar ( slot ) ;
}
}
}
break ;
case wxFSW_EVENT_RENAME :
wxString new_name = event . GetNewPath ( ) . GetFullName ( ) ;
2020-01-10 00:01:24 +01:00
2020-01-10 22:50:41 +01:00
long slot ;
2020-01-12 14:54:23 +01:00
if ( regex . Compile ( wxString : : Format ( " Unit([0-3][0-9])%s \\ .sav \\ .tmp " , _manager . steamId ( ) ) , wxRE_ADVANCED ) , regex . Matches ( new_name ) ) {
if ( regex . GetMatch ( new_name , 1 ) . ToLong ( & slot ) & & slot > = 0 & & slot < 32 ) {
refreshHangar ( slot ) ;
}
}
else if ( regex . Compile ( wxString : : Format ( " Unit([0-3][0-9]) % s \ \ . sav " , _manager.steamId()), wxRE_ADVANCED), regex.Matches(new_name)) {
if ( regex . GetMatch ( new_name , 1 ) . ToLong ( & slot ) & & slot > = 0 & & slot < 32 ) {
refreshHangar ( slot ) ;
if ( regex . Matches ( event_file ) ) {
if ( regex . GetMatch ( event_file , 1 ) . ToLong ( & slot ) & & slot > = 0 & & slot < 32 ) {
refreshHangar ( slot ) ;
}
}
}
2020-01-10 22:50:41 +01:00
}
2020-01-12 14:54:23 +01:00
break ;
2020-01-10 22:50:41 +01:00
}
2020-01-10 00:01:24 +01:00
2020-01-12 14:54:23 +01:00
_lastWatcherEventType = event_type ;
2020-01-10 00:01:24 +01:00
updateCommandsState ( ) ;
2020-01-09 13:59:19 +01:00
}
2020-01-10 12:23:25 +01:00
void EvtMainFrame : : gameCheckTimerEvent ( wxTimerEvent & ) {
isGameRunning ( ) ;
}
2020-01-09 13:59:19 +01:00
void EvtMainFrame : : initialiseListView ( ) {
for ( long i = 0 ; i < 32 ; i + + ) {
_installedListView - > InsertItem ( i , wxString : : Format ( " %.2i " , i + 1 ) ) ;
}
2020-01-10 22:50:41 +01:00
getActiveSlot ( ) ;
2020-01-09 13:59:19 +01:00
_installedListView - > SetColumnWidth ( 0 , wxLIST_AUTOSIZE_USEHEADER ) ;
_installedListView - > SetColumnWidth ( 1 , wxLIST_AUTOSIZE_USEHEADER ) ;
refreshListView ( ) ;
}
2020-01-10 12:23:25 +01:00
void EvtMainFrame : : isGameRunning ( ) {
2020-01-12 14:54:23 +01:00
GameState state = _manager . checkGameState ( ) ;
switch ( state ) {
case GameState : : Unknown :
_gameStatus - > SetLabel ( " unknown " ) ;
_gameStatus - > SetForegroundColour ( wxColour ( " orange " ) ) ;
break ;
case GameState : : NotRunning :
2020-01-10 12:23:25 +01:00
_gameStatus - > SetLabel ( " not running " ) ;
_gameStatus - > SetForegroundColour ( wxSystemSettings : : GetColour ( wxSYS_COLOUR_CAPTIONTEXT ) ) ;
2020-01-12 14:54:23 +01:00
break ;
case GameState : : Running :
_gameStatus - > SetLabel ( " running " ) ;
_gameStatus - > SetForegroundColour ( wxColour ( " red " ) ) ;
break ;
2020-01-10 12:23:25 +01:00
}
updateCommandsState ( ) ;
}
2020-01-09 13:59:19 +01:00
void EvtMainFrame : : refreshListView ( ) {
2020-01-12 14:54:23 +01:00
for ( int i = 0 ; i < 32 ; i + + ) {
refreshHangar ( i ) ;
2020-01-09 13:59:19 +01:00
}
updateCommandsState ( ) ;
}
2020-01-10 22:50:41 +01:00
void EvtMainFrame : : getActiveSlot ( ) {
2020-01-12 14:54:23 +01:00
char slot = _manager . activeSlot ( ) ;
2020-01-10 22:50:41 +01:00
2020-01-12 14:54:23 +01:00
wxFont tmp_font = _installedListView - > GetItemFont ( slot ) ;
2020-01-10 22:50:41 +01:00
tmp_font . SetWeight ( wxFONTWEIGHT_NORMAL ) ;
2020-01-12 14:54:23 +01:00
_installedListView - > SetItemFont ( slot , tmp_font ) ;
slot = _manager . getActiveSlot ( ) ;
2020-01-11 11:02:45 +01:00
2020-01-12 14:54:23 +01:00
if ( slot ! = - 1 ) {
_installedListView - > SetItemFont ( slot , _installedListView - > GetItemFont ( slot ) . Bold ( ) ) ;
2020-01-11 11:02:45 +01:00
}
2020-01-10 22:50:41 +01:00
}
2020-01-09 13:59:19 +01:00
void EvtMainFrame : : updateCommandsState ( ) {
long selection = _installedListView - > GetFirstSelected ( ) ;
2020-01-12 14:54:23 +01:00
GameState game_state = _manager . gameState ( ) ;
HangarState hangar_state = _manager . hangarState ( selection ) ;
2020-01-10 12:23:25 +01:00
2020-01-12 14:54:23 +01:00
_importButton - > Enable ( selection ! = - 1 & & game_state ! = GameState : : Running ) ;
2020-01-14 12:26:05 +01:00
_exportButton - > Enable ( selection ! = - 1 ) ;
2020-01-12 14:54:23 +01:00
_moveButton - > Enable ( selection ! = - 1 & & game_state ! = GameState : : Running & & hangar_state ! = HangarState : : Empty & & hangar_state ! = HangarState : : Invalid ) ;
_deleteButton - > Enable ( selection ! = - 1 & & game_state ! = GameState : : Running & & hangar_state ! = HangarState : : Empty ) ;
2020-01-09 13:59:19 +01:00
}
2020-01-12 14:54:23 +01:00
void EvtMainFrame : : refreshHangar ( int slot ) {
if ( slot < 0 & & slot > = 32 ) {
return ;
2020-01-09 13:59:19 +01:00
}
2020-01-12 14:54:23 +01:00
_manager . refreshHangar ( slot ) ;
2020-01-09 13:59:19 +01:00
2020-01-12 14:54:23 +01:00
switch ( _manager . hangarState ( slot ) ) {
case HangarState : : Empty :
_installedListView - > SetItem ( slot , 1 , " <Empty> " ) ;
break ;
case HangarState : : Invalid :
_installedListView - > SetItem ( slot , 1 , " <Invalid> " ) ;
break ;
case HangarState : : Filled :
_installedListView - > SetItem ( slot , 1 , * ( _manager . massName ( slot ) ) ) ;
break ;
2020-01-09 13:59:19 +01:00
}
}
void EvtMainFrame : : infoMessage ( const wxString & message ) {
wxMessageBox ( message , " Information " , wxOK | wxCENTRE | wxICON_INFORMATION , this ) ;
}
void EvtMainFrame : : warningMessage ( const wxString & message ) {
wxMessageBox ( message , " Warning " , wxOK | wxCENTRE | wxICON_WARNING , this ) ;
}
void EvtMainFrame : : errorMessage ( const wxString & message ) {
wxMessageBox ( message , " Error " , wxOK | wxCENTRE | wxICON_ERROR , this ) ;
}