2017-07-13 10:26:03 +02:00
/*
* InfoWindows . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2014-07-15 10:14:49 +03:00
# include "StdInc.h"
# include "InfoWindows.h"
# include "../CGameInfo.h"
2023-04-17 12:06:58 +02:00
# include "../PlayerLocalState.h"
2014-07-15 10:14:49 +03:00
# include "../CPlayerInterface.h"
# include "../CMusicHandler.h"
# include "../widgets/CComponent.h"
# include "../widgets/MiscWidgets.h"
2023-02-01 20:42:06 +02:00
# include "../widgets/Buttons.h"
# include "../widgets/TextControls.h"
2014-07-15 10:14:49 +03:00
# include "../gui/CGuiHandler.h"
2023-05-16 14:10:26 +02:00
# include "../gui/WindowHandler.h"
2022-12-09 13:38:46 +02:00
# include "../battle/BattleInterface.h"
# include "../battle/BattleInterfaceClasses.h"
2023-05-08 14:18:34 +02:00
# include "../adventureMap/AdventureMapInterface.h"
2023-02-01 20:42:06 +02:00
# include "../windows/CMessage.h"
2023-06-02 15:42:18 +02:00
# include "../render/Canvas.h"
2023-02-01 20:42:06 +02:00
# include "../renderSDL/SDL_Extensions.h"
# include "../gui/CursorHandler.h"
2023-04-27 19:21:06 +02:00
# include "../gui/Shortcut.h"
2014-07-15 10:14:49 +03:00
# include "../../CCallback.h"
# include "../../lib/CConfigHandler.h"
# include "../../lib/CondSh.h"
# include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
# include "../../lib/mapObjects/CGHeroInstance.h"
# include "../../lib/mapObjects/CGTownInstance.h"
# include "../../lib/mapObjects/MiscObjects.h"
2023-06-23 17:02:48 +02:00
# include "../../lib/gameState/InfoAboutArmy.h"
2014-07-15 10:14:49 +03:00
2023-01-30 19:55:32 +02:00
# include <SDL_surface.h>
2023-06-02 15:42:18 +02:00
void CSimpleWindow : : show ( Canvas & to )
2014-07-15 10:14:49 +03:00
{
if ( bitmap )
2023-06-02 15:42:18 +02:00
CSDL_Ext : : blitAt ( bitmap , pos . x , pos . y , to . getInternalSurface ( ) ) ;
2014-07-15 10:14:49 +03:00
}
CSimpleWindow : : ~ CSimpleWindow ( )
{
if ( bitmap )
{
SDL_FreeSurface ( bitmap ) ;
bitmap = nullptr ;
}
}
void CSelWindow : : selectionChange ( unsigned to )
{
for ( unsigned i = 0 ; i < components . size ( ) ; i + + )
{
2018-04-07 13:34:11 +02:00
auto pom = std : : dynamic_pointer_cast < CSelectableComponent > ( components [ i ] ) ;
2014-07-15 10:14:49 +03:00
if ( ! pom )
continue ;
pom - > select ( i = = to ) ;
}
redraw ( ) ;
}
2018-04-07 13:34:11 +02:00
CSelWindow : : CSelWindow ( const std : : string & Text , PlayerColor player , int charperline , const std : : vector < std : : shared_ptr < CSelectableComponent > > & comps , const std : : vector < std : : pair < std : : string , CFunctionList < void ( ) > > > & Buttons , QueryID askID )
2014-07-15 10:14:49 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-15 10:14:49 +03:00
ID = askID ;
2014-09-05 21:13:58 +03:00
for ( int i = 0 ; i < Buttons . size ( ) ; i + + )
2014-07-15 10:14:49 +03:00
{
2018-04-07 13:34:11 +02:00
buttons . push_back ( std : : make_shared < CButton > ( Point ( 0 , 0 ) , Buttons [ i ] . first , CButton : : tooltip ( ) , Buttons [ i ] . second ) ) ;
2014-09-05 21:13:58 +03:00
if ( ! i & & askID . getNum ( ) > = 0 )
buttons . back ( ) - > addCallback ( std : : bind ( & CSelWindow : : madeChoice , this ) ) ;
buttons [ i ] - > addCallback ( std : : bind ( & CInfoWindow : : close , this ) ) ; //each button will close the window apart from call-defined actions
2014-07-15 10:14:49 +03:00
}
2022-11-26 23:12:20 +02:00
text = std : : make_shared < CTextBox > ( Text , Rect ( 0 , 0 , 250 , 100 ) , 0 , FONT_MEDIUM , ETextAlignment : : CENTER , Colors : : WHITE ) ;
2014-07-15 10:14:49 +03:00
2014-09-05 21:13:58 +03:00
if ( buttons . size ( ) > 1 & & askID . getNum ( ) > = 0 ) //cancel button functionality
{
buttons . back ( ) - > addCallback ( [ askID ] ( ) {
LOCPLINT - > cb . get ( ) - > selectionMade ( 0 , askID ) ;
} ) ;
//buttons.back()->addCallback(std::bind(&CCallback::selectionMade, LOCPLINT->cb.get(), 0, askID));
}
2014-07-15 10:14:49 +03:00
2023-07-22 21:29:05 +02:00
if ( buttons . size ( ) = = 1 )
buttons . front ( ) - > assignedKey = EShortcut : : GLOBAL_RETURN ;
if ( buttons . size ( ) = = 2 )
{
buttons . front ( ) - > assignedKey = EShortcut : : GLOBAL_ACCEPT ;
buttons . back ( ) - > assignedKey = EShortcut : : GLOBAL_CANCEL ;
}
2014-07-15 10:14:49 +03:00
for ( int i = 0 ; i < comps . size ( ) ; i + + )
{
2018-04-07 13:34:11 +02:00
comps [ i ] - > recActions = 255 - DISPOSE ;
addChild ( comps [ i ] . get ( ) ) ;
2014-07-15 10:14:49 +03:00
components . push_back ( comps [ i ] ) ;
2014-08-09 15:14:31 +03:00
comps [ i ] - > onSelect = std : : bind ( & CSelWindow : : selectionChange , this , i ) ;
2023-04-27 19:21:06 +02:00
if ( i < 8 )
2023-04-27 23:29:16 +02:00
comps [ i ] - > assignedKey = vstd : : next ( EShortcut : : SELECT_INDEX_1 , i ) ;
2014-07-15 10:14:49 +03:00
}
CMessage : : drawIWindow ( this , Text , player ) ;
}
void CSelWindow : : madeChoice ( )
{
if ( ID . getNum ( ) < 0 )
return ;
int ret = - 1 ;
for ( int i = 0 ; i < components . size ( ) ; i + + )
{
2018-04-07 13:34:11 +02:00
if ( std : : dynamic_pointer_cast < CSelectableComponent > ( components [ i ] ) - > selected )
2014-07-15 10:14:49 +03:00
{
ret = i ;
}
}
LOCPLINT - > cb - > selectionMade ( ret + 1 , ID ) ;
}
2018-04-07 13:34:11 +02:00
CInfoWindow : : CInfoWindow ( std : : string Text , PlayerColor player , const TCompsInfo & comps , const TButtonsInfo & Buttons )
2014-07-15 10:14:49 +03:00
{
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
2014-07-15 10:14:49 +03:00
ID = QueryID ( - 1 ) ;
for ( auto & Button : Buttons )
{
2018-04-07 13:34:11 +02:00
std : : shared_ptr < CButton > button = std : : make_shared < CButton > ( Point ( 0 , 0 ) , Button . first , CButton : : tooltip ( ) , std : : bind ( & CInfoWindow : : close , this ) ) ;
2018-03-17 06:23:22 +02:00
button - > setBorderColor ( Colors : : METALLIC_GOLD ) ;
2014-08-03 14:16:19 +03:00
button - > addCallback ( Button . second ) ; //each button will close the window apart from call-defined actions
2014-07-15 10:14:49 +03:00
buttons . push_back ( button ) ;
}
2022-11-26 23:12:20 +02:00
text = std : : make_shared < CTextBox > ( Text , Rect ( 0 , 0 , 250 , 100 ) , 0 , FONT_MEDIUM , ETextAlignment : : CENTER , Colors : : WHITE ) ;
2014-07-15 10:14:49 +03:00
if ( ! text - > slider )
{
text - > resize ( text - > label - > textSize ) ;
}
2023-04-28 13:22:03 +02:00
if ( buttons . size ( ) = = 1 )
buttons . front ( ) - > assignedKey = EShortcut : : GLOBAL_RETURN ;
if ( buttons . size ( ) = = 2 )
{
buttons . front ( ) - > assignedKey = EShortcut : : GLOBAL_ACCEPT ;
buttons . back ( ) - > assignedKey = EShortcut : : GLOBAL_CANCEL ;
}
2014-07-15 10:14:49 +03:00
for ( auto & comp : comps )
{
2018-04-07 13:34:11 +02:00
comp - > recActions = 0xff & ~ DISPOSE ;
addChild ( comp . get ( ) ) ;
2014-07-15 10:14:49 +03:00
comp - > recActions & = ~ ( SHOWALL | UPDATE ) ;
components . push_back ( comp ) ;
}
2018-04-07 13:34:11 +02:00
2014-07-15 10:14:49 +03:00
CMessage : : drawIWindow ( this , Text , player ) ;
}
CInfoWindow : : CInfoWindow ( )
{
ID = QueryID ( - 1 ) ;
}
void CInfoWindow : : close ( )
{
2018-07-25 00:36:48 +02:00
WindowBase : : close ( ) ;
2014-07-15 10:14:49 +03:00
if ( LOCPLINT )
LOCPLINT - > showingDialog - > setn ( false ) ;
}
2023-06-02 15:42:18 +02:00
void CInfoWindow : : show ( Canvas & to )
2014-07-15 10:14:49 +03:00
{
CIntObject : : show ( to ) ;
}
2018-04-07 13:34:11 +02:00
CInfoWindow : : ~ CInfoWindow ( ) = default ;
2014-07-15 10:14:49 +03:00
2023-06-02 15:42:18 +02:00
void CInfoWindow : : showAll ( Canvas & to )
2014-07-15 10:14:49 +03:00
{
CSimpleWindow : : show ( to ) ;
CIntObject : : showAll ( to ) ;
}
2018-04-07 13:34:11 +02:00
void CInfoWindow : : showInfoDialog ( const std : : string & text , const TCompsInfo & components , PlayerColor player )
2014-07-15 10:14:49 +03:00
{
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( CInfoWindow : : create ( text , player , components ) ) ;
2014-07-15 10:14:49 +03:00
}
2018-04-07 13:34:11 +02:00
void CInfoWindow : : showYesNoDialog ( const std : : string & text , const TCompsInfo & components , const CFunctionList < void ( ) > & onYes , const CFunctionList < void ( ) > & onNo , PlayerColor player )
2014-07-15 10:14:49 +03:00
{
assert ( ! LOCPLINT | | LOCPLINT - > showingDialog - > get ( ) ) ;
std : : vector < std : : pair < std : : string , CFunctionList < void ( ) > > > pom ;
pom . push_back ( std : : pair < std : : string , CFunctionList < void ( ) > > ( " IOKAY.DEF " , 0 ) ) ;
pom . push_back ( std : : pair < std : : string , CFunctionList < void ( ) > > ( " ICANCEL.DEF " , 0 ) ) ;
2018-07-25 00:36:48 +02:00
std : : shared_ptr < CInfoWindow > temp = std : : make_shared < CInfoWindow > ( text , player , components , pom ) ;
2014-11-12 22:45:48 +02:00
temp - > buttons [ 0 ] - > addCallback ( onYes ) ;
temp - > buttons [ 1 ] - > addCallback ( onNo ) ;
2014-07-15 10:14:49 +03:00
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( temp ) ;
2014-07-15 10:14:49 +03:00
}
2018-07-25 00:36:48 +02:00
std : : shared_ptr < CInfoWindow > CInfoWindow : : create ( const std : : string & text , PlayerColor playerID , const TCompsInfo & components )
2014-07-15 10:14:49 +03:00
{
std : : vector < std : : pair < std : : string , CFunctionList < void ( ) > > > pom ;
pom . push_back ( std : : pair < std : : string , CFunctionList < void ( ) > > ( " IOKAY.DEF " , 0 ) ) ;
2018-07-25 00:36:48 +02:00
return std : : make_shared < CInfoWindow > ( text , playerID , components , pom ) ;
2014-07-15 10:14:49 +03:00
}
std : : string CInfoWindow : : genText ( std : : string title , std : : string description )
{
return std : : string ( " { " ) + title + " } " + " \n \n " + description ;
}
CInfoPopup : : CInfoPopup ( SDL_Surface * Bitmap , int x , int y , bool Free )
: free ( Free ) , bitmap ( Bitmap )
{
init ( x , y ) ;
}
2022-11-26 23:12:20 +02:00
CInfoPopup : : CInfoPopup ( SDL_Surface * Bitmap , const Point & p , ETextAlignment alignment , bool Free )
2014-07-15 10:14:49 +03:00
: free ( Free ) , bitmap ( Bitmap )
{
switch ( alignment )
{
2022-11-26 23:12:20 +02:00
case ETextAlignment : : BOTTOMRIGHT :
2014-07-15 10:14:49 +03:00
init ( p . x - Bitmap - > w , p . y - Bitmap - > h ) ;
break ;
2022-11-26 23:12:20 +02:00
case ETextAlignment : : CENTER :
2014-07-15 10:14:49 +03:00
init ( p . x - Bitmap - > w / 2 , p . y - Bitmap - > h / 2 ) ;
break ;
2022-11-26 23:12:20 +02:00
case ETextAlignment : : TOPLEFT :
2014-07-15 10:14:49 +03:00
init ( p . x , p . y ) ;
break ;
default :
assert ( 0 ) ; //not implemented
}
}
CInfoPopup : : CInfoPopup ( SDL_Surface * Bitmap , bool Free )
{
CCS - > curh - > hide ( ) ;
free = Free ;
bitmap = Bitmap ;
if ( bitmap )
{
2023-02-08 13:56:09 +02:00
pos . x = GH . screenDimensions ( ) . x / 2 - bitmap - > w / 2 ;
pos . y = GH . screenDimensions ( ) . y / 2 - bitmap - > h / 2 ;
2014-07-15 10:14:49 +03:00
pos . h = bitmap - > h ;
pos . w = bitmap - > w ;
}
}
void CInfoPopup : : close ( )
{
if ( free )
SDL_FreeSurface ( bitmap ) ;
2018-07-25 00:36:48 +02:00
WindowBase : : close ( ) ;
2014-07-15 10:14:49 +03:00
}
2023-06-02 15:42:18 +02:00
void CInfoPopup : : show ( Canvas & to )
2014-07-15 10:14:49 +03:00
{
2023-06-02 15:42:18 +02:00
CSDL_Ext : : blitAt ( bitmap , pos . x , pos . y , to . getInternalSurface ( ) ) ;
2014-07-15 10:14:49 +03:00
}
CInfoPopup : : ~ CInfoPopup ( )
{
CCS - > curh - > show ( ) ;
}
void CInfoPopup : : init ( int x , int y )
{
CCS - > curh - > hide ( ) ;
pos . x = x ;
pos . y = y ;
pos . h = bitmap - > h ;
pos . w = bitmap - > w ;
// Put the window back on screen if necessary
vstd : : amax ( pos . x , 0 ) ;
vstd : : amax ( pos . y , 0 ) ;
2023-02-03 18:23:53 +02:00
vstd : : amin ( pos . x , GH . screenDimensions ( ) . x - bitmap - > w ) ;
vstd : : amin ( pos . y , GH . screenDimensions ( ) . y - bitmap - > h ) ;
2014-07-15 10:14:49 +03:00
}
2023-06-11 19:38:42 +02:00
bool CRClickPopup : : isPopupWindow ( ) const
2014-07-15 10:14:49 +03:00
{
2023-06-11 19:38:42 +02:00
return true ;
2014-07-15 10:14:49 +03:00
}
void CRClickPopup : : close ( )
{
2018-07-25 00:36:48 +02:00
WindowBase : : close ( ) ;
2014-07-15 10:14:49 +03:00
}
void CRClickPopup : : createAndPush ( const std : : string & txt , const CInfoWindow : : TCompsInfo & comps )
{
PlayerColor player = LOCPLINT ? LOCPLINT - > playerID : PlayerColor ( 1 ) ; //if no player, then use blue
2017-06-03 07:25:10 +02:00
if ( settings [ " session " ] [ " spectate " ] . Bool ( ) ) //TODO: there must be better way to implement this
player = PlayerColor ( 1 ) ;
2014-07-15 10:14:49 +03:00
2018-07-25 00:36:48 +02:00
auto temp = std : : make_shared < CInfoWindow > ( txt , player , comps ) ;
2023-01-27 00:27:06 +02:00
temp - > center ( GH . getCursorPosition ( ) ) ; //center on mouse
2023-06-11 19:38:42 +02:00
# ifdef VCMI_MOBILE
temp - > moveBy ( { 0 , - temp - > pos . h / 2 } ) ;
2021-03-21 12:43:01 +02:00
# endif
2014-07-15 10:14:49 +03:00
temp - > fitToScreen ( 10 ) ;
2018-07-25 00:36:48 +02:00
2023-05-16 15:20:35 +02:00
GH . windows ( ) . createAndPushWindow < CRClickPopupInt > ( temp ) ;
2014-07-15 10:14:49 +03:00
}
2018-04-07 13:34:11 +02:00
void CRClickPopup : : createAndPush ( const std : : string & txt , std : : shared_ptr < CComponent > component )
2014-07-15 10:14:49 +03:00
{
CInfoWindow : : TCompsInfo intComps ;
intComps . push_back ( component ) ;
createAndPush ( txt , intComps ) ;
}
2022-11-26 23:12:20 +02:00
void CRClickPopup : : createAndPush ( const CGObjectInstance * obj , const Point & p , ETextAlignment alignment )
2014-07-15 10:14:49 +03:00
{
2018-07-25 00:36:48 +02:00
auto iWin = createInfoWin ( p , obj ) ; //try get custom infowindow for this obj
2014-07-15 10:14:49 +03:00
if ( iWin )
2018-07-25 00:36:48 +02:00
{
2023-05-16 15:20:35 +02:00
GH . windows ( ) . pushWindow ( iWin ) ;
2018-07-25 00:36:48 +02:00
}
2014-07-15 10:14:49 +03:00
else
{
2023-04-17 12:06:58 +02:00
if ( LOCPLINT - > localState - > getCurrentHero ( ) )
CRClickPopup : : createAndPush ( obj - > getHoverText ( LOCPLINT - > localState - > getCurrentHero ( ) ) ) ;
2014-07-15 10:14:49 +03:00
else
CRClickPopup : : createAndPush ( obj - > getHoverText ( LOCPLINT - > playerID ) ) ;
}
}
2018-07-25 00:36:48 +02:00
CRClickPopupInt : : CRClickPopupInt ( std : : shared_ptr < CIntObject > our )
2014-07-15 10:14:49 +03:00
{
CCS - > curh - > hide ( ) ;
2018-07-25 00:36:48 +02:00
defActions = SHOWALL | UPDATE ;
our - > recActions = defActions ;
2014-07-15 10:14:49 +03:00
inner = our ;
2018-07-25 00:36:48 +02:00
addChild ( our . get ( ) , false ) ;
2014-07-15 10:14:49 +03:00
}
CRClickPopupInt : : ~ CRClickPopupInt ( )
{
CCS - > curh - > show ( ) ;
}
Point CInfoBoxPopup : : toScreen ( Point p )
{
2023-02-10 23:29:13 +02:00
auto bounds = adventureInt - > terrainAreaPixels ( ) ;
vstd : : abetween ( p . x , bounds . top ( ) + 100 , bounds . bottom ( ) - 100 ) ;
vstd : : abetween ( p . y , bounds . left ( ) + 100 , bounds . right ( ) - 100 ) ;
2014-07-15 10:14:49 +03:00
return p ;
}
2018-04-07 13:34:11 +02:00
CInfoBoxPopup : : CInfoBoxPopup ( Point position , const CGTownInstance * town )
: CWindowObject ( RCLICK_POPUP | PLAYER_COLORED , " TOWNQVBK " , toScreen ( position ) )
2014-07-15 10:14:49 +03:00
{
InfoAboutTown iah ;
2023-04-17 12:06:58 +02:00
LOCPLINT - > cb - > getTownInfo ( town , iah , LOCPLINT - > localState - > getCurrentTown ( ) ) ; //todo: should this be nearest hero?
2014-07-15 10:14:49 +03:00
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
tooltip = std : : make_shared < CTownTooltip > ( Point ( 9 , 10 ) , iah ) ;
2014-07-15 10:14:49 +03:00
}
2018-04-07 13:34:11 +02:00
CInfoBoxPopup : : CInfoBoxPopup ( Point position , const CGHeroInstance * hero )
: CWindowObject ( RCLICK_POPUP | PLAYER_COLORED , " HEROQVBK " , toScreen ( position ) )
2014-07-15 10:14:49 +03:00
{
InfoAboutHero iah ;
2023-04-17 12:06:58 +02:00
LOCPLINT - > cb - > getHeroInfo ( hero , iah , LOCPLINT - > localState - > getCurrentHero ( ) ) ; //todo: should this be nearest hero?
2014-07-15 10:14:49 +03:00
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
tooltip = std : : make_shared < CHeroTooltip > ( Point ( 9 , 10 ) , iah ) ;
2014-07-15 10:14:49 +03:00
}
2018-04-07 13:34:11 +02:00
CInfoBoxPopup : : CInfoBoxPopup ( Point position , const CGGarrison * garr )
: CWindowObject ( RCLICK_POPUP | PLAYER_COLORED , " TOWNQVBK " , toScreen ( position ) )
2014-07-15 10:14:49 +03:00
{
InfoAboutTown iah ;
LOCPLINT - > cb - > getTownInfo ( garr , iah ) ;
2018-04-07 13:34:11 +02:00
OBJECT_CONSTRUCTION_CAPTURING ( 255 - DISPOSE ) ;
tooltip = std : : make_shared < CArmyTooltip > ( Point ( 9 , 10 ) , iah ) ;
2014-07-15 10:14:49 +03:00
}
2018-07-25 00:36:48 +02:00
std : : shared_ptr < WindowBase > CRClickPopup : : createInfoWin ( Point position , const CGObjectInstance * specific ) //specific=0 => draws info about selected town/hero
2014-07-15 10:14:49 +03:00
{
2015-02-06 14:41:26 +02:00
if ( nullptr = = specific )
2023-04-17 12:06:58 +02:00
specific = LOCPLINT - > localState - > getCurrentArmy ( ) ;
2018-04-07 13:34:11 +02:00
2015-02-06 14:41:26 +02:00
if ( nullptr = = specific )
{
logGlobal - > error ( " createInfoWin: no object to describe " ) ;
return nullptr ;
2018-04-07 13:34:11 +02:00
}
2014-07-15 10:14:49 +03:00
switch ( specific - > ID )
{
case Obj : : HERO :
2018-07-25 00:36:48 +02:00
return std : : make_shared < CInfoBoxPopup > ( position , dynamic_cast < const CGHeroInstance * > ( specific ) ) ;
2014-07-15 10:14:49 +03:00
case Obj : : TOWN :
2018-07-25 00:36:48 +02:00
return std : : make_shared < CInfoBoxPopup > ( position , dynamic_cast < const CGTownInstance * > ( specific ) ) ;
2014-07-15 10:14:49 +03:00
case Obj : : GARRISON :
case Obj : : GARRISON2 :
2018-07-25 00:36:48 +02:00
return std : : make_shared < CInfoBoxPopup > ( position , dynamic_cast < const CGGarrison * > ( specific ) ) ;
2014-07-15 10:14:49 +03:00
default :
2018-07-25 00:36:48 +02:00
return std : : shared_ptr < WindowBase > ( ) ;
2014-07-15 10:14:49 +03:00
}
}