Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

436 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "vguitextwindow.h"
  9. #include <networkstringtabledefs.h>
  10. #include <cdll_client_int.h>
  11. #include <clientmode_shared.h>
  12. #include <vgui/IScheme.h>
  13. #include <vgui/ILocalize.h>
  14. #include <vgui/ISurface.h>
  15. #include <filesystem.h>
  16. #include <KeyValues.h>
  17. #include <convar.h>
  18. #include <vgui_controls/ImageList.h>
  19. #include <vgui_controls/TextEntry.h>
  20. #include <vgui_controls/Button.h>
  21. #include <game/client/iviewport.h>
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. using namespace vgui;
  25. extern INetworkStringTable *g_pStringTableInfoPanel;
  26. #define TEMP_HTML_FILE "textwindow_temp.html"
  27. ConVar cl_disablehtmlmotd( "cl_disablehtmlmotd", "0", FCVAR_ARCHIVE, "Disable HTML motds." );
  28. //=============================================================================
  29. // HPE_BEGIN:
  30. // [Forrest] Replaced text window command string with TEXTWINDOW_CMD enumeration
  31. // of options. Passing a command string is dangerous and allowed a server network
  32. // message to run arbitrary commands on the client.
  33. //=============================================================================
  34. CON_COMMAND( showinfo, "Shows a info panel: <type> <title> <message> [<command number>]" )
  35. {
  36. if ( !gViewPortInterface )
  37. return;
  38. if ( args.ArgC() < 4 )
  39. return;
  40. IViewPortPanel * panel = gViewPortInterface->FindPanelByName( PANEL_INFO );
  41. if ( panel )
  42. {
  43. KeyValues *kv = new KeyValues("data");
  44. kv->SetInt( "type", Q_atoi(args[ 1 ]) );
  45. kv->SetString( "title", args[ 2 ] );
  46. kv->SetString( "message", args[ 3 ] );
  47. if ( args.ArgC() == 5 )
  48. kv->SetString( "command", args[ 4 ] );
  49. panel->SetData( kv );
  50. gViewPortInterface->ShowPanel( panel, true );
  51. kv->deleteThis();
  52. }
  53. else
  54. {
  55. Msg("Couldn't find info panel.\n" );
  56. }
  57. }
  58. //=============================================================================
  59. // HPE_END
  60. //=============================================================================
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Constructor
  63. //-----------------------------------------------------------------------------
  64. CTextWindow::CTextWindow(IViewPort *pViewPort) : Frame(NULL, PANEL_INFO )
  65. {
  66. // initialize dialog
  67. m_pViewPort = pViewPort;
  68. // SetTitle("", true);
  69. m_szTitle[0] = '\0';
  70. m_szMessage[0] = '\0';
  71. m_szMessageFallback[0] = '\0';
  72. m_nExitCommand = TEXTWINDOW_CMD_NONE;
  73. m_bShownURL = false;
  74. m_bUnloadOnDismissal = false;
  75. // load the new scheme early!!
  76. SetScheme("ClientScheme");
  77. SetMoveable(false);
  78. SetSizeable(false);
  79. SetProportional(true);
  80. // hide the system buttons
  81. SetTitleBarVisible( false );
  82. m_pTextMessage = new TextEntry( this, "TextMessage" );
  83. m_pHTMLMessage = new CMOTDHTML( this,"HTMLMessage" );
  84. m_pTitleLabel = new Label( this, "MessageTitle", "Message Title" );
  85. m_pOK = new Button(this, "ok", "#PropertyDialog_OK");
  86. m_pOK->SetCommand("okay");
  87. m_pTextMessage->SetMultiline( true );
  88. m_nContentType = TYPE_TEXT;
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Purpose:
  92. //-----------------------------------------------------------------------------
  93. void CTextWindow::ApplySchemeSettings( IScheme *pScheme )
  94. {
  95. BaseClass::ApplySchemeSettings( pScheme );
  96. LoadControlSettings("Resource/UI/TextWindow.res");
  97. Reset();
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose: Destructor
  101. //-----------------------------------------------------------------------------
  102. CTextWindow::~CTextWindow()
  103. {
  104. // remove temp file again
  105. g_pFullFileSystem->RemoveFile( TEMP_HTML_FILE, "DEFAULT_WRITE_PATH" );
  106. }
  107. void CTextWindow::Reset( void )
  108. {
  109. //=============================================================================
  110. // HPE_BEGIN:
  111. // [Forrest] Replace strange hard-coded default message with hard-coded error message.
  112. //=============================================================================
  113. V_strcpy_safe( m_szTitle, "Error loading info message." );
  114. V_strcpy_safe( m_szMessage, "" );
  115. V_strcpy_safe( m_szMessageFallback, "" );
  116. //=============================================================================
  117. // HPE_END
  118. //=============================================================================
  119. m_nExitCommand = TEXTWINDOW_CMD_NONE;
  120. m_nContentType = TYPE_TEXT;
  121. m_bShownURL = false;
  122. m_bUnloadOnDismissal = false;
  123. Update();
  124. }
  125. void CTextWindow::ShowText( const char *text )
  126. {
  127. m_pTextMessage->SetVisible( true );
  128. m_pTextMessage->SetText( text );
  129. m_pTextMessage->GotoTextStart();
  130. }
  131. void CTextWindow::ShowURL( const char *URL, bool bAllowUserToDisable )
  132. {
  133. #ifdef _DEBUG
  134. Msg( "CTextWindow::ShowURL( %s )\n", URL );
  135. #endif
  136. ClientModeShared *mode = ( ClientModeShared * )GetClientModeNormal();
  137. if ( ( bAllowUserToDisable && cl_disablehtmlmotd.GetBool() ) || !mode->IsHTMLInfoPanelAllowed() )
  138. {
  139. Warning( "Blocking HTML info panel '%s'; Using plaintext instead.\n", URL );
  140. // User has disabled HTML TextWindows. Show the fallback as text only.
  141. if ( g_pStringTableInfoPanel )
  142. {
  143. int index = g_pStringTableInfoPanel->FindStringIndex( "motd_text" );
  144. if ( index != ::INVALID_STRING_INDEX )
  145. {
  146. int length = 0;
  147. const char *data = (const char *)g_pStringTableInfoPanel->GetStringUserData( index, &length );
  148. if ( data && data[0] )
  149. {
  150. m_pHTMLMessage->SetVisible( false );
  151. ShowText( data );
  152. }
  153. }
  154. }
  155. return;
  156. }
  157. m_pHTMLMessage->SetVisible( true );
  158. m_pHTMLMessage->OpenURL( URL, NULL );
  159. m_bShownURL = true;
  160. }
  161. void CTextWindow::ShowIndex( const char *entry )
  162. {
  163. const char *data = NULL;
  164. int length = 0;
  165. if ( NULL == g_pStringTableInfoPanel )
  166. return;
  167. int index = g_pStringTableInfoPanel->FindStringIndex( m_szMessage );
  168. if ( index != ::INVALID_STRING_INDEX )
  169. data = (const char *)g_pStringTableInfoPanel->GetStringUserData( index, &length );
  170. if ( !data || !data[0] )
  171. return; // nothing to show
  172. // is this a web URL ?
  173. if ( !Q_strncmp( data, "http://", 7 ) || !Q_strncmp( data, "https://", 8 ) )
  174. {
  175. ShowURL( data );
  176. return;
  177. }
  178. // try to figure out if this is HTML or not
  179. if ( data[0] != '<' )
  180. {
  181. ShowText( data );
  182. return;
  183. }
  184. // data is a HTML, we have to write to a file and then load the file
  185. FileHandle_t hFile = g_pFullFileSystem->Open( TEMP_HTML_FILE, "wb", "DEFAULT_WRITE_PATH" );
  186. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  187. return;
  188. g_pFullFileSystem->Write( data, length, hFile );
  189. g_pFullFileSystem->Close( hFile );
  190. if ( g_pFullFileSystem->Size( TEMP_HTML_FILE ) != (unsigned int)length )
  191. return; // something went wrong while writing
  192. ShowFile( TEMP_HTML_FILE );
  193. }
  194. void CTextWindow::ShowFile( const char *filename )
  195. {
  196. if ( Q_stristr( filename, ".htm" ) || Q_stristr( filename, ".html" ) )
  197. {
  198. // it's a local HTML file
  199. char localURL[ _MAX_PATH + 7 ];
  200. Q_strncpy( localURL, "file://", sizeof( localURL ) );
  201. char pPathData[ _MAX_PATH ];
  202. g_pFullFileSystem->GetLocalPath( filename, pPathData, sizeof(pPathData) );
  203. Q_strncat( localURL, pPathData, sizeof( localURL ), COPY_ALL_CHARACTERS );
  204. ShowURL( localURL );
  205. }
  206. else
  207. {
  208. // read from local text from file
  209. FileHandle_t f = g_pFullFileSystem->Open( m_szMessage, "rb", "GAME" );
  210. if ( !f )
  211. return;
  212. char buffer[2048];
  213. int size = MIN( g_pFullFileSystem->Size( f ), sizeof(buffer)-1 ); // just allow 2KB
  214. g_pFullFileSystem->Read( buffer, size, f );
  215. g_pFullFileSystem->Close( f );
  216. buffer[size]=0; //terminate string
  217. ShowText( buffer );
  218. }
  219. }
  220. void CTextWindow::Update( void )
  221. {
  222. SetTitle( m_szTitle, false );
  223. m_pTitleLabel->SetText( m_szTitle );
  224. if ( m_pHTMLMessage )
  225. m_pHTMLMessage->SetVisible( false );
  226. m_pTextMessage->SetVisible( false );
  227. if ( m_nContentType == TYPE_INDEX )
  228. {
  229. ShowIndex( m_szMessage );
  230. }
  231. else if ( m_nContentType == TYPE_URL )
  232. {
  233. if ( !Q_strncmp( m_szMessage, "http://", 7 ) || !Q_strncmp( m_szMessage, "https://", 8 ) || !Q_stricmp( m_szMessage, "about:blank" ) )
  234. {
  235. ShowURL( m_szMessage );
  236. }
  237. else
  238. {
  239. // We should have trapped this at a higher level
  240. Assert( !"URL protocol is missing or blocked" );
  241. }
  242. }
  243. else if ( m_nContentType == TYPE_FILE )
  244. {
  245. ShowFile( m_szMessage );
  246. }
  247. else if ( m_nContentType == TYPE_TEXT )
  248. {
  249. ShowText( m_szMessage );
  250. }
  251. else
  252. {
  253. DevMsg("CTextWindow::Update: unknown content type %i\n", m_nContentType );
  254. }
  255. }
  256. void CTextWindow::OnCommand( const char *command )
  257. {
  258. if (!Q_strcmp(command, "okay"))
  259. {
  260. //=============================================================================
  261. // HPE_BEGIN:
  262. // [Forrest] Replaced text window command string with TEXTWINDOW_CMD enumeration
  263. // of options. Passing a command string is dangerous and allowed a server network
  264. // message to run arbitrary commands on the client.
  265. //=============================================================================
  266. const char *pszCommand = NULL;
  267. switch ( m_nExitCommand )
  268. {
  269. case TEXTWINDOW_CMD_NONE:
  270. break;
  271. case TEXTWINDOW_CMD_JOINGAME:
  272. pszCommand = "joingame";
  273. break;
  274. case TEXTWINDOW_CMD_CHANGETEAM:
  275. pszCommand = "changeteam";
  276. break;
  277. case TEXTWINDOW_CMD_IMPULSE101:
  278. pszCommand = "impulse 101";
  279. break;
  280. case TEXTWINDOW_CMD_MAPINFO:
  281. pszCommand = "mapinfo";
  282. break;
  283. case TEXTWINDOW_CMD_CLOSED_HTMLPAGE:
  284. pszCommand = "closed_htmlpage";
  285. break;
  286. case TEXTWINDOW_CMD_CHOOSETEAM:
  287. pszCommand = "chooseteam";
  288. break;
  289. default:
  290. DevMsg("CTextWindow::OnCommand: unknown exit command value %i\n", m_nExitCommand );
  291. break;
  292. }
  293. if ( pszCommand != NULL )
  294. {
  295. engine->ClientCmd_Unrestricted( pszCommand );
  296. }
  297. //=============================================================================
  298. // HPE_END
  299. //=============================================================================
  300. m_pViewPort->ShowPanel( this, false );
  301. }
  302. BaseClass::OnCommand(command);
  303. }
  304. void CTextWindow::OnKeyCodePressed( vgui::KeyCode code )
  305. {
  306. if ( code == KEY_XBUTTON_A || code == KEY_XBUTTON_B || code == STEAMCONTROLLER_A || code == STEAMCONTROLLER_B )
  307. {
  308. OnCommand( "okay" );
  309. return;
  310. }
  311. BaseClass::OnKeyCodePressed(code);
  312. }
  313. void CTextWindow::SetData(KeyValues *data)
  314. {
  315. SetData( data->GetInt( "type" ), data->GetString( "title" ), data->GetString( "msg" ), data->GetString( "msg_fallback" ), data->GetInt( "cmd" ), data->GetBool( "unload" ) );
  316. }
  317. void CTextWindow::SetData( int type, const char *title, const char *message, const char *message_fallback, int command, bool bUnload )
  318. {
  319. Q_strncpy( m_szTitle, title, sizeof( m_szTitle ) );
  320. Q_strncpy( m_szMessage, message, sizeof( m_szMessage ) );
  321. Q_strncpy( m_szMessageFallback, message_fallback, sizeof( m_szMessageFallback ) );
  322. m_nExitCommand = command;
  323. m_nContentType = type;
  324. m_bUnloadOnDismissal = bUnload;
  325. Update();
  326. }
  327. void CTextWindow::ShowPanel( bool bShow )
  328. {
  329. if ( BaseClass::IsVisible() == bShow )
  330. return;
  331. m_pViewPort->ShowBackGround( bShow );
  332. if ( bShow )
  333. {
  334. Activate();
  335. SetMouseInputEnabled( true );
  336. }
  337. else
  338. {
  339. SetVisible( false );
  340. SetMouseInputEnabled( false );
  341. if ( m_bUnloadOnDismissal && m_bShownURL && m_pHTMLMessage )
  342. {
  343. m_pHTMLMessage->OpenURL( "about:blank", NULL );
  344. m_bShownURL = false;
  345. }
  346. }
  347. }
  348. bool CTextWindow::CMOTDHTML::OnStartRequest( const char *url, const char *target, const char *pchPostData, bool bIsRedirect )
  349. {
  350. if ( Q_strstr( url, "steam://" ) )
  351. return false;
  352. return BaseClass::OnStartRequest( url, target, pchPostData, bIsRedirect );
  353. }