2007-05-30 21:56:52 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
2002-09-27 23:27:14 +00:00
*
2021-12-26 18:47:58 +01:00
* 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 .
2002-09-27 23:27:14 +00:00
*
* 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
2021-12-26 18:47:58 +01:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2014-02-18 02:34:20 +01:00
*
2002-09-27 23:27:14 +00:00
*/
2021-07-12 19:48:19 +05:30
# include "base/version.h"
2003-11-05 00:59:03 +00:00
# include "common/config-manager.h"
2021-07-12 19:48:19 +05:30
# include "common/events.h"
# include "common/fs.h"
# include "common/util.h"
# include "common/system.h"
# include "common/translation.h"
# include "gui/about.h"
# include "gui/browser.h"
# include "gui/chooser.h"
2023-08-02 13:23:54 +05:30
# include "gui/dlcsdialog.h"
2021-07-12 19:48:19 +05:30
# include "gui/editgamedialog.h"
2023-05-05 00:44:00 +02:00
# include "gui/helpdialog.h"
2021-07-12 19:48:19 +05:30
# include "gui/launcher.h"
# include "gui/massadd.h"
# include "gui/message.h"
2021-07-07 19:45:46 +05:30
# include "gui/gui-manager.h"
2021-07-12 19:48:19 +05:30
# include "gui/options.h"
# ifdef ENABLE_EVENTRECORDER
# include "gui/onscreendialog.h"
# include "gui/recorderdialog.h"
# include "gui/EventRecorder.h"
# endif
# include "gui/saveload.h"
# include "gui/unknown-game-dialog.h"
# include "gui/widgets/edittext.h"
2021-07-17 16:30:01 +05:30
# include "gui/widgets/groupedlist.h"
2021-07-12 19:48:19 +05:30
# include "gui/widgets/tab.h"
# include "gui/widgets/popup.h"
# include "gui/widgets/grid.h"
2021-07-07 19:45:46 +05:30
# include "gui/ThemeEval.h"
2021-07-12 19:48:19 +05:30
# include "engines/advancedDetector.h"
2003-11-05 00:59:03 +00:00
2021-07-12 19:48:19 +05:30
# include "graphics/cursorman.h"
# if defined(USE_CLOUD) && defined(USE_LIBCURL)
# include "backends/cloud/cloudmanager.h"
# endif
2023-08-08 18:00:50 +05:30
# if defined(USE_DLC)
2023-08-03 00:18:24 +05:30
# include "backends/dlc/dlcmanager.h"
2023-08-05 00:27:35 +05:30
# endif
2002-09-27 23:27:14 +00:00
2003-11-10 23:40:48 +00:00
namespace GUI {
2021-07-12 19:48:19 +05:30
enum {
kStartCmd = ' STRT ' ,
kAboutCmd = ' ABOU ' ,
kOptionsCmd = ' OPTN ' ,
kAddGameCmd = ' ADDG ' ,
kMassAddGameCmd = ' MADD ' ,
2023-06-13 00:32:36 +05:30
kDownloadGameCmd = ' DWNG ' ,
2021-07-12 19:48:19 +05:30
kEditGameCmd = ' EDTG ' ,
kRemoveGameCmd = ' REMG ' ,
kLoadGameCmd = ' LOAD ' ,
kRecordGameCmd = ' RECG ' ,
kQuitCmd = ' QUIT ' ,
kSearchCmd = ' SRCH ' ,
kListSearchCmd = ' LSSR ' ,
kSearchClearCmd = ' SRCL ' ,
2021-07-22 21:24:25 +05:30
kSetGroupMethodCmd = ' GPBY ' ,
2023-05-05 00:44:00 +02:00
kHelpCmd = ' HELP ' ,
2021-07-12 19:48:19 +05:30
2021-11-10 16:21:51 +01:00
kListSwitchCmd = ' LIST ' ,
kGridSwitchCmd = ' GRID ' ,
2021-07-12 19:48:19 +05:30
kCmdGlobalGraphicsOverride = ' OGFX ' ,
kCmdGlobalAudioOverride = ' OSFX ' ,
kCmdGlobalMIDIOverride = ' OMID ' ,
kCmdGlobalMT32Override = ' OM32 ' ,
kCmdGlobalVolumeOverride = ' OVOL ' ,
kCmdChooseSoundFontCmd = ' chsf ' ,
kCmdExtraBrowser = ' PEXT ' ,
kCmdExtraPathClear = ' PEXC ' ,
kCmdGameBrowser = ' PGME ' ,
kCmdSaveBrowser = ' PSAV ' ,
kCmdSavePathClear = ' PSAC '
} ;
2022-02-23 00:20:47 +00:00
const GroupingMode groupingModes [ ] = {
// I18N: Group name for the game list
2022-12-23 11:18:10 +00:00
{ " none " , _sc ( " None " , " group " ) , nullptr , kGroupByNone } ,
2022-02-23 00:20:47 +00:00
// I18N: Group name for the game list, grouped by the first letter of the game title
2022-12-23 11:18:10 +00:00
{ " initial " , _sc ( " First letter " , " group " ) , _sc ( " Initial " , " group " ) , kGroupByFirstLetter } ,
2022-02-23 00:20:47 +00:00
// I18N: Group name for the game list, grouped by game engine
2022-12-23 11:18:10 +00:00
{ " engine " , _sc ( " Engine " , " group " ) , nullptr , kGroupByEngine } ,
2022-02-23 00:20:47 +00:00
// I18N: Group name for the game list, grouped by game series
2022-12-23 11:18:10 +00:00
{ " series " , _sc ( " Series " , " group " ) , nullptr , kGroupBySeries } ,
2022-02-23 00:20:47 +00:00
// I18N: Group name for the game list, grouped by game publisher
2022-12-23 11:18:10 +00:00
{ " company " , _sc ( " Publisher " , " group " ) , nullptr , kGroupByCompany } ,
2022-02-23 00:20:47 +00:00
// I18N: Group name for the game list, grouped by language
2022-12-23 11:18:10 +00:00
{ " language " , _sc ( " Language " , " group " ) , nullptr , kGroupByLanguage } ,
2022-02-23 00:20:47 +00:00
// I18N: Group name for the game list, grouped by game platform
2022-12-23 11:18:10 +00:00
{ " platform " , _sc ( " Platform " , " group " ) , nullptr , kGroupByPlatform } ,
2023-11-10 23:45:15 +01:00
// I18N: Group name for the game list, grouped by year
{ " year " , _sc ( " Year " , " year " ) , nullptr , kGroupByYear } ,
2022-12-23 11:18:10 +00:00
{ nullptr , nullptr , nullptr , kGroupByNone }
2022-02-23 00:20:47 +00:00
} ;
2021-07-12 19:48:19 +05:30
# pragma mark -
2023-06-06 01:12:17 +02:00
static Common : : String buildQualifiedGameName ( const Common : : String & engineId , const Common : : String & gameId ) {
return Common : : String : : format ( " %s:%s " , engineId . c_str ( ) , gameId . c_str ( ) ) ;
}
2022-09-18 13:20:35 +02:00
bool LauncherFilterMatcher ( void * boss , int idx , const Common : : U32String & item , const Common : : U32String & token_ ) {
2021-07-12 19:48:19 +05:30
bool invert = false ;
2022-09-18 13:20:35 +02:00
Common : : U32String token ( token_ ) ;
2021-07-12 19:48:19 +05:30
while ( token . size ( ) & & token [ 0 ] = = ' ! ' ) {
token = token . substr ( 1 ) ;
invert = ! invert ;
}
bool result = false ;
Common : : String token8 = token ;
size_t pos = token8 . findFirstOf ( " :=~ " ) ;
if ( pos ! = token8 . npos ) {
Common : : String key = token8 . substr ( 0 , token8 . findFirstOf ( token8 [ pos ] ) ) ;
Common : : String filter = token8 . substr ( token8 . findFirstOf ( token8 [ pos ] ) + 1 ) ;
if ( key . size ( ) ) {
if ( Common : : String ( " description " ) . hasPrefix ( key ) ) {
key = " description " ;
} else if ( Common : : String ( " engineid " ) . hasPrefix ( key ) ) {
key = " engineid " ;
} else if ( Common : : String ( " gameid " ) . hasPrefix ( key ) ) {
key = " gameid " ;
} else if ( Common : : String ( " language " ) . hasPrefix ( key ) ) {
key = " language " ;
} else if ( Common : : String ( " path " ) . hasPrefix ( key ) ) {
key = " path " ;
} else if ( Common : : String ( " platform " ) . hasPrefix ( key ) ) {
key = " platform " ;
}
}
LauncherDialog * launcher = ( LauncherDialog * ) ( boss ) ;
Common : : String data = launcher - > getGameConfig ( idx , key ) ;
data . toLowercase ( ) ;
if ( token8 [ pos ] = = ' : ' ) {
result = data . contains ( filter ) ;
} else if ( token8 [ pos ] = = ' = ' ) {
result = data = = filter ;
} else if ( token8 [ pos ] = = ' ~ ' ) {
result = data . matchString ( filter ) ;
}
} else {
result = item . contains ( token ) ;
}
return invert ? ! result : result ;
}
2023-06-06 01:12:17 +02:00
LauncherDialog : : LauncherDialog ( const Common : : String & dialogName )
2021-07-12 19:48:19 +05:30
: Dialog ( dialogName ) , _title ( dialogName ) , _browser ( nullptr ) ,
2021-07-23 18:29:19 +05:30
_loadDialog ( nullptr ) , _searchClearButton ( nullptr ) , _searchDesc ( nullptr ) ,
2023-06-06 01:12:17 +02:00
_grpChooserDesc ( nullptr ) , _grpChooserPopup ( nullptr ) , _groupBy ( kGroupByNone )
2021-07-12 19:48:19 +05:30
# ifndef DISABLE_FANCY_THEMES
2021-11-12 23:01:02 +01:00
, _logo ( nullptr ) , _searchPic ( nullptr ) , _groupPic ( nullptr )
2021-07-12 19:48:19 +05:30
# endif // !DISABLE_FANCY_THEMES
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
, _listButton ( nullptr ) , _gridButton ( nullptr )
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
{
_backgroundType = GUI : : ThemeEngine : : kDialogBackgroundMain ;
GUI : : GuiManager : : instance ( ) . _launched = true ;
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
2021-08-13 16:44:32 +05:30
addLayoutChooserButtons ( ) ;
2021-07-12 19:48:19 +05:30
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
2021-11-12 14:14:03 +01:00
2021-08-08 19:14:59 +05:30
Common : : ArchiveMemberList mdFiles ;
2021-11-12 14:14:03 +01:00
2022-06-29 19:27:27 +01:00
g_gui . lockIconsSet ( ) ;
2021-11-12 14:14:03 +01:00
g_gui . getIconsSet ( ) . listMatchingMembers ( mdFiles , " *.xml " ) ;
2021-08-08 19:14:59 +05:30
for ( Common : : ArchiveMemberList : : iterator md = mdFiles . begin ( ) ; md ! = mdFiles . end ( ) ; + + md ) {
if ( _metadataParser . loadStream ( ( * md ) - > createReadStream ( ) ) = = false ) {
2021-11-10 11:30:56 +01:00
warning ( " Failed to load XML file '%s' " , ( * md ) - > getDisplayName ( ) . encode ( ) . c_str ( ) ) ;
2021-08-08 19:14:59 +05:30
_metadataParser . close ( ) ;
}
if ( _metadataParser . parse ( ) = = false ) {
2021-11-10 11:30:56 +01:00
warning ( " Failed to parse XML file '%s' " , ( * md ) - > getDisplayName ( ) . encode ( ) . c_str ( ) ) ;
2021-08-08 19:14:59 +05:30
}
2022-01-03 13:32:01 +01:00
_metadataParser . close ( ) ;
2021-08-08 19:14:59 +05:30
}
2022-06-29 19:27:27 +01:00
g_gui . unlockIconsSet ( ) ;
2023-08-03 00:18:24 +05:30
2023-08-10 00:24:33 +05:30
# if defined(USE_DLC)
2023-08-08 18:00:50 +05:30
if ( g_system - > hasFeature ( OSystem : : kFeatureDLC ) ) {
DLCMan . setLauncher ( this ) ;
}
2023-08-05 00:27:35 +05:30
# endif
2021-07-12 19:48:19 +05:30
}
LauncherDialog : : ~ LauncherDialog ( ) {
delete _browser ;
delete _loadDialog ;
}
void LauncherDialog : : build ( ) {
2021-11-12 23:01:02 +01:00
# ifndef DISABLE_FANCY_THEMES
if ( g_gui . xmlEval ( ) - > getVar ( " Globals.ShowSearchPic " ) = = 1 & & g_gui . theme ( ) - > supportsImages ( ) ) {
_grpChooserDesc = nullptr ;
_groupPic = new GraphicsWidget ( this , _title + " .GroupPic " , _ ( " Select Group by " ) ) ;
_groupPic - > setGfxFromTheme ( ThemeEngine : : kImageGroup ) ;
} else
# endif
2022-12-23 11:18:10 +00:00
_grpChooserDesc = new StaticTextWidget ( this , Common : : String ( _title + " .laGroupPopupDesc " ) , _ ( " Group: " ) ) ;
2021-11-12 23:01:02 +01:00
2022-12-23 11:18:10 +00:00
_grpChooserPopup = new PopUpWidget ( this , Common : : String ( _title + " .laGroupPopup " ) , _ ( " Select a criteria to group the entries " ) , kSetGroupMethodCmd ) ;
2022-02-23 00:20:47 +00:00
Common : : String grouping = ConfMan . get ( " grouping " ) ;
const GroupingMode * mode = groupingModes ;
while ( mode - > name ) {
2023-10-03 23:41:14 +01:00
if ( mode - > lowresDescription & & g_gui . useLowResGUI ( ) ) {
2022-12-23 11:18:10 +00:00
_grpChooserPopup - > appendEntry ( _c ( mode - > lowresDescription , " group " ) , mode - > id ) ;
} else {
_grpChooserPopup - > appendEntry ( _c ( mode - > description , " group " ) , mode - > id ) ;
}
2022-02-23 00:20:47 +00:00
if ( grouping = = mode - > name )
_groupBy = mode - > id ;
+ + mode ;
}
_grpChooserPopup - > setSelected ( _groupBy ) ;
2022-12-23 11:18:10 +00:00
# ifndef DISABLE_FANCY_THEMES
_logo = nullptr ;
2021-07-12 19:48:19 +05:30
if ( g_gui . xmlEval ( ) - > getVar ( " Globals.ShowLauncherLogo " ) = = 1 & & g_gui . theme ( ) - > supportsImages ( ) ) {
2021-08-14 21:41:21 +05:30
_logo = new GraphicsWidget ( this , _title + " .Logo " ) ;
2021-07-12 19:48:19 +05:30
_logo - > setGfxFromTheme ( ThemeEngine : : kImageLogo ) ;
2021-08-14 21:41:21 +05:30
new StaticTextWidget ( this , _title + " .Version " , Common : : U32String ( gScummVMVersionDate ) ) ;
2021-07-12 19:48:19 +05:30
} else
# endif
2022-12-23 11:18:10 +00:00
new StaticTextWidget ( this , _title + " .Version " , Common : : U32String ( gScummVMFullVersion ) ) ;
2023-09-25 23:32:05 +02:00
new ButtonWidget ( this , _title + " .HelpButton " , Common : : U32String ( " ? " ) , _ ( " Help " ) , kHelpCmd ) ;
2023-05-05 00:44:00 +02:00
2023-03-26 18:02:02 +02:00
if ( ! g_system - > hasFeature ( OSystem : : kFeatureNoQuit ) ) {
// I18N: Button Quit ScummVM program. Q is the shortcut, Ctrl+Q, put it in parens for non-latin (~Q~)
2021-08-14 21:41:21 +05:30
new ButtonWidget ( this , _title + " .QuitButton " , _ ( " ~Q~uit " ) , _ ( " Quit ScummVM " ) , kQuitCmd ) ;
2023-03-26 18:02:02 +02:00
}
2022-05-15 05:16:29 -05:00
2023-03-26 18:02:02 +02:00
// I18N: Button About ScummVM program. b is the shortcut, Ctrl+b, put it in parens for non-latin (~b~)
GUI: Changing "About..." button to "About"
The current recommended and de factor behavior from both Microsoft and Apple is to have "About" menus be without trailing ellipses. Therefore, I think we should follow suit.
Traditionally, About menus and buttons had a trailing ellipsis. [This article](https://uxdesign.cc/dot-dot-dot-7ce6170bfc7f) demonstrates this behavior for both Mac OS 1.0 (technically "System 1.0") and Windows 1.0.
However, current versions of macOS and Windows do not have a trailing ellipsis in About menus. Moreover, their design guidelines recommend not using them. From [Apple](https://developer.apple.com/design/human-interface-guidelines/macos/menus/menu-anatomy/):
> Use an ellipsis whenever choosing a menu item requires additional input from the user. The ellipsis character (…) means a dialog or separate window will open and prompt the user for additional information or to make a choice.
Since no additional input is required from the user for the About menu, it is inappropriate to use an ellipsis according to these guidlelines.
Microsoft [explicitly says](https://docs.microsoft.com/en-us/windows/win32/uxguide/ctrl-command-buttons) that About menus should not have an ellipsis (emphasis mine):
> This doesn't mean you should use an ellipsis whenever an action displays another window only when additional information is required to perform the action. Consequently,** any command button whose implicit verb is to "show another window" doesn't take an ellipsis, such as with the commands About**, Advanced, Help (or any other command linking to a Help topic), Options, Properties, or Settings.
Given all this, I think we should be in line with current best practices and remove it from the menu.
2022-01-25 19:22:12 -06:00
new ButtonWidget ( this , _title + " .AboutButton " , _ ( " A~b~out " ) , _ ( " About ScummVM " ) , kAboutCmd ) ;
2023-03-26 18:02:02 +02:00
// I18N: Button caption. O is the shortcut, Ctrl+O, put it in parens for non-latin (~O~)
2022-05-23 16:36:56 -05:00
new ButtonWidget ( this , _title + " .OptionsButton " , _ ( " Global ~O~ptions... " ) , _ ( " Change global ScummVM options " ) , kOptionsCmd , 0 , _c ( " Global ~O~pts... " , " lowres " ) ) ;
2023-06-26 11:57:22 +05:30
2023-08-10 00:24:33 +05:30
# if defined(USE_DLC)
2023-08-08 18:00:50 +05:30
if ( g_system - > hasFeature ( OSystem : : kFeatureDLC ) ) {
2023-06-26 11:57:22 +05:30
new ButtonWidget ( this , _title + " .DownloadGamesButton " , _ ( " Download Games " ) , _ ( " Download freeware games for ScummVM " ) , kDownloadGameCmd ) ;
}
2023-08-08 18:00:50 +05:30
# endif
2021-08-14 21:41:21 +05:30
// Above the lowest button rows: two more buttons (directly below the list box)
2022-05-15 05:16:29 -05:00
DropdownButtonWidget * addButton =
2023-03-26 18:02:02 +02:00
// I18N: Button caption. A is the shortcut, Ctrl+A, put it in parens for non-latin (~A~)
2022-05-15 05:16:29 -05:00
new DropdownButtonWidget ( this , _title + " .AddGameButton " , _ ( " ~A~dd Game... " ) , _ ( " Add games to the list " ) , kAddGameCmd , 0 , _c ( " ~A~dd Game... " , " lowres " ) ) ;
_addButton = addButton ;
_removeButton =
2023-03-26 18:02:02 +02:00
// I18N: Button caption. R is the shortcut, Ctrl+R, put it in parens for non-latin (~R~)
2022-05-15 05:16:29 -05:00
new ButtonWidget ( this , _title + " .RemoveGameButton " , _ ( " ~R~emove Game " ) , _ ( " Remove game from the list. The game data files stay intact " ) , kRemoveGameCmd , 0 , _c ( " ~R~emove Game " , " lowres " ) ) ;
2023-10-03 23:41:14 +01:00
if ( ! g_gui . useLowResGUI ( ) ) {
2023-03-26 18:02:02 +02:00
// I18N: Button caption. Mass add games
2021-08-14 21:41:21 +05:30
addButton - > appendEntry ( _ ( " Mass Add... " ) , kMassAddGameCmd ) ;
} else {
2023-03-26 18:02:02 +02:00
// I18N: Button caption for lower resolution GUI. Mass add games
2021-08-14 21:41:21 +05:30
addButton - > appendEntry ( _c ( " Mass Add... " , " lowres " ) , kMassAddGameCmd ) ;
}
2021-07-12 19:48:19 +05:30
// Search box
2021-08-14 21:41:21 +05:30
_searchDesc = nullptr ;
2021-07-12 19:48:19 +05:30
# ifndef DISABLE_FANCY_THEMES
_searchPic = nullptr ;
if ( g_gui . xmlEval ( ) - > getVar ( " Globals.ShowSearchPic " ) = = 1 & & g_gui . theme ( ) - > supportsImages ( ) ) {
2021-08-14 21:41:21 +05:30
_searchPic = new GraphicsWidget ( this , _title + " .SearchPic " , _ ( " Search in game list " ) ) ;
2021-07-12 19:48:19 +05:30
_searchPic - > setGfxFromTheme ( ThemeEngine : : kImageSearch ) ;
} else
# endif
2021-08-14 21:41:21 +05:30
_searchDesc = new StaticTextWidget ( this , _title + " .SearchDesc " , _ ( " Search: " ) ) ;
2021-07-12 19:48:19 +05:30
2021-08-14 21:41:21 +05:30
_searchWidget = new EditTextWidget ( this , _title + " .Search " , _search , Common : : U32String ( ) , kSearchCmd ) ;
_searchClearButton = addClearButton ( this , _title + " .SearchClearButton " , kSearchClearCmd ) ;
2021-07-12 19:48:19 +05:30
// Create file browser dialog
_browser = new BrowserDialog ( _ ( " Select directory with game data " ) , true ) ;
// Create Load dialog
_loadDialog = new SaveLoadChooser ( _ ( " Load game: " ) , _ ( " Load " ) , false ) ;
}
void LauncherDialog : : clean ( ) {
while ( _firstWidget ) {
Widget * w = _firstWidget ;
removeWidget ( w ) ;
// This is called from rebuild() which may result from handleCommand being called by
// a child widget sendCommand call. In such a case sendCommand is still being executed
// so we should not delete yet the child widget. Thus delay the deletion.
g_gui . addToTrash ( w , this ) ;
}
delete _browser ;
delete _loadDialog ;
}
void LauncherDialog : : rebuild ( ) {
clean ( ) ;
build ( ) ;
reflowLayout ( ) ;
setDefaultFocusedWidget ( ) ;
}
int LauncherDialog : : run ( ) {
// Open up
open ( ) ;
// Start processing events
g_gui . runLoop ( ) ;
// Return the result code
return getResult ( ) ;
}
void LauncherDialog : : open ( ) {
// Clear the active domain, in case we return to the dialog from a
// failure to launch a game. Otherwise, pressing ESC will attempt to
// re-launch the same game again.
ConfMan . setActiveDomain ( " " ) ;
CursorMan . popAllCursors ( ) ;
Dialog : : open ( ) ;
updateButtons ( ) ;
}
void LauncherDialog : : close ( ) {
// Save last selection
const int sel = getSelected ( ) ;
if ( sel > = 0 )
2021-11-10 16:28:10 +01:00
ConfMan . set ( " lastselectedgame " , _domains [ sel ] , Common : : ConfigManager : : kApplicationDomain ) ;
2021-07-12 19:48:19 +05:30
else
2021-11-10 16:28:10 +01:00
ConfMan . removeKey ( " lastselectedgame " , Common : : ConfigManager : : kApplicationDomain ) ;
2021-07-12 19:48:19 +05:30
ConfMan . flushToDisk ( ) ;
Dialog : : close ( ) ;
}
struct LauncherEntryComparator {
bool operator ( ) ( const LauncherEntry & x , const LauncherEntry & y ) const {
return scumm_compareDictionary ( x . description . c_str ( ) , y . description . c_str ( ) ) < 0 ;
}
} ;
void LauncherDialog : : addGame ( ) {
// Allow user to add a new game to the list.
// 1) show a dir selection dialog which lets the user pick the directory
// the game data resides in.
// 2) try to auto detect which game is in the directory, if we cannot
// determine it uniquely present a list of candidates to the user
// to pick from
// 3) Display the 'Edit' dialog for that item, letting the user specify
// an alternate description (to distinguish multiple versions of the
// game, e.g. 'Monkey German' and 'Monkey English') and set default
// options for that game
// 4) If no game is found in the specified directory, return to the
// dialog.
bool looping ;
do {
looping = false ;
if ( _browser - > runModal ( ) > 0 ) {
// User made his choice...
# if defined(USE_CLOUD) && defined(USE_LIBCURL)
2023-09-03 09:43:40 +02:00
Common : : Path selectedDirectory = _browser - > getResult ( ) . getPath ( ) ;
Common : : Path bannedDirectory = CloudMan . getDownloadLocalDirectory ( ) ;
selectedDirectory . removeTrailingSeparators ( ) ;
bannedDirectory . removeTrailingSeparators ( ) ;
if ( ! selectedDirectory . empty ( ) & & ! bannedDirectory . empty ( ) & & selectedDirectory . equalsIgnoreCase ( bannedDirectory ) ) {
2021-07-12 19:48:19 +05:30
MessageDialog alert ( _ ( " This directory cannot be used yet, it is being downloaded into! " ) ) ;
alert . runModal ( ) ;
return ;
}
# endif
looping = ! doGameDetection ( _browser - > getResult ( ) . getPath ( ) ) ;
}
} while ( looping ) ;
}
void LauncherDialog : : massAddGame ( ) {
MessageDialog alert ( _ ( " Do you really want to run the mass game detector? "
" This could potentially add a huge number of games. " ) , _ ( " Yes " ) , _ ( " No " ) ) ;
if ( alert . runModal ( ) = = GUI : : kMessageOK & & _browser - > runModal ( ) > 0 ) {
2023-08-15 23:48:47 +03:00
ADCacheMan . clear ( ) ;
2021-07-12 19:48:19 +05:30
MassAddDialog massAddDlg ( _browser - > getResult ( ) ) ;
massAddDlg . runModal ( ) ;
// Update the ListWidget and force a redraw
// If new target(s) were added, update the ListWidget and move
// the selection to to first newly detected game.
Common : : String newTarget = massAddDlg . getFirstAddedTarget ( ) ;
if ( ! newTarget . empty ( ) ) {
updateListing ( ) ;
selectTarget ( newTarget ) ;
}
g_gui . scheduleTopDialogRedraw ( ) ;
}
}
Common : : String LauncherDialog : : getGameConfig ( int item , Common : : String key ) {
if ( ConfMan . hasKey ( key , _domains [ item ] ) ) {
return ConfMan . get ( key , _domains [ item ] ) ;
}
return " " ;
}
void LauncherDialog : : removeGame ( int item ) {
MessageDialog alert ( _ ( " Do you really want to remove this game configuration? " ) , _ ( " Yes " ) , _ ( " No " ) ) ;
if ( alert . runModal ( ) = = GUI : : kMessageOK ) {
// Remove the currently selected game from the list
assert ( item > = 0 ) ;
ConfMan . removeGameDomain ( _domains [ item ] ) ;
// Write config to disk
ConfMan . flushToDisk ( ) ;
// Update the ListWidget/GridWidget and force a redraw
updateListing ( ) ;
g_gui . scheduleTopDialogRedraw ( ) ;
}
}
void LauncherDialog : : editGame ( int item ) {
// Set game specific options. Most of these should be "optional", i.e. by
// default set nothing and use the global ScummVM settings. E.g. the user
// can set here an optional alternate music volume, or for specific games
// a different music driver etc.
// This is useful because e.g. MonkeyVGA needs AdLib music to have decent
// music support etc.
assert ( item > = 0 ) ;
EditGameDialog editDialog ( _domains [ item ] ) ;
if ( editDialog . runModal ( ) > 0 ) {
// User pressed OK, so make changes permanent
// Write config to disk
ConfMan . flushToDisk ( ) ;
// Update the ListWidget, reselect the edited game and force a redraw
updateListing ( ) ;
selectTarget ( editDialog . getDomain ( ) ) ;
g_gui . scheduleTopDialogRedraw ( ) ;
}
}
# ifdef ENABLE_EVENTRECORDER
void LauncherDialog : : recordGame ( int item ) {
RecorderDialog recorderDialog ;
MessageDialog alert ( _ ( " Do you want to load saved game? " ) ,
_ ( " Yes " ) , _ ( " No " ) ) ;
switch ( recorderDialog . runModal ( _domains [ item ] ) ) {
default :
// fallthrough intended
case RecorderDialog : : kRecordDialogClose :
break ;
case RecorderDialog : : kRecordDialogPlayback :
ConfMan . setActiveDomain ( _domains [ item ] ) ;
close ( ) ;
2021-11-10 16:28:10 +01:00
ConfMan . set ( " record_mode " , " playback " , Common : : ConfigManager : : kTransientDomain ) ;
ConfMan . set ( " record_file_name " , recorderDialog . getFileName ( ) , Common : : ConfigManager : : kTransientDomain ) ;
2021-07-12 19:48:19 +05:30
break ;
case RecorderDialog : : kRecordDialogRecord :
ConfMan . setActiveDomain ( _domains [ item ] ) ;
if ( alert . runModal ( ) = = GUI : : kMessageOK ) {
loadGame ( item ) ;
}
close ( ) ;
g_eventRec . setAuthor ( recorderDialog . _author ) ;
g_eventRec . setName ( recorderDialog . _name ) ;
g_eventRec . setNotes ( recorderDialog . _notes ) ;
2021-11-10 16:28:10 +01:00
ConfMan . set ( " record_mode " , " record " , Common : : ConfigManager : : kTransientDomain ) ;
2021-07-12 19:48:19 +05:30
break ;
}
}
# endif
void LauncherDialog : : loadGame ( int item ) {
2021-11-10 15:56:44 +01:00
Common : : String target = _domains [ item ] ;
2021-07-12 19:48:19 +05:30
target . toLowercase ( ) ;
EngineMan . upgradeTargetIfNecessary ( target ) ;
// Look for the plugin
const Plugin * metaEnginePlugin = nullptr ;
const Plugin * enginePlugin = nullptr ;
EngineMan . findTarget ( target , & metaEnginePlugin ) ;
// If we found a relevant plugin, find the matching engine plugin.
if ( metaEnginePlugin ) {
enginePlugin = PluginMan . getEngineFromMetaEngine ( metaEnginePlugin ) ;
}
if ( enginePlugin ) {
assert ( enginePlugin - > getType ( ) = = PLUGIN_TYPE_ENGINE ) ;
const MetaEngine & metaEngine = enginePlugin - > get < MetaEngine > ( ) ;
if ( metaEngine . hasFeature ( MetaEngine : : kSupportsListSaves ) & &
metaEngine . hasFeature ( MetaEngine : : kSupportsLoadingDuringStartup ) ) {
int slot = _loadDialog - > runModalWithMetaEngineAndTarget ( & metaEngine , target ) ;
if ( slot > = 0 ) {
ConfMan . setActiveDomain ( _domains [ item ] ) ;
ConfMan . setInt ( " save_slot " , slot , Common : : ConfigManager : : kTransientDomain ) ;
close ( ) ;
}
} else {
MessageDialog dialog
( _ ( " This game does not support loading games from the launcher. " ) , _ ( " OK " ) ) ;
dialog . runModal ( ) ;
}
} else {
MessageDialog dialog ( _ ( " ScummVM could not find any engine capable of running the selected game! " ) , _ ( " OK " ) ) ;
dialog . runModal ( ) ;
}
}
2023-06-06 01:12:17 +02:00
Common : : Array < LauncherEntry > LauncherDialog : : generateEntries ( const Common : : ConfigManager : : DomainMap & domains ) {
Common : : Array < LauncherEntry > domainList ;
for ( Common : : ConfigManager : : DomainMap : : const_iterator iter = domains . begin ( ) ; iter ! = domains . end ( ) ; + + iter ) {
// Do not list temporary targets added when starting a game from the command line
if ( iter - > _value . contains ( " id_came_from_command_line " ) )
continue ;
Common : : String description ;
Common : : String title ;
if ( ! iter - > _value . tryGetVal ( " description " , description ) ) {
QualifiedGameDescriptor g = EngineMan . findTarget ( iter - > _key ) ;
if ( ! g . description . empty ( ) )
description = g . description ;
}
Common : : String engineid = iter - > _value . getValOrDefault ( " engineid " ) ;
Common : : String gameid ;
if ( ! iter - > _value . tryGetVal ( " gameid " , gameid ) ) {
gameid = iter - > _key ;
}
Common : : StringMap & engineMap = _engines [ engineid ] ;
if ( ! engineMap . contains ( gameid ) ) {
const Plugin * plugin = EngineMan . findPlugin ( engineid ) ;
if ( plugin ) {
PlainGameDescriptor gd = plugin - > get < MetaEngineDetection > ( ) . findGame ( gameid . c_str ( ) ) ;
if ( gd . description )
engineMap [ gameid ] = gd . description ;
}
}
// Either game description or empty (default) string
title = engineMap [ gameid ] ;
// This is not reliable
if ( ! title . empty ( ) & & gameid . contains ( " -demo " ) )
title + = " (Demo) " ;
if ( description . empty ( ) ) {
description = Common : : String : : format ( " Unknown (target %s, gameid %s) " , iter - > _key . c_str ( ) , gameid . c_str ( ) ) ;
}
if ( title . empty ( ) )
title = description ;
if ( ! description . empty ( ) )
domainList . push_back ( LauncherEntry ( iter - > _key , engineid , gameid , description , title , & iter - > _value ) ) ;
}
// Now sort the list in dictionary order
Common : : sort ( domainList . begin ( ) , domainList . end ( ) , LauncherEntryComparator ( ) ) ;
return domainList ;
}
2021-07-12 19:48:19 +05:30
void LauncherDialog : : handleKeyDown ( Common : : KeyState state ) {
Dialog : : handleKeyDown ( state ) ;
}
void LauncherDialog : : handleKeyUp ( Common : : KeyState state ) {
Dialog : : handleKeyUp ( state ) ;
updateButtons ( ) ;
}
void LauncherDialog : : handleOtherEvent ( const Common : : Event & evt ) {
Dialog : : handleOtherEvent ( evt ) ;
if ( evt . type = = Common : : EVENT_DROP_FILE ) {
2022-06-05 18:54:46 +01:00
// If the path is a file, take the parent directory for the detection
Common : : String path = evt . path ;
Common : : FSNode node ( path ) ;
if ( ! node . isDirectory ( ) )
path = node . getParent ( ) . getPath ( ) ;
doGameDetection ( path ) ;
2021-07-12 19:48:19 +05:30
}
}
bool LauncherDialog : : doGameDetection ( const Common : : String & path ) {
// Allow user to add a new game to the list.
// 2) try to auto detect which game is in the directory, if we cannot
// determine it uniquely present a list of candidates to the user
// to pick from
// 3) Display the 'Edit' dialog for that item, letting the user specify
// an alternate description (to distinguish multiple versions of the
// game, e.g. 'Monkey German' and 'Monkey English') and set default
// options for that game
// 4) If no game is found in the specified directory, return to the
// dialog.
// User made his choice...
Common : : FSNode dir ( path ) ;
Common : : FSList files ;
if ( ! dir . getChildren ( files , Common : : FSNode : : kListAll ) ) {
2023-12-02 15:21:11 -08:00
Common : : U32String msg ( _ ( " ScummVM couldn't open the specified directory! " ) ) ;
# ifdef __ANDROID__
msg + = Common : : U32String ( " \n \n " ) ;
msg + = _ ( " Did you add this directory to the SAF? Press the help button [?] on the top for detailed instructions " ) ;
# endif
MessageDialog alert ( msg ) ;
2021-07-12 19:48:19 +05:30
alert . runModal ( ) ;
return true ;
}
// ...so let's determine a list of candidates, games that
// could be contained in the specified directory.
DetectionResults detectionResults = EngineMan . detectGames ( files ) ;
if ( detectionResults . foundUnknownGames ( ) ) {
Common : : U32String report = detectionResults . generateUnknownGameReport ( false , 80 ) ;
g_system - > logMessage ( LogMessageType : : kInfo , report . encode ( ) . c_str ( ) ) ;
}
Common : : Array < DetectedGame > candidates = detectionResults . listDetectedGames ( ) ;
int idx ;
if ( candidates . empty ( ) ) {
// No game was found in the specified directory
2023-12-02 15:21:11 -08:00
Common : : U32String msg ( _ ( " ScummVM could not find any game in the specified directory! " ) ) ;
# ifdef __ANDROID__
msg + = Common : : U32String ( " \n \n " ) ;
msg + = _ ( " Did you add this directory to the SAF? Press the help button [?] on the top for detailed instructions " ) ;
# endif
MessageDialog alert ( msg ) ;
2021-07-12 19:48:19 +05:30
alert . runModal ( ) ;
idx = - 1 ;
return false ;
} else if ( candidates . size ( ) = = 1 ) {
// Exact match
idx = 0 ;
} else {
// Display the candidates to the user and let her/him pick one
2021-11-10 15:56:44 +01:00
Common : : U32StringArray list ;
2021-07-12 19:48:19 +05:30
for ( idx = 0 ; idx < ( int ) candidates . size ( ) ; idx + + ) {
Common : : U32String description = candidates [ idx ] . description ;
if ( candidates [ idx ] . hasUnknownFiles ) {
description + = Common : : U32String ( " - " ) ;
2021-11-10 16:18:26 +01:00
// I18N: Unknown game variant
2021-07-12 19:48:19 +05:30
description + = _ ( " Unknown variant " ) ;
}
list . push_back ( description ) ;
}
ChooserDialog dialog ( _ ( " Pick the game: " ) ) ;
dialog . setList ( list ) ;
idx = dialog . runModal ( ) ;
}
if ( 0 < = idx & & idx < ( int ) candidates . size ( ) ) {
const DetectedGame & result = candidates [ idx ] ;
if ( result . hasUnknownFiles ) {
UnknownGameDialog dialog ( result ) ;
bool cancel = dialog . runModal ( ) = = - 1 ;
if ( cancel ) {
idx = - 1 ;
}
}
}
if ( 0 < = idx & & idx < ( int ) candidates . size ( ) ) {
const DetectedGame & result = candidates [ idx ] ;
Common : : String domain = EngineMan . createTargetForGame ( result ) ;
// Display edit dialog for the new entry
EditGameDialog editDialog ( domain ) ;
if ( editDialog . runModal ( ) > 0 ) {
// User pressed OK, so make changes permanent
// Write config to disk
ConfMan . flushToDisk ( ) ;
// Update the ListWidget, select the new item, and force a redraw
updateListing ( ) ;
selectTarget ( editDialog . getDomain ( ) ) ;
g_gui . scheduleTopDialogRedraw ( ) ;
} else {
2022-07-29 15:28:41 +02:00
// User aborted, remove the new domain again
2021-07-12 19:48:19 +05:30
ConfMan . removeGameDomain ( domain ) ;
}
}
return true ;
}
void LauncherDialog : : handleCommand ( CommandSender * sender , uint32 cmd , uint32 data ) {
int item = getSelected ( ) ;
switch ( cmd ) {
case kAddGameCmd :
addGame ( ) ;
break ;
case kMassAddGameCmd :
massAddGame ( ) ;
break ;
2023-08-08 18:00:50 +05:30
# if defined(USE_DLC)
2023-06-13 00:32:36 +05:30
case kDownloadGameCmd : {
2023-08-03 00:18:24 +05:30
DLCsDialog downloader ;
2023-06-13 00:32:36 +05:30
downloader . runModal ( ) ;
}
break ;
2023-08-05 00:27:35 +05:30
# endif
2021-07-12 19:48:19 +05:30
case kRemoveGameCmd :
2021-07-25 21:37:19 +05:30
if ( item < 0 ) return ;
2021-07-12 19:48:19 +05:30
removeGame ( item ) ;
break ;
case kEditGameCmd :
2021-07-25 21:37:19 +05:30
if ( item < 0 ) return ;
2021-07-12 19:48:19 +05:30
editGame ( item ) ;
break ;
case kLoadGameCmd :
2021-07-25 21:37:19 +05:30
if ( item < 0 ) return ;
2021-07-12 19:48:19 +05:30
loadGame ( item ) ;
break ;
# ifdef ENABLE_EVENTRECORDER
case kRecordGameCmd :
2021-07-25 21:37:19 +05:30
if ( item < 0 ) return ;
2021-07-12 19:48:19 +05:30
recordGame ( item ) ;
break ;
# endif
case kOptionsCmd : {
GlobalOptionsDialog options ( this ) ;
options . runModal ( ) ;
}
break ;
case kAboutCmd : {
AboutDialog about ;
about . runModal ( ) ;
}
break ;
case kStartCmd :
// Start the selected game.
2021-07-25 21:37:19 +05:30
if ( item < 0 ) return ;
2021-07-12 19:48:19 +05:30
ConfMan . setActiveDomain ( _domains [ item ] ) ;
close ( ) ;
break ;
case kQuitCmd :
ConfMan . setActiveDomain ( " " ) ;
setResult ( - 1 ) ;
close ( ) ;
break ;
2023-05-05 00:44:00 +02:00
case kHelpCmd : {
HelpDialog dlg ;
dlg . runModal ( ) ;
}
break ;
2021-07-12 19:48:19 +05:30
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
case kGridSwitchCmd :
setResult ( kSwitchLauncherDialog ) ;
ConfMan . set ( " gui_launcher_chooser " , " grid " , Common : : ConfigManager : : kApplicationDomain ) ;
close ( ) ;
break ;
case kListSwitchCmd :
setResult ( kSwitchLauncherDialog ) ;
ConfMan . set ( " gui_launcher_chooser " , " list " , Common : : ConfigManager : : kApplicationDomain ) ;
close ( ) ;
break ;
# endif
default :
Dialog : : handleCommand ( sender , cmd , data ) ;
}
}
void LauncherDialog : : reflowLayout ( ) {
if ( getType ( ) = = kLauncherDisplayGrid & & ! g_gui . xmlEval ( ) - > getVar ( " Globals.GridSupported " , 0 ) ) {
setResult ( kSwitchLauncherDialog ) ;
close ( ) ;
return ;
}
# ifndef DISABLE_FANCY_THEMES
if ( g_gui . xmlEval ( ) - > getVar ( " Globals.ShowLauncherLogo " ) = = 1 & & g_gui . theme ( ) - > supportsImages ( ) ) {
2021-11-10 15:56:44 +01:00
StaticTextWidget * ver = ( StaticTextWidget * ) findWidget ( Common : : String ( _title + " .Version " ) . c_str ( ) ) ;
2021-07-12 19:48:19 +05:30
if ( ver ) {
ver - > setAlign ( g_gui . xmlEval ( ) - > getWidgetTextHAlign ( _title + " .Version " ) ) ;
ver - > setLabel ( Common : : U32String ( gScummVMVersionDate ) ) ;
}
if ( ! _logo )
_logo = new GraphicsWidget ( this , _title + " .Logo " ) ;
_logo - > setGfxFromTheme ( ThemeEngine : : kImageLogo ) ;
} else {
2021-11-10 15:56:44 +01:00
StaticTextWidget * ver = ( StaticTextWidget * ) findWidget ( Common : : String ( _title + " .Version " ) . c_str ( ) ) ;
2021-07-12 19:48:19 +05:30
if ( ver ) {
ver - > setAlign ( g_gui . xmlEval ( ) - > getWidgetTextHAlign ( _title + " .Version " ) ) ;
ver - > setLabel ( Common : : U32String ( gScummVMFullVersion ) ) ;
}
if ( _logo ) {
removeWidget ( _logo ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _logo , this ) ;
2021-07-12 19:48:19 +05:30
_logo = nullptr ;
}
}
if ( g_gui . xmlEval ( ) - > getVar ( " Globals.ShowSearchPic " ) = = 1 & & g_gui . theme ( ) - > supportsImages ( ) ) {
if ( ! _searchPic )
2023-01-07 22:39:56 +01:00
_searchPic = new GraphicsWidget ( this , _title + " .SearchPic " , _ ( " Search in game list " ) ) ;
2021-07-12 19:48:19 +05:30
_searchPic - > setGfxFromTheme ( ThemeEngine : : kImageSearch ) ;
if ( _searchDesc ) {
removeWidget ( _searchDesc ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _searchDesc , this ) ;
2021-07-12 19:48:19 +05:30
_searchDesc = nullptr ;
}
2021-11-12 23:01:02 +01:00
if ( ! _groupPic )
_groupPic = new GraphicsWidget ( this , _title + " .GroupPic " ) ;
_groupPic - > setGfxFromTheme ( ThemeEngine : : kImageGroup ) ;
if ( _grpChooserDesc ) {
removeWidget ( _grpChooserDesc ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _grpChooserDesc , this ) ;
2021-11-12 23:01:02 +01:00
_grpChooserDesc = nullptr ;
}
2021-07-12 19:48:19 +05:30
} else {
if ( ! _searchDesc )
_searchDesc = new StaticTextWidget ( this , _title + " .SearchDesc " , _ ( " Search: " ) ) ;
if ( _searchPic ) {
removeWidget ( _searchPic ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _searchPic , this ) ;
2021-07-12 19:48:19 +05:30
_searchPic = nullptr ;
}
2021-11-12 23:01:02 +01:00
if ( ! _grpChooserDesc )
2022-12-23 11:18:10 +00:00
_grpChooserDesc = new StaticTextWidget ( this , _title + " .SearchDesc " , _ ( " Group: " ) ) ;
2021-11-12 23:01:02 +01:00
if ( _groupPic ) {
removeWidget ( _groupPic ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _groupPic , this ) ;
2021-11-12 23:01:02 +01:00
_groupPic = nullptr ;
}
2021-07-12 19:48:19 +05:30
}
removeWidget ( _searchClearButton ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _searchClearButton , this ) ;
2021-07-12 19:48:19 +05:30
_searchClearButton = addClearButton ( this , _title + " .SearchClearButton " , kSearchClearCmd ) ;
# endif
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
2021-08-13 16:44:32 +05:30
addLayoutChooserButtons ( ) ;
2021-07-12 19:48:19 +05:30
# endif
_w = g_system - > getOverlayWidth ( ) ;
_h = g_system - > getOverlayHeight ( ) ;
Dialog : : reflowLayout ( ) ;
}
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
2021-08-13 16:44:32 +05:30
void LauncherDialog : : addLayoutChooserButtons ( ) {
2021-07-12 19:48:19 +05:30
if ( _listButton ) {
removeWidget ( _listButton ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _listButton , this ) ;
2021-07-12 19:48:19 +05:30
_listButton = nullptr ;
}
if ( _gridButton ) {
removeWidget ( _gridButton ) ;
2021-11-14 14:39:40 +01:00
g_gui . addToTrash ( _gridButton , this ) ;
2021-07-12 19:48:19 +05:30
_gridButton = nullptr ;
}
if ( ! g_gui . xmlEval ( ) - > getVar ( " Globals.GridSupported " , 0 ) )
return ;
_listButton = createSwitchButton ( _title + " .ListSwitch " , Common : : U32String ( " L " ) , _ ( " List view " ) , ThemeEngine : : kImageList , kListSwitchCmd ) ;
_gridButton = createSwitchButton ( _title + " .GridSwitch " , Common : : U32String ( " G " ) , _ ( " Grid view " ) , ThemeEngine : : kImageGrid , kGridSwitchCmd ) ;
}
ButtonWidget * LauncherDialog : : createSwitchButton ( const Common : : String & name , const Common : : U32String & desc , const Common : : U32String & tooltip , const char * image , uint32 cmd ) {
ButtonWidget * button ;
# ifndef DISABLE_FANCY_THEMES
if ( g_gui . xmlEval ( ) - > getVar ( " Globals.ShowChooserPics " ) = = 1 & & g_gui . theme ( ) - > supportsImages ( ) ) {
button = new PicButtonWidget ( this , name , tooltip , cmd ) ;
2023-03-16 23:22:27 +00:00
( ( PicButtonWidget * ) button ) - > setGfxFromTheme ( image , kPicButtonStateEnabled , false ) ;
2021-07-12 19:48:19 +05:30
} else
# endif
button = new ButtonWidget ( this , name , desc , tooltip , cmd ) ;
return button ;
}
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
bool LauncherDialog : : checkModifier ( int checkedModifier ) {
int modifiers = g_system - > getEventManager ( ) - > getModifierState ( ) ;
return ( modifiers & checkedModifier ) ! = 0 ;
}
# pragma mark -
2022-06-08 01:36:40 +02:00
LauncherChooser : : LauncherChooser ( ) : _impl ( nullptr ) {
}
2021-07-01 22:04:11 +05:30
LauncherChooser : : ~ LauncherChooser ( ) {
delete _impl ;
_impl = nullptr ;
2016-10-30 18:56:35 +00:00
}
2021-07-01 22:04:11 +05:30
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
LauncherDisplayType getRequestedLauncherType ( ) {
const Common : : String & userConfig = ConfMan . get ( " gui_launcher_chooser " , Common : : ConfigManager : : kApplicationDomain ) ;
// If grid needs to be disabled on certain resolutions,
// those conditions need to be added here
2021-07-07 19:45:46 +05:30
if ( userConfig . equalsIgnoreCase ( " grid " ) & & g_gui . xmlEval ( ) - > getVar ( " Globals.GridSupported " , 0 ) ) {
2021-07-01 22:04:11 +05:30
return kLauncherDisplayGrid ;
} else {
return kLauncherDisplayList ;
}
}
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
2021-11-10 16:26:03 +01:00
class LauncherSimple : public LauncherDialog {
public :
2023-06-06 01:12:17 +02:00
LauncherSimple ( const Common : : String & title ) ;
2023-04-11 19:19:56 -07:00
~ LauncherSimple ( ) override ;
2021-11-10 16:26:03 +01:00
void handleCommand ( CommandSender * sender , uint32 cmd , uint32 data ) override ;
void handleKeyDown ( Common : : KeyState state ) override ;
LauncherDisplayType getType ( ) const override { return kLauncherDisplayList ; }
protected :
void updateListing ( ) override ;
2022-10-01 18:26:15 +02:00
void groupEntries ( const Common : : Array < LauncherEntry > & metadata ) ;
2021-11-10 16:26:03 +01:00
void updateButtons ( ) override ;
void selectTarget ( const Common : : String & target ) override ;
int getSelected ( ) override ;
void build ( ) override ;
private :
GroupedListWidget * _list ;
} ;
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
class LauncherGrid : public LauncherDialog {
public :
2023-06-06 01:12:17 +02:00
LauncherGrid ( const Common : : String & title ) ;
2023-04-11 19:19:56 -07:00
~ LauncherGrid ( ) override ;
2021-11-10 16:26:03 +01:00
void handleCommand ( CommandSender * sender , uint32 cmd , uint32 data ) override ;
void handleKeyDown ( Common : : KeyState state ) override ;
LauncherDisplayType getType ( ) const override { return kLauncherDisplayGrid ; }
protected :
void updateListing ( ) override ;
2022-10-01 18:26:15 +02:00
void groupEntries ( const Common : : Array < LauncherEntry > & metadata ) ;
2021-11-10 16:26:03 +01:00
void updateButtons ( ) override ;
void selectTarget ( const Common : : String & target ) override ;
int getSelected ( ) override ;
void build ( ) override ;
private :
2022-06-04 01:06:34 +01:00
GridWidget * _grid ;
SliderWidget * _gridItemSizeSlider ;
StaticTextWidget * _gridItemSizeLabel ;
2021-11-10 16:26:03 +01:00
} ;
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
2021-07-01 22:04:11 +05:30
void LauncherChooser : : selectLauncher ( ) {
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
LauncherDisplayType requestedType = getRequestedLauncherType ( ) ;
if ( ! _impl | | _impl - > getType ( ) ! = requestedType ) {
delete _impl ;
_impl = nullptr ;
2021-07-08 16:03:17 +05:30
switch ( requestedType ) {
2021-07-01 22:04:11 +05:30
case kLauncherDisplayGrid :
2023-06-06 01:12:17 +02:00
_impl = new LauncherGrid ( " LauncherGrid " ) ;
2021-07-01 22:04:11 +05:30
break ;
default :
// fallthrough intended
case kLauncherDisplayList :
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
2023-06-06 01:12:17 +02:00
_impl = new LauncherSimple ( " Launcher " ) ;
2021-07-01 22:04:11 +05:30
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
break ;
2016-10-30 18:56:35 +00:00
}
}
2021-07-01 22:04:11 +05:30
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
}
int LauncherChooser : : runModal ( ) {
if ( ! _impl )
return - 1 ;
int ret ;
do {
ret = _impl - > run ( ) ;
if ( ret = = kSwitchLauncherDialog ) {
selectLauncher ( ) ;
}
} while ( ret < - 1 ) ;
return ret ;
}
2021-07-12 19:48:19 +05:30
# pragma mark -
2023-06-06 01:12:17 +02:00
LauncherSimple : : LauncherSimple ( const Common : : String & title )
: LauncherDialog ( title ) ,
2021-08-14 21:41:21 +05:30
_list ( nullptr ) {
2021-07-12 19:48:19 +05:30
build ( ) ;
}
2023-04-11 19:19:56 -07:00
LauncherSimple : : ~ LauncherSimple ( ) {
_list - > saveClosedGroups ( Common : : U32String ( groupingModes [ _groupBy ] . name ) ) ;
}
2021-11-10 15:56:44 +01:00
void LauncherSimple : : selectTarget ( const Common : : String & target ) {
2021-07-12 19:48:19 +05:30
if ( ! target . empty ( ) ) {
int itemToSelect = 0 ;
2021-11-10 15:56:44 +01:00
Common : : StringArray : : const_iterator iter ;
2021-07-12 19:48:19 +05:30
for ( iter = _domains . begin ( ) ; iter ! = _domains . end ( ) ; + + iter , + + itemToSelect ) {
if ( target = = * iter ) {
_list - > setSelected ( itemToSelect ) ;
break ;
}
}
}
}
2021-11-10 11:30:56 +01:00
int LauncherSimple : : getSelected ( ) { return _list - > getSelected ( ) ; }
2021-07-12 19:48:19 +05:30
void LauncherSimple : : build ( ) {
2021-08-14 21:41:21 +05:30
LauncherDialog : : build ( ) ;
2021-07-12 19:48:19 +05:30
_startButton =
new ButtonWidget ( this , " Launcher.StartButton " , _ ( " ~S~tart " ) , _ ( " Start selected game " ) , kStartCmd ) ;
DropdownButtonWidget * loadButton =
new DropdownButtonWidget ( this , " Launcher.LoadGameButton " , _ ( " ~L~oad... " ) , _ ( " Load saved game for selected game " ) , kLoadGameCmd ) ;
# ifdef ENABLE_EVENTRECORDER
loadButton - > appendEntry ( _ ( " Record... " ) , kRecordGameCmd ) ;
# endif
_loadButton = loadButton ;
2021-08-14 21:41:21 +05:30
// Add edit button
2022-05-15 05:16:29 -05:00
_editButton =
2022-05-23 16:36:56 -05:00
new ButtonWidget ( this , " Launcher.EditGameButton " , _ ( " ~G~ame Options... " ) , _ ( " Change game options " ) , kEditGameCmd , 0 , _c ( " ~G~ame Opts... " , " lowres " ) ) ;
2021-07-12 19:48:19 +05:30
// Add list with game titles
2021-07-17 16:30:01 +05:30
_list = new GroupedListWidget ( this , " Launcher.GameList " , Common : : U32String ( ) , kListSearchCmd ) ;
2021-07-12 19:48:19 +05:30
_list - > setEditable ( false ) ;
_list - > enableDictionarySelect ( true ) ;
_list - > setNumberingMode ( kListNumberingOff ) ;
_list - > setFilterMatcher ( LauncherFilterMatcher , this ) ;
// Populate the list
updateListing ( ) ;
// Restore last selection
2021-11-10 16:28:10 +01:00
Common : : String last ( ConfMan . get ( " lastselectedgame " , Common : : ConfigManager : : kApplicationDomain ) ) ;
2021-07-12 19:48:19 +05:30
selectTarget ( last ) ;
// En-/disable the buttons depending on the list selection
updateButtons ( ) ;
}
void LauncherSimple : : updateListing ( ) {
2021-11-10 15:56:44 +01:00
Common : : U32StringArray l ;
2021-07-12 19:48:19 +05:30
ThemeEngine : : FontColor color ;
int numEntries = ConfMan . getInt ( " gui_list_max_scan_entries " ) ;
// Retrieve a list of all games defined in the config file
_domains . clear ( ) ;
2021-11-10 16:28:10 +01:00
const Common : : ConfigManager : : DomainMap & domains = ConfMan . getGameDomains ( ) ;
2021-07-12 19:48:19 +05:30
bool scanEntries = numEntries = = - 1 ? true : ( ( int ) domains . size ( ) < = numEntries ) ;
2022-10-01 18:26:15 +02:00
// Turn it into a sorted list of entries
2023-06-06 01:12:17 +02:00
Common : : Array < LauncherEntry > domainList = generateEntries ( domains ) ;
2021-07-12 19:48:19 +05:30
// And fill out our structures
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = domainList . begin ( ) ; iter ! = domainList . end ( ) ; + + iter ) {
2021-07-12 19:48:19 +05:30
color = ThemeEngine : : kFontColorNormal ;
if ( scanEntries ) {
Common : : String path ;
if ( ! iter - > domain - > tryGetVal ( " path " , path ) | | ! Common : : FSNode ( path ) . isDirectory ( ) ) {
color = ThemeEngine : : kFontColorAlternate ;
// If more conditions which grey out entries are added we should consider
// enabling this so that it is easy to spot why a certain game entry cannot
// be started.
// description += Common::String::format(" (%s)", _("Not found"));
}
}
2022-05-25 22:30:53 +02:00
Common : : U32String gameDesc = GUI : : ListWidget : : getThemeColor ( color ) + Common : : U32String ( iter - > description ) ;
l . push_back ( gameDesc ) ;
2021-07-12 19:48:19 +05:30
_domains . push_back ( iter - > key ) ;
}
const int oldSel = _list - > getSelected ( ) ;
2022-05-29 00:39:35 +02:00
_list - > setList ( l ) ;
2021-07-27 23:49:04 +05:30
2022-10-01 18:26:15 +02:00
groupEntries ( domainList ) ;
2021-07-22 18:12:02 +05:30
2021-07-25 21:19:31 +05:30
if ( oldSel < ( int ) l . size ( ) & & oldSel > = 0 )
2021-07-12 19:48:19 +05:30
_list - > setSelected ( oldSel ) ; // Restore the old selection
else if ( oldSel ! = - 1 )
// Select the last entry if the list has been reduced
_list - > setSelected ( _list - > getList ( ) . size ( ) - 1 ) ;
updateButtons ( ) ;
// Update the filter settings, those are lost when "setList"
// is called.
_list - > setFilter ( _searchWidget - > getEditString ( ) ) ;
2023-05-05 00:44:00 +02:00
2023-04-11 19:19:56 -07:00
// Close groups that the user closed earlier
_list - > loadClosedGroups ( Common : : U32String ( groupingModes [ _groupBy ] . name ) ) ;
2021-07-12 19:48:19 +05:30
}
2022-10-01 18:26:15 +02:00
void LauncherSimple : : groupEntries ( const Common : : Array < LauncherEntry > & metadata ) {
2021-11-10 15:56:44 +01:00
Common : : U32StringArray attrs ;
2021-08-13 01:33:18 +05:30
Common : : StringMap metadataNames ;
2021-11-10 11:21:07 +01:00
_list - > setGroupsVisibility ( true ) ;
2021-07-22 21:24:25 +05:30
switch ( _groupBy ) {
2021-08-13 01:57:20 +05:30
case kGroupByFirstLetter : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
attrs . push_back ( iter - > description . substr ( 0 , 1 ) ) ;
2021-07-22 21:24:25 +05:30
}
2021-11-10 15:56:44 +01:00
_list - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " ... " ) ) ;
2021-07-22 21:24:25 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
case kGroupByEngine : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
attrs . push_back ( iter - > engineid ) ;
2021-07-22 21:24:25 +05:30
}
2021-11-10 15:56:44 +01:00
_list - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List grouping when no engine is specified
metadataNames [ " " ] = _ ( " Unknown Engine " ) ;
2021-11-10 15:56:44 +01:00
Common : : HashMap < Common : : String , MetadataEngine , Common : : IgnoreCase_Hash , Common : : IgnoreCase_EqualTo > : : iterator i = _metadataParser . _engineInfo . begin ( ) ;
2021-08-13 01:57:20 +05:30
for ( ; i ! = _metadataParser . _engineInfo . end ( ) ; + + i ) {
2021-08-13 01:33:18 +05:30
if ( i - > _value . alt_name . empty ( ) ) {
metadataNames [ i - > _key ] = i - > _value . name ;
} else {
metadataNames [ i - > _key ] = Common : : String : : format ( " %s (%s) " , i - > _value . name . c_str ( ) , i - > _value . alt_name . c_str ( ) ) ;
}
}
2021-07-22 21:24:25 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
2021-08-13 02:45:00 +05:30
case kGroupByCompany : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
attrs . push_back ( _metadataParser . _gameInfo [ buildQualifiedGameName ( iter - > engineid , iter - > gameid ) ] . company_id ) ;
2021-08-13 02:45:00 +05:30
}
2021-11-10 15:56:44 +01:00
_list - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List grouping when no pubisher is specified
metadataNames [ " " ] = _ ( " Unknown Publisher " ) ;
2021-11-10 15:56:44 +01:00
Common : : HashMap < Common : : String , MetadataCompany , Common : : IgnoreCase_Hash , Common : : IgnoreCase_EqualTo > : : iterator i = _metadataParser . _companyInfo . begin ( ) ;
2021-08-13 02:45:00 +05:30
for ( ; i ! = _metadataParser . _companyInfo . end ( ) ; + + i ) {
if ( i - > _value . alt_name . empty ( ) ) {
metadataNames [ i - > _key ] = i - > _value . name ;
} else {
metadataNames [ i - > _key ] = Common : : String : : format ( " %s (%s) " , i - > _value . name . c_str ( ) , i - > _value . alt_name . c_str ( ) ) ;
}
}
break ;
}
2021-08-13 01:57:20 +05:30
case kGroupBySeries : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
attrs . push_back ( _metadataParser . _gameInfo [ buildQualifiedGameName ( iter - > engineid , iter - > gameid ) ] . series_id ) ;
2021-08-08 19:14:59 +05:30
}
2021-11-10 15:56:44 +01:00
_list - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List group when no game series is specified
metadataNames [ " " ] = _ ( " No Series " ) ;
2021-11-10 15:56:44 +01:00
Common : : HashMap < Common : : String , MetadataSeries , Common : : IgnoreCase_Hash , Common : : IgnoreCase_EqualTo > : : iterator i = _metadataParser . _seriesInfo . begin ( ) ;
2021-08-13 01:57:20 +05:30
for ( ; i ! = _metadataParser . _seriesInfo . end ( ) ; + + i ) {
2021-08-13 01:33:18 +05:30
metadataNames [ i - > _key ] = i - > _value . name ;
}
2021-08-08 19:14:59 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
case kGroupByLanguage : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
Common : : U32String language = iter - > domain - > getValOrDefault ( Common : : String ( " language " ) ) ;
2021-08-05 21:35:01 +05:30
attrs . push_back ( language ) ;
2021-07-22 21:24:25 +05:30
}
2021-11-10 15:56:44 +01:00
_list - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List group when no languageis specified
metadataNames [ " " ] = _ ( " Language not detected " ) ;
2021-08-13 01:57:20 +05:30
const Common : : LanguageDescription * l = Common : : g_languages ;
for ( ; l - > code ; + + l ) {
2021-08-13 01:33:18 +05:30
metadataNames [ l - > code ] = l - > description ;
}
2021-07-22 21:24:25 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
case kGroupByPlatform : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
Common : : U32String platform = iter - > domain - > getValOrDefault ( Common : : String ( " Platform " ) ) ;
2021-08-05 21:35:01 +05:30
attrs . push_back ( platform ) ;
2021-07-22 21:24:25 +05:30
}
2021-11-10 15:56:44 +01:00
_list - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List group when no platform is specified
metadataNames [ " " ] = _ ( " Platform not detected " ) ;
2021-08-13 01:57:20 +05:30
const Common : : PlatformDescription * p = Common : : g_platforms ;
for ( ; p - > code ; + + p ) {
2021-08-13 01:33:18 +05:30
metadataNames [ p - > code ] = p - > description ;
}
2021-07-22 21:24:25 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
2023-11-10 23:45:15 +01:00
case kGroupByYear : {
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
Common : : U32String year = _metadataParser . _gameInfo [ buildQualifiedGameName ( iter - > engineid , iter - > gameid ) ] . year ;
attrs . push_back ( year ) ;
if ( ! metadataNames . contains ( year ) )
metadataNames [ year ] = year ;
}
_list - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
// I18N: List group when no year is specified
metadataNames [ " " ] = _ ( " Unknown Year " ) ;
break ;
}
2021-07-22 21:24:25 +05:30
case kGroupByNone : // Fall-through intentional
default :
2021-11-10 11:21:07 +01:00
_list - > setGroupsVisibility ( false ) ;
2021-07-22 21:24:25 +05:30
break ;
2021-07-22 18:12:02 +05:30
}
2021-08-13 15:19:07 +05:30
_list - > setMetadataNames ( metadataNames ) ;
_list - > setAttributeValues ( attrs ) ;
_list - > groupByAttribute ( ) ;
2021-07-22 18:12:02 +05:30
}
2021-07-12 19:48:19 +05:30
void LauncherSimple : : handleKeyDown ( Common : : KeyState state ) {
if ( state . keycode = = Common : : KEYCODE_TAB ) {
// Toggle between the game list and the quick search field.
if ( getFocusWidget ( ) = = _searchWidget ) {
setFocusWidget ( _list ) ;
} else if ( getFocusWidget ( ) = = _list ) {
setFocusWidget ( _searchWidget ) ;
}
}
Dialog : : handleKeyDown ( state ) ;
updateButtons ( ) ;
}
void LauncherSimple : : handleCommand ( CommandSender * sender , uint32 cmd , uint32 data ) {
switch ( cmd ) {
case kListItemActivatedCmd :
case kListItemDoubleClickedCmd :
LauncherDialog : : handleCommand ( sender , kStartCmd , 0 ) ;
break ;
case kListItemRemovalRequestCmd :
LauncherDialog : : handleCommand ( sender , kRemoveGameCmd , 0 ) ;
break ;
case kListSelectionChangedCmd :
updateButtons ( ) ;
break ;
case kSearchCmd :
// Update the active search filter.
_list - > setFilter ( _searchWidget - > getEditString ( ) ) ;
break ;
case kSearchClearCmd :
// Reset the active search filter, thus showing all games again
_searchWidget - > setEditString ( Common : : U32String ( ) ) ;
_list - > setFilter ( Common : : U32String ( ) ) ;
break ;
2021-08-15 03:48:48 +05:30
case kSetGroupMethodCmd : {
2021-07-22 21:24:25 +05:30
// Change the grouping criteria
2021-08-15 03:48:48 +05:30
GroupingMethod newGroupBy = ( GroupingMethod ) data ;
if ( _groupBy ! = newGroupBy ) {
2023-04-11 19:19:56 -07:00
_list - > saveClosedGroups ( Common : : U32String ( groupingModes [ _groupBy ] . name ) ) ;
2021-08-15 03:48:48 +05:30
_groupBy = newGroupBy ;
2022-02-23 00:20:47 +00:00
const GroupingMode * mode = groupingModes ;
while ( mode - > name ) {
if ( mode - > id = = newGroupBy ) {
ConfMan . setAndFlush ( " grouping " , mode - > name ) ;
break ;
}
+ + mode ;
}
2021-08-15 03:48:48 +05:30
updateListing ( ) ;
2021-07-22 21:24:25 +05:30
}
break ;
2021-08-15 03:48:48 +05:30
}
2021-07-12 19:48:19 +05:30
default :
LauncherDialog : : handleCommand ( sender , cmd , data ) ;
}
}
void LauncherSimple : : updateButtons ( ) {
bool enable = ( _list - > getSelected ( ) > = 0 ) ;
2023-05-01 02:53:21 +02:00
_startButton - > setEnabled ( enable ) ;
_editButton - > setEnabled ( enable ) ;
_removeButton - > setEnabled ( enable ) ;
2021-07-12 19:48:19 +05:30
int item = _list - > getSelected ( ) ;
bool en = enable ;
if ( item > = 0 )
en = ! ( Common : : checkGameGUIOption ( GUIO_NOLAUNCHLOAD , ConfMan . get ( " guioptions " , _domains [ item ] ) ) ) ;
2023-05-01 02:53:21 +02:00
_loadButton - > setEnabled ( en ) ;
2021-07-12 19:48:19 +05:30
}
# pragma mark -
# ifndef DISABLE_LAUNCHERDISPLAY_GRID
2023-06-06 01:12:17 +02:00
LauncherGrid : : LauncherGrid ( const Common : : String & title )
: LauncherDialog ( title ) ,
2022-06-04 01:06:34 +01:00
_grid ( nullptr ) , _gridItemSizeSlider ( nullptr ) , _gridItemSizeLabel ( nullptr ) {
2021-07-12 19:48:19 +05:30
build ( ) ;
}
2023-04-11 19:19:56 -07:00
LauncherGrid : : ~ LauncherGrid ( ) {
_grid - > saveClosedGroups ( Common : : U32String ( groupingModes [ _groupBy ] . name ) ) ;
}
2022-10-01 18:26:15 +02:00
void LauncherGrid : : groupEntries ( const Common : : Array < LauncherEntry > & metadata ) {
2021-11-10 15:56:44 +01:00
Common : : U32StringArray attrs ;
2021-08-08 19:14:59 +05:30
Common : : StringMap metadataNames ;
2021-08-05 21:29:08 +05:30
switch ( _groupBy ) {
2021-08-13 01:57:20 +05:30
case kGroupByFirstLetter : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
2022-10-01 18:27:45 +02:00
attrs . push_back ( iter - > title . substr ( 0 , 1 ) ) ;
2021-08-05 21:29:08 +05:30
}
2021-11-10 15:56:44 +01:00
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " ... " ) ) ;
2021-08-05 21:29:08 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
case kGroupByEngine : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
attrs . push_back ( iter - > engineid ) ;
2021-08-05 21:29:08 +05:30
}
2021-11-10 15:56:44 +01:00
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List grouping when no enginr is specified
metadataNames [ " " ] = _ ( " Unknown Engine " ) ;
2021-11-10 15:56:44 +01:00
Common : : HashMap < Common : : String , MetadataEngine , Common : : IgnoreCase_Hash , Common : : IgnoreCase_EqualTo > : : iterator i = _metadataParser . _engineInfo . begin ( ) ;
2021-08-13 01:57:20 +05:30
for ( ; i ! = _metadataParser . _engineInfo . end ( ) ; + + i ) {
2021-08-08 19:14:59 +05:30
if ( i - > _value . alt_name . empty ( ) ) {
metadataNames [ i - > _key ] = i - > _value . name ;
} else {
metadataNames [ i - > _key ] = Common : : String : : format ( " %s (%s) " , i - > _value . name . c_str ( ) , i - > _value . alt_name . c_str ( ) ) ;
}
}
break ;
2021-08-13 01:57:20 +05:30
}
2021-08-13 02:45:00 +05:30
case kGroupByCompany : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
attrs . push_back ( _metadataParser . _gameInfo [ buildQualifiedGameName ( iter - > engineid , iter - > gameid ) ] . company_id ) ;
2021-08-13 02:45:00 +05:30
}
2021-11-10 15:56:44 +01:00
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List group when no publisher is specified
metadataNames [ " " ] = _ ( " Unknown Publisher " ) ;
2021-11-10 15:56:44 +01:00
Common : : HashMap < Common : : String , MetadataCompany , Common : : IgnoreCase_Hash , Common : : IgnoreCase_EqualTo > : : iterator i = _metadataParser . _companyInfo . begin ( ) ;
2021-08-13 02:45:00 +05:30
for ( ; i ! = _metadataParser . _companyInfo . end ( ) ; + + i ) {
if ( i - > _value . alt_name . empty ( ) ) {
metadataNames [ i - > _key ] = i - > _value . name ;
} else {
metadataNames [ i - > _key ] = Common : : String : : format ( " %s (%s) " , i - > _value . name . c_str ( ) , i - > _value . alt_name . c_str ( ) ) ;
}
}
break ;
}
2022-10-01 18:26:15 +02:00
case kGroupBySeries : {
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
attrs . push_back ( _metadataParser . _gameInfo [ buildQualifiedGameName ( iter - > engineid , iter - > gameid ) ] . series_id ) ;
}
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
// I18N: List grouping when no game series is specified
metadataNames [ " " ] = _ ( " No Series " ) ;
Common : : HashMap < Common : : String , MetadataSeries , Common : : IgnoreCase_Hash , Common : : IgnoreCase_EqualTo > : : iterator i = _metadataParser . _seriesInfo . begin ( ) ;
for ( ; i ! = _metadataParser . _seriesInfo . end ( ) ; + + i ) {
metadataNames [ i - > _key ] = i - > _value . name ;
}
break ;
}
2021-08-13 01:57:20 +05:30
case kGroupByLanguage : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
Common : : U32String language = iter - > domain - > getValOrDefault ( Common : : String ( " language " ) ) ;
2021-08-05 21:35:01 +05:30
attrs . push_back ( language ) ;
2021-08-05 21:29:08 +05:30
}
2021-11-10 15:56:44 +01:00
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List group when no language is specified
metadataNames [ " " ] = _ ( " Language not detected " ) ;
2021-08-13 01:57:20 +05:30
const Common : : LanguageDescription * l = Common : : g_languages ;
for ( ; l - > code ; + + l ) {
2021-08-13 01:02:51 +05:30
metadataNames [ l - > code ] = l - > description ;
}
2021-08-05 21:29:08 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
case kGroupByPlatform : {
2022-10-01 18:26:15 +02:00
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
Common : : U32String platform = iter - > domain - > getValOrDefault ( Common : : String ( " Platform " ) ) ;
2021-08-05 21:35:01 +05:30
attrs . push_back ( platform ) ;
2021-08-05 21:29:08 +05:30
}
2021-11-10 15:56:44 +01:00
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-11-10 16:18:26 +01:00
// I18N: List group when no platform is specified
metadataNames [ " " ] = _ ( " Platform not detected " ) ;
2021-08-13 01:57:20 +05:30
const Common : : PlatformDescription * p = Common : : g_platforms ;
for ( ; p - > code ; + + p ) {
2021-08-13 01:02:51 +05:30
metadataNames [ p - > code ] = p - > description ;
}
2021-08-05 21:29:08 +05:30
break ;
2021-08-13 01:57:20 +05:30
}
2023-11-10 23:45:15 +01:00
case kGroupByYear : {
for ( Common : : Array < LauncherEntry > : : const_iterator iter = metadata . begin ( ) ; iter ! = metadata . end ( ) ; + + iter ) {
Common : : U32String year = _metadataParser . _gameInfo [ buildQualifiedGameName ( iter - > engineid , iter - > gameid ) ] . year ;
attrs . push_back ( year ) ;
if ( ! metadataNames . contains ( year ) )
metadataNames [ year ] = year ;
}
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
// I18N: List group when no year is specified
metadataNames [ " " ] = _ ( " Unknown Year " ) ;
break ;
}
2021-08-05 21:29:08 +05:30
case kGroupByNone : // Fall-through intentional
default :
for ( uint i = 0 ; i < metadata . size ( ) ; + + i ) {
2021-11-10 16:18:26 +01:00
// I18N: Group for All items
attrs . push_back ( _ ( " All " ) ) ;
2021-08-05 21:29:08 +05:30
}
2021-11-10 15:56:44 +01:00
_grid - > setGroupHeaderFormat ( Common : : U32String ( " " ) , Common : : U32String ( " " ) ) ;
2021-08-05 21:29:08 +05:30
break ;
}
2021-08-13 15:19:07 +05:30
_grid - > setMetadataNames ( metadataNames ) ;
_grid - > setAttributeValues ( attrs ) ;
_grid - > groupEntries ( ) ;
2021-08-05 21:29:08 +05:30
}
2021-07-12 19:48:19 +05:30
void LauncherGrid : : handleKeyDown ( Common : : KeyState state ) {
if ( state . keycode = = Common : : KEYCODE_TAB ) {
// Toggle between the game list and the quick search field.
if ( getFocusWidget ( ) = = _searchWidget ) {
setFocusWidget ( _grid ) ;
} else {
setFocusWidget ( _searchWidget ) ;
}
}
Dialog : : handleKeyDown ( state ) ;
updateButtons ( ) ;
}
void LauncherGrid : : handleCommand ( CommandSender * sender , uint32 cmd , uint32 data ) {
switch ( cmd ) {
case kPlayButtonCmd :
case kItemDoubleClickedCmd :
LauncherDialog : : handleCommand ( sender , kStartCmd , 0 ) ;
break ;
case kEditButtonCmd :
LauncherDialog : : handleCommand ( sender , kEditGameCmd , 0 ) ;
break ;
case kLoadButtonCmd :
LauncherDialog : : handleCommand ( sender , kLoadGameCmd , 0 ) ;
break ;
case kItemClicked :
updateButtons ( ) ;
break ;
2021-08-15 01:36:25 +05:30
case kSearchCmd :
// Update the active search filter.
_grid - > setFilter ( _searchWidget - > getEditString ( ) ) ;
break ;
case kSearchClearCmd :
// Reset the active search filter, thus showing all games again
_searchWidget - > setEditString ( Common : : U32String ( ) ) ;
_grid - > setFilter ( Common : : U32String ( ) ) ;
break ;
2021-08-15 03:48:48 +05:30
case kSetGroupMethodCmd : {
2023-04-11 19:19:56 -07:00
_grid - > saveClosedGroups ( Common : : U32String ( groupingModes [ _groupBy ] . name ) ) ;
2023-05-05 00:44:00 +02:00
2021-08-05 21:29:08 +05:30
// Change the grouping criteria
2021-08-15 03:48:48 +05:30
GroupingMethod newGroupBy = ( GroupingMethod ) data ;
if ( _groupBy ! = newGroupBy ) {
_groupBy = newGroupBy ;
2022-02-23 00:20:47 +00:00
const GroupingMode * mode = groupingModes ;
while ( mode - > name ) {
if ( mode - > id = = newGroupBy ) {
ConfMan . setAndFlush ( " grouping " , mode - > name ) ;
break ;
}
+ + mode ;
}
2021-08-15 03:48:48 +05:30
updateListing ( ) ;
2021-08-05 21:29:08 +05:30
}
break ;
2021-08-15 03:48:48 +05:30
}
2022-06-04 01:06:34 +01:00
case kItemSizeCmd :
_gridItemSizeLabel - > setValue ( _gridItemSizeSlider - > getValue ( ) ) ;
_gridItemSizeLabel - > markAsDirty ( ) ;
ConfMan . setInt ( " grid_items_per_row " , _gridItemSizeSlider - > getValue ( ) ) ;
ConfMan . flushToDisk ( ) ;
reflowLayout ( ) ;
break ;
2022-06-28 01:17:06 +01:00
case kIconsSetLoadedCmd :
rebuild ( ) ;
break ;
2021-07-12 19:48:19 +05:30
default :
LauncherDialog : : handleCommand ( sender , cmd , data ) ;
}
}
void LauncherGrid : : updateListing ( ) {
// Retrieve a list of all games defined in the config file
_domains . clear ( ) ;
2021-11-10 16:28:10 +01:00
const Common : : ConfigManager : : DomainMap & domains = ConfMan . getGameDomains ( ) ;
2021-07-12 19:48:19 +05:30
2022-10-01 18:26:15 +02:00
// Turn it into a sorted list of entries
2023-06-06 01:12:17 +02:00
Common : : Array < LauncherEntry > domainList = generateEntries ( domains ) ;
2021-07-12 19:48:19 +05:30
Common : : Array < GridItemInfo > gridList ;
int k = 0 ;
for ( Common : : Array < LauncherEntry > : : const_iterator iter = domainList . begin ( ) ; iter ! = domainList . end ( ) ; + + iter ) {
Common : : String gameid = iter - > domain - > getVal ( " gameid " ) ;
Common : : String engineid = " UNK " ;
Common : : String language = " XX " ;
Common : : String platform ;
2022-08-27 00:15:19 +02:00
Common : : String extra ;
2023-02-07 20:54:51 +01:00
Common : : String path ;
bool valid_path = false ;
2021-07-12 19:48:19 +05:30
iter - > domain - > tryGetVal ( " engineid " , engineid ) ;
2021-07-14 18:27:51 +05:30
iter - > domain - > tryGetVal ( " language " , language ) ;
2021-07-12 19:48:19 +05:30
iter - > domain - > tryGetVal ( " platform " , platform ) ;
2022-08-27 00:15:19 +02:00
iter - > domain - > tryGetVal ( " extra " , extra ) ;
2023-02-07 20:54:51 +01:00
valid_path = ( ! iter - > domain - > tryGetVal ( " path " , path ) | | ! Common : : FSNode ( path ) . isDirectory ( ) ) ? false : true ;
2023-04-23 01:27:11 +02:00
gridList . push_back ( GridItemInfo ( k + + , engineid , gameid , iter - > description , iter - > title , extra , Common : : parseLanguage ( language ) , Common : : parsePlatform ( platform ) , valid_path ) ) ;
2022-10-01 18:26:15 +02:00
_domains . push_back ( iter - > key ) ;
2021-07-12 19:48:19 +05:30
}
2022-05-29 18:38:46 +02:00
const int oldSel = _grid - > getSelected ( ) ;
2021-07-12 19:48:19 +05:30
_grid - > setEntryList ( & gridList ) ;
2022-10-01 18:26:15 +02:00
groupEntries ( domainList ) ;
2022-05-29 18:38:46 +02:00
if ( oldSel < ( int ) gridList . size ( ) & & oldSel > = 0 )
_grid - > setSelected ( oldSel ) ; // Restore the old selection
else if ( oldSel ! = - 1 )
// Select the last entry if the list has been reduced
_grid - > setSelected ( gridList . size ( ) - 1 ) ;
updateButtons ( ) ;
2023-04-11 19:19:56 -07:00
_grid - > loadClosedGroups ( Common : : U32String ( groupingModes [ _groupBy ] . name ) ) ;
2021-07-12 19:48:19 +05:30
}
void LauncherGrid : : updateButtons ( ) {
bool enable = ( _grid - > getSelected ( ) > = 0 ) ;
2023-05-01 02:53:21 +02:00
_removeButton - > setEnabled ( enable ) ;
2021-07-12 19:48:19 +05:30
}
2022-05-29 18:38:46 +02:00
void LauncherGrid : : selectTarget ( const Common : : String & target ) {
if ( ! target . empty ( ) ) {
int itemToSelect = 0 ;
Common : : StringArray : : const_iterator iter ;
for ( iter = _domains . begin ( ) ; iter ! = _domains . end ( ) ; + + iter , + + itemToSelect ) {
if ( target = = * iter ) {
_grid - > setSelected ( itemToSelect ) ;
break ;
}
}
}
}
2021-11-10 11:30:56 +01:00
int LauncherGrid : : getSelected ( ) { return _grid - > getSelected ( ) ; }
2021-07-12 19:48:19 +05:30
void LauncherGrid : : build ( ) {
2021-08-14 21:41:21 +05:30
LauncherDialog : : build ( ) ;
2021-07-12 19:48:19 +05:30
2022-06-04 01:06:34 +01:00
new StaticTextWidget ( this , " LauncherGrid.GridItemsPerRowDesc " , _ ( " Icons per row: " ) ) ;
_gridItemSizeSlider = new SliderWidget ( this , " LauncherGrid.GridItemsPerRow " , Common : : U32String ( ) , kItemSizeCmd ) ;
_gridItemSizeSlider - > setMinValue ( 1 ) ;
_gridItemSizeSlider - > setMaxValue ( 12 ) ;
_gridItemSizeSlider - > setValue ( ConfMan . getInt ( " grid_items_per_row " ) ) ;
_gridItemSizeLabel = new StaticTextWidget ( this , " LauncherGrid.GridItemsPerRowLabel " , Common : : U32String ( " " ) , Common : : U32String ( ) , ThemeEngine : : kFontStyleBold , Common : : UNK_LANG , false ) ;
_gridItemSizeLabel - > setValue ( ConfMan . getInt ( " grid_items_per_row " ) ) ;
2021-07-12 19:48:19 +05:30
// Add list with game titles
_grid = new GridWidget ( this , " LauncherGrid.IconArea " ) ;
// Populate the list
updateListing ( ) ;
// Restore last selection
2021-11-10 16:28:10 +01:00
Common : : String last ( ConfMan . get ( " lastselectedgame " , Common : : ConfigManager : : kApplicationDomain ) ) ;
2021-07-12 19:48:19 +05:30
selectTarget ( last ) ;
// En-/disable the buttons depending on the list selection
updateButtons ( ) ;
}
# endif // !DISABLE_LAUNCHERDISPLAY_GRID
2003-11-10 23:40:48 +00:00
} // End of namespace GUI