2023-06-15 01:04:45 -04:00
/**
* Furnace Tracker - multi - system chiptune tracker
* Copyright ( C ) 2021 - 2023 tildearrow and contributors
*
* 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 2 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 , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include "gui.h"
# include "guiConst.h"
2023-07-01 14:02:12 -04:00
# include "commandPalette.h"
2023-06-15 01:04:45 -04:00
# include "misc/cpp/imgui_stdlib.h"
# include <fmt/printf.h>
# include <algorithm>
# include <cctype>
# include "../ta-log.h"
2023-06-18 22:21:16 -04:00
static inline bool matchFuzzy ( const char * haystack , const char * needle ) {
2023-06-15 01:04:45 -04:00
size_t h_i = 0 ; // haystack idx
size_t n_i = 0 ; // needle idx
while ( needle [ n_i ] ! = ' \0 ' ) {
for ( ; std : : tolower ( haystack [ h_i ] ) ! = std : : tolower ( needle [ n_i ] ) ; h_i + + ) {
if ( haystack [ h_i ] = = ' \0 ' )
return false ;
}
n_i + = 1 ;
}
return true ;
}
void FurnaceGUI : : drawPalette ( ) {
bool accepted = false ;
if ( paletteFirstFrame )
ImGui : : SetKeyboardFocusHere ( ) ;
2023-07-03 15:01:46 -04:00
int width = ImGui : : GetContentRegionAvail ( ) . x ;
ImGui : : SetNextItemWidth ( width ) ;
2023-06-15 01:04:45 -04:00
2023-07-31 15:12:29 -04:00
const char * hint = " Search... " ;
switch ( curPaletteType ) {
case CMDPAL_TYPE_RECENT :
hint = " Search recent files... " ;
break ;
case CMDPAL_TYPE_INSTRUMENTS :
hint = " Search instruments... " ;
break ;
case CMDPAL_TYPE_SAMPLES :
hint = " Search samples... " ;
break ;
2023-08-15 17:37:30 -04:00
case CMDPAL_TYPE_INSTRUMENT_CHANGE :
hint = " Search instruments (to change to)... " ;
break ;
2023-08-27 13:19:26 -04:00
case CMDPAL_TYPE_ADD_CHIP :
hint = " Search chip (to add)... " ;
break ;
2023-07-31 15:12:29 -04:00
}
if ( ImGui : : InputTextWithHint ( " ##CommandPaletteSearch " , hint , & paletteQuery ) | | paletteFirstFrame ) {
2023-06-15 01:04:45 -04:00
paletteSearchResults . clear ( ) ;
2023-06-18 22:21:16 -04:00
switch ( curPaletteType ) {
case CMDPAL_TYPE_MAIN :
for ( int i = 0 ; i < GUI_ACTION_MAX ; i + + ) {
if ( guiActions [ i ] . defaultBind = = - 1 ) continue ;
if ( matchFuzzy ( guiActions [ i ] . friendlyName , paletteQuery . c_str ( ) ) ) {
paletteSearchResults . push_back ( i ) ;
}
2023-06-15 01:04:45 -04:00
}
2023-06-18 22:21:16 -04:00
break ;
case CMDPAL_TYPE_RECENT :
for ( int i = 0 ; i < ( int ) recentFile . size ( ) ; i + + ) {
2023-10-29 19:05:01 -04:00
if ( matchFuzzy ( recentFile [ i ] . c_str ( ) , paletteQuery . c_str ( ) ) ) {
2023-06-18 22:21:16 -04:00
paletteSearchResults . push_back ( i ) ;
}
}
break ;
2023-07-31 14:58:38 -04:00
case CMDPAL_TYPE_INSTRUMENTS :
2023-08-15 17:37:30 -04:00
case CMDPAL_TYPE_INSTRUMENT_CHANGE :
2023-07-31 14:58:38 -04:00
if ( matchFuzzy ( " - None - " , paletteQuery . c_str ( ) ) ) {
paletteSearchResults . push_back ( 0 ) ;
}
for ( int i = 0 ; i < e - > song . insLen ; i + + ) {
2023-12-18 13:34:43 -05:00
String s = fmt : : sprintf ( " %02d: %s " , i , e - > song . ins [ i ] - > name . c_str ( ) ) ;
if ( matchFuzzy ( s . c_str ( ) , paletteQuery . c_str ( ) ) ) {
2023-07-31 14:58:38 -04:00
paletteSearchResults . push_back ( i + 1 ) ; // because over here ins=0 is 'None'
}
}
break ;
case CMDPAL_TYPE_SAMPLES :
for ( int i = 0 ; i < e - > song . sampleLen ; i + + ) {
if ( matchFuzzy ( e - > song . sample [ i ] - > name . c_str ( ) , paletteQuery . c_str ( ) ) ) {
paletteSearchResults . push_back ( i ) ;
}
}
break ;
2023-08-27 13:19:26 -04:00
case CMDPAL_TYPE_ADD_CHIP :
for ( int i = 0 ; availableSystems [ i ] ; i + + ) {
int ds = availableSystems [ i ] ;
const char * sysname = getSystemName ( ( DivSystem ) ds ) ;
if ( matchFuzzy ( sysname , paletteQuery . c_str ( ) ) ) {
paletteSearchResults . push_back ( ds ) ;
}
}
break ;
2023-06-18 22:21:16 -04:00
default :
2023-07-01 14:35:13 -04:00
logE ( " invalid command palette type " ) ;
ImGui : : CloseCurrentPopup ( ) ;
2023-06-18 22:21:16 -04:00
break ;
} ;
2023-06-15 01:04:45 -04:00
}
2023-07-03 15:38:08 -04:00
ImVec2 avail = ImGui : : GetContentRegionAvail ( ) ;
avail . y - = ImGui : : GetFrameHeightWithSpacing ( ) ;
if ( ImGui : : BeginChild ( " CommandPaletteList " , avail , false , 0 ) ) {
2023-06-15 01:04:45 -04:00
bool navigated = false ;
if ( ImGui : : IsKeyPressed ( ImGuiKey_UpArrow ) & & curPaletteChoice > 0 ) {
curPaletteChoice - = 1 ;
navigated = true ;
}
if ( ImGui : : IsKeyPressed ( ImGuiKey_DownArrow ) ) {
curPaletteChoice + = 1 ;
navigated = true ;
}
2023-08-27 13:19:26 -04:00
if ( paletteSearchResults . size ( ) > 0 & & curPaletteChoice < 0 ) {
curPaletteChoice = 0 ;
navigated = true ;
}
if ( curPaletteChoice > = ( int ) paletteSearchResults . size ( ) ) {
curPaletteChoice = paletteSearchResults . size ( ) - 1 ;
navigated = true ;
}
2023-06-18 22:21:16 -04:00
for ( int i = 0 ; i < ( int ) paletteSearchResults . size ( ) ; i + + ) {
2023-06-15 01:04:45 -04:00
bool current = ( i = = curPaletteChoice ) ;
int id = paletteSearchResults [ i ] ;
2023-10-29 19:27:06 -04:00
String s = " ??? " ;
2023-06-18 22:21:16 -04:00
switch ( curPaletteType ) {
case CMDPAL_TYPE_MAIN :
2023-07-01 14:02:12 -04:00
s = guiActions [ id ] . friendlyName ;
2023-06-18 22:21:16 -04:00
break ;
case CMDPAL_TYPE_RECENT :
2023-10-29 19:05:01 -04:00
s = recentFile [ id ] . c_str ( ) ;
2023-06-18 22:21:16 -04:00
break ;
2023-07-31 14:58:38 -04:00
case CMDPAL_TYPE_INSTRUMENTS :
2023-08-15 17:37:30 -04:00
case CMDPAL_TYPE_INSTRUMENT_CHANGE :
2023-07-31 14:58:38 -04:00
if ( id = = 0 ) {
s = " - None - " ;
} else {
2023-10-29 19:27:06 -04:00
s = fmt : : sprintf ( " %02d: %s " , id - 1 , e - > song . ins [ id - 1 ] - > name . c_str ( ) ) ;
2023-07-31 14:58:38 -04:00
}
break ;
case CMDPAL_TYPE_SAMPLES :
s = e - > song . sample [ id ] - > name . c_str ( ) ;
break ;
2023-08-27 13:19:26 -04:00
case CMDPAL_TYPE_ADD_CHIP :
s = getSystemName ( ( DivSystem ) id ) ;
break ;
2023-06-18 22:21:16 -04:00
default :
2023-07-01 14:35:13 -04:00
logE ( " invalid command palette type " ) ;
2023-06-18 22:21:16 -04:00
break ;
} ;
2023-10-29 19:27:06 -04:00
if ( ImGui : : Selectable ( s . c_str ( ) , current ) ) {
2023-06-15 01:04:45 -04:00
curPaletteChoice = i ;
accepted = true ;
}
2023-07-12 10:14:51 -04:00
if ( ( navigated | | paletteFirstFrame ) & & current ) ImGui : : SetScrollHereY ( ) ;
2023-06-15 01:04:45 -04:00
}
}
ImGui : : EndChild ( ) ;
if ( ! accepted ) {
2023-06-18 22:21:16 -04:00
if ( curPaletteChoice > = ( int ) paletteSearchResults . size ( ) ) {
2023-06-15 01:04:45 -04:00
curPaletteChoice = paletteSearchResults . size ( ) - 1 ;
}
2023-07-01 14:15:41 -04:00
accepted = ImGui : : IsKeyPressed ( ImGuiKey_Enter ) ;
2023-06-15 01:04:45 -04:00
}
if ( ImGui : : Button ( " Cancel " ) | | ImGui : : IsKeyPressed ( ImGuiKey_Escape ) ) {
ImGui : : CloseCurrentPopup ( ) ;
}
2023-07-31 14:07:10 -04:00
// do not move this to after the resetPalette() calls!
// if they are called before and we're jumping from one palette to the next, the paletteFirstFrame won't be true at the start and the setup will not happen.
paletteFirstFrame = false ;
2023-06-15 01:04:45 -04:00
if ( accepted ) {
2023-07-31 15:12:29 -04:00
if ( paletteSearchResults . size ( ) > 0 ) {
2023-07-01 14:15:41 -04:00
int i = paletteSearchResults [ curPaletteChoice ] ;
switch ( curPaletteType ) {
case CMDPAL_TYPE_MAIN :
doAction ( i ) ;
break ;
2023-06-18 22:21:16 -04:00
2023-07-01 14:15:41 -04:00
case CMDPAL_TYPE_RECENT :
2023-10-29 19:05:01 -04:00
openRecentFile ( recentFile [ i ] ) ;
2023-07-01 14:15:41 -04:00
break ;
2023-06-18 22:21:16 -04:00
2023-07-31 14:58:38 -04:00
case CMDPAL_TYPE_INSTRUMENTS :
curIns = i - 1 ;
break ;
case CMDPAL_TYPE_SAMPLES :
curSample = i ;
break ;
2023-08-15 17:37:30 -04:00
case CMDPAL_TYPE_INSTRUMENT_CHANGE :
doChangeIns ( i - 1 ) ;
break ;
2023-08-27 13:19:26 -04:00
case CMDPAL_TYPE_ADD_CHIP :
if ( i ! = DIV_SYSTEM_NULL ) {
if ( ! e - > addSystem ( ( DivSystem ) i ) ) {
showError ( " cannot add chip! ( " + e - > getLastError ( ) + " ) " ) ;
} else {
MARK_MODIFIED ;
}
ImGui : : CloseCurrentPopup ( ) ;
if ( e - > song . autoSystem ) {
autoDetectSystem ( ) ;
}
updateWindowTitle ( ) ;
}
break ;
2023-07-01 14:15:41 -04:00
default :
2023-07-01 14:35:13 -04:00
logE ( " invalid command palette type " ) ;
2023-07-01 14:15:41 -04:00
break ;
} ;
}
2023-07-31 15:12:29 -04:00
ImGui : : CloseCurrentPopup ( ) ;
2023-06-18 22:21:16 -04:00
}
2023-06-15 01:04:45 -04:00
}