source-engine/utils/vproject/vproject.cpp

649 lines
15 KiB
C++
Raw Permalink Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// VPROJECT.CPP
//
//=====================================================================================//
#include "vproject.h"
#define MAX_PROJECTS 50
#define ID_PROJECTS_LISTVIEW 100
// column id
#define ID_PROJECT_NAME 0
#define ID_PROJECT_DIRECTORY 1
#define WM_TRAY (WM_APP + 1)
#define ID_TRAY 5000
#define ID_TRAY_ADDVPROJECT 6000
#define ID_TRAY_MODIFYVPROJECT 6001
#define ID_TRAY_MOVEUP 6002
#define ID_TRAY_MOVEDOWN 6003
#define ID_TRAY_EXIT 6004
typedef struct
{
char *pName;
char *pGamedir;
} project_t;
HWND g_hWnd;
char g_project_name[256];
char g_project_gamedir[256];
HINSTANCE g_hInstance;
project_t g_projects[MAX_PROJECTS];
int g_numProjects;
NOTIFYICONDATA g_iconData;
HMENU g_hMenu;
int g_nActiveVProject;
POINT g_cursorPoint;
void TrayMessageHandler( HWND hWnd, UINT uMessageID );
//-----------------------------------------------------------------------------
// SetVProject
//
//-----------------------------------------------------------------------------
void SetVProject( const char *pProjectName )
{
char *pGamedir;
char project[256];
int i;
if ( pProjectName )
{
strcpy( project, pProjectName );
Sys_StripQuotesFromToken( project );
for ( i=0; i<g_numProjects; i++ )
{
if ( !stricmp( g_projects[i].pName, project ) )
{
// found
break;
}
}
if ( i >= g_numProjects )
{
// not found
return;
}
pGamedir = g_projects[i].pGamedir;
}
else
{
pGamedir = "";
}
// Changed to CURRENT_USER to solve security issues in vista!
Sys_SetRegistryString(
//"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\Environment\\VProject",
"HKEY_CURRENT_USER\\Environment\\VProject"
pGamedir );
DWORD result;
SendMessageTimeout(
HWND_BROADCAST,
WM_SETTINGCHANGE,
0,
(LPARAM)"Environment",
SMTO_ABORTIFHUNG,
0,
&result );
}
//-----------------------------------------------------------------------------
// ModifyVProject
//
//-----------------------------------------------------------------------------
void ModifyVProject( int index, const char *pName, const char *pGamedir )
{
free( g_projects[index].pName );
free( g_projects[index].pGamedir );
if ( !pName || !pName[0] )
{
// delete
if ( g_numProjects-index-1 > 0 )
{
// shift remaining elements
memcpy( &g_projects[index], &g_projects[index+1], (g_numProjects-index-1)*sizeof( project_t ) );
}
g_projects[g_numProjects-1].pName = NULL;
g_projects[g_numProjects-1].pGamedir = NULL;
g_numProjects--;
if ( g_nActiveVProject == index+1 )
{
// deleted current vproject
if ( !g_numProjects )
{
// no more projects
g_nActiveVProject = 0;
SetVProject( NULL );
}
else
{
// set to top
g_nActiveVProject = 1;
SetVProject( g_projects[0].pName );
}
}
}
else
{
g_projects[index].pName = strdup( pName );
g_projects[index].pGamedir = strdup( pGamedir );
}
}
//-----------------------------------------------------------------------------
// AddVProject
//
//-----------------------------------------------------------------------------
void AddVProject( const char *pName, const char *pGamedir )
{
if ( !pName || !pName[0] )
{
// do not add empty projects
return;
}
ModifyVProject( g_numProjects, pName, pGamedir );
g_numProjects++;
}
//-----------------------------------------------------------------------------
// LoadRegistryValues
//
//-----------------------------------------------------------------------------
void LoadRegistryValues()
{
char keyBuff[32];
char valueBuff[256];
char projectName[256];
char gamedirString[256];
char *ptr;
char *token;
int i;
for ( i=0; i<MAX_PROJECTS; i++ )
{
projectName[0] = '\0';
gamedirString[0] = '\0';
sprintf( keyBuff, "project%d", i );
Sys_GetRegistryString( keyBuff, valueBuff, "", sizeof( valueBuff ) );
// parse and populate valid values
ptr = valueBuff;
token = Sys_GetToken( &ptr, false, NULL );
if ( token[0] )
{
strcpy( projectName, token );
}
else
{
continue;
}
token = Sys_GetToken( &ptr, false, NULL );
if ( token[0] )
{
strcpy( gamedirString, token );
}
AddVProject( projectName, gamedirString );
}
}
//-----------------------------------------------------------------------------
// SaveRegistryValues
//
//-----------------------------------------------------------------------------
void SaveRegistryValues()
{
char valueBuff[256];
char keyBuff[32];
char *pProjectName;
char *pGamedir;
int len;
int i;
for ( i=0; i<MAX_PROJECTS; i++ )
{
sprintf( keyBuff, "project%d", i );
pProjectName = g_projects[i].pName;
if ( !pProjectName )
{
pProjectName = "";
}
pGamedir = g_projects[i].pGamedir;
if ( !pGamedir )
{
pGamedir = "";
}
len = _snprintf( valueBuff, sizeof( valueBuff ), "\"%s\" \"%s\"", pProjectName, pGamedir );
if ( len == -1 )
{
// kill it
valueBuff[0] = '\0';
}
Sys_SetRegistryString( keyBuff, valueBuff );
}
}
//-----------------------------------------------------------------------------
// ShiftActiveProjectUp
//
//-----------------------------------------------------------------------------
void ShiftActiveProjectUp()
{
if ( g_numProjects <= 1 || !g_nActiveVProject )
{
// nothing to do
return;
}
int active = g_nActiveVProject-1;
int previous = (active + g_numProjects - 1) % g_numProjects;
project_t tempProject;
tempProject = g_projects[previous];
g_projects[previous] = g_projects[active];
g_projects[active] = tempProject;
g_nActiveVProject = previous + 1;
}
//-----------------------------------------------------------------------------
// ShiftActiveProjectDown
//
//-----------------------------------------------------------------------------
void ShiftActiveProjectDown()
{
if ( g_numProjects <= 1 || !g_nActiveVProject )
{
// nothing to do
return;
}
int active = g_nActiveVProject-1;
int next = (active + g_numProjects + 1) % g_numProjects;
project_t tempProject;
tempProject = g_projects[next];
g_projects[next] = g_projects[active];
g_projects[active] = tempProject;
g_nActiveVProject = next + 1;
}
//-----------------------------------------------------------------------------
// ModifyDlg_Proc
//
//-----------------------------------------------------------------------------
BOOL CALLBACK ModifyDlg_Proc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
size_t len;
int width;
int height;
RECT rect;
switch ( message )
{
case WM_INITDIALOG:
SetDlgItemText( hWnd, IDC_MODIFY_PROJECT, g_project_name );
SetDlgItemText( hWnd, IDC_MODIFY_GAMEDIR, g_project_gamedir );
// center dialog
GetWindowRect( hWnd, &rect );
width = GetSystemMetrics( SM_CXSCREEN );
height = GetSystemMetrics( SM_CYSCREEN );
SetWindowPos( hWnd, NULL, ( width - ( rect.right - rect.left ) )/2, ( height - ( rect.bottom - rect.top ) )/2, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
return ( TRUE );
case WM_COMMAND:
switch ( LOWORD( wParam ) )
{
case IDC_OK:
GetDlgItemText( hWnd, IDC_MODIFY_PROJECT, g_project_name, sizeof( g_project_name ) );
GetDlgItemText( hWnd, IDC_MODIFY_GAMEDIR, g_project_gamedir, sizeof( g_project_gamedir ) );
// remove trailing seperator
Sys_NormalizePath( g_project_gamedir, false );
len = strlen( g_project_gamedir );
if ( len > 2 && g_project_gamedir[len-1] == '\\' )
{
g_project_gamedir[len-1] = '\0';
}
// fall through
case IDCANCEL:
case IDC_CANCEL:
EndDialog( hWnd, wParam );
return ( TRUE );
}
break;
}
return ( FALSE );
}
//-----------------------------------------------------------------------------
// ModifyDlg_Open
//
//-----------------------------------------------------------------------------
BOOL ModifyDlg_Open()
{
int result;
result = DialogBox( g_hInstance, MAKEINTRESOURCE( IDD_VPROJECT ), g_hWnd, ( DLGPROC )ModifyDlg_Proc );
if ( LOWORD( result ) != IDC_OK )
{
return FALSE;
}
return TRUE;
}
//-----------------------------------------------------------------------------
// ShowPopupMenu
//
//-----------------------------------------------------------------------------
void ShowPopupMenu( HWND hWnd, bool bUseCachedMenuPos )
{
BOOL bDelete = true;
// delete existing entries
for ( int nIndex = 1; nIndex < ID_TRAY_ADDVPROJECT && bDelete; nIndex++ )
{
bDelete = DeleteMenu( g_hMenu, nIndex, MF_BYCOMMAND );
}
// Insert projects
char szMenuItem[MAX_PATH];
for ( int nIndex = 0; nIndex < g_numProjects; nIndex++ )
{
strcpy( szMenuItem, g_projects[nIndex].pName );
strcat( szMenuItem, "\t" );
strcat( szMenuItem, g_projects[nIndex].pGamedir );
strcat( szMenuItem, " " );
InsertMenu( g_hMenu, nIndex, MF_BYPOSITION | MF_STRING, nIndex + 1, szMenuItem );
}
if ( g_nActiveVProject )
{
CheckMenuItem( g_hMenu, g_nActiveVProject, MF_BYCOMMAND|MF_CHECKED );
SetMenuDefaultItem( g_hMenu, g_nActiveVProject, FALSE );
}
// Display the popup menu at the current cursor location
// Use the cached cursor position if set
if ( !bUseCachedMenuPos || ( !g_cursorPoint.x && !g_cursorPoint.y ) )
{
GetCursorPos( &g_cursorPoint );
}
SetForegroundWindow( hWnd );
TrackPopupMenu( g_hMenu, 0, g_cursorPoint.x, g_cursorPoint.y, 0, hWnd, 0 );
PostMessage( hWnd, WM_NULL, 0, 0 );
}
//-----------------------------------------------------------------------------
// TrayMessageHandler
//
//-----------------------------------------------------------------------------
void TrayMessageHandler( HWND hWnd, UINT uMessageID )
{
switch ( uMessageID )
{
case 0:
break;
case ID_TRAY_EXIT:
DestroyWindow( hWnd );
break;
case ID_TRAY_ADDVPROJECT:
SetForegroundWindow( hWnd );
g_project_name[0] = '\0';
g_project_gamedir[0] = '\0';
if ( ModifyDlg_Open() )
{
AddVProject( g_project_name, g_project_gamedir );
SaveRegistryValues();
}
ShowPopupMenu( hWnd, true );
break;
case ID_TRAY_MODIFYVPROJECT:
SetForegroundWindow( hWnd );
if ( g_nActiveVProject )
{
strcpy( g_project_name, g_projects[g_nActiveVProject-1].pName );
strcpy( g_project_gamedir, g_projects[g_nActiveVProject-1].pGamedir );
if ( ModifyDlg_Open() )
{
ModifyVProject( g_nActiveVProject-1, g_project_name, g_project_gamedir );
SaveRegistryValues();
}
ShowPopupMenu( hWnd, true );
}
break;
case ID_TRAY_MOVEUP:
SetForegroundWindow( hWnd );
if ( g_nActiveVProject )
{
ShiftActiveProjectUp();
SaveRegistryValues();
ShowPopupMenu( hWnd, true );
}
break;
case ID_TRAY_MOVEDOWN:
SetForegroundWindow( hWnd );
if ( g_nActiveVProject )
{
ShiftActiveProjectDown();
SaveRegistryValues();
ShowPopupMenu( hWnd, true );
}
break;
default:
if ( uMessageID >= 1 && uMessageID <= MAX_PROJECTS )
{
// set current vproject
g_nActiveVProject = uMessageID;
SetVProject( g_projects[uMessageID-1].pName );
}
break;
}
}
//-----------------------------------------------------------------------------
// Main_WndProc
//
//-----------------------------------------------------------------------------
LRESULT CALLBACK Main_WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch ( message )
{
case WM_DESTROY:
PostQuitMessage( 0 );
return 0L;
case WM_TRAY:
if ( lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN )
{
ShowPopupMenu( hWnd, false );
return 0L;
}
break;
case WM_COMMAND:
TrayMessageHandler( hWnd, LOWORD( wParam ) );
break;
}
return ( DefWindowProc( hWnd, message, wParam, lParam ) );
}
//-----------------------------------------------------------------------------
// Startup
//
//-----------------------------------------------------------------------------
bool Startup()
{
int i;
// set up our window class
WNDCLASS wndclass;
memset( &wndclass, 0, sizeof( wndclass ) );
wndclass.style = 0;
wndclass.lpfnWndProc = Main_WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = g_hInstance;
wndclass.hIcon = LoadIcon( g_hInstance, (LPCTSTR)IDI_VPROJECT );
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = VPROJECT_CLASSNAME;
if ( !RegisterClass( &wndclass ) )
{
return false;
}
// create the hidden window
g_hWnd = CreateWindow(
VPROJECT_CLASSNAME,
0,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
0,
CW_USEDEFAULT,
0,
NULL,
NULL,
g_hInstance,
NULL );
// Create tray icon
g_iconData.cbSize = sizeof( NOTIFYICONDATA );
g_iconData.hIcon = LoadIcon( g_hInstance, (LPCTSTR)IDI_VPROJECT );
g_iconData.hWnd = g_hWnd;
g_iconData.uCallbackMessage = WM_TRAY;
g_iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
g_iconData.uID = ID_TRAY;
strcpy(g_iconData.szTip, "VPROJECT");
Shell_NotifyIcon( NIM_ADD, &g_iconData );
// Create popup menu and add initial items
g_hMenu = CreatePopupMenu();
AppendMenu( g_hMenu, MF_SEPARATOR, 0, 0);
AppendMenu( g_hMenu, MF_STRING, ID_TRAY_ADDVPROJECT, "Add VProject" );
AppendMenu( g_hMenu, MF_STRING, ID_TRAY_MODIFYVPROJECT, "Modify VProject" );
AppendMenu( g_hMenu, MF_STRING, ID_TRAY_MOVEUP, "Move Up" );
AppendMenu( g_hMenu, MF_STRING, ID_TRAY_MOVEDOWN, "Move Down" );
AppendMenu( g_hMenu, MF_SEPARATOR, 0, 0);
AppendMenu( g_hMenu, MF_STRING, ID_TRAY_EXIT, "Remove From Tray" );
// find the current vproject
char* vproject = getenv( "vproject" );
if ( vproject && vproject[0] )
{
char temp[MAX_PATH];
strcpy( temp, vproject );
Sys_NormalizePath( temp, false );
for ( i=0; i<g_numProjects; i++ )
{
if ( !stricmp( g_projects[i].pGamedir, temp ) )
{
// found
g_nActiveVProject = i+1;
break;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Shutdown
//
// Free all resources
//-----------------------------------------------------------------------------
void Shutdown()
{
}
//-----------------------------------------------------------------------------
// WinMain
//
// Entry point for program
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow )
{
bool error = true;
MSG msg = {0};
g_hInstance = hInstance;
// get the project list
LoadRegistryValues();
if ( pCmdLine && pCmdLine[0] )
{
// set directly
SetVProject( pCmdLine );
return 0;
}
HWND hwnd = FindWindow( VPROJECT_CLASSNAME, NULL );
if ( hwnd )
{
// single instance only
return ( FALSE );
}
if ( !Startup() )
{
goto cleanUp;
}
// message pump
while ( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// no-error, end of app
error = false;
cleanUp:
if ( error )
{
char str[255];
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, str, 255, NULL );
MessageBox( NULL, str, NULL, MB_OK );
}
Shutdown();
return ( (int)msg.wParam );
}