#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <shlobj.h> // For FileOpenDialog
#include <commctrl.h>
#include "Commondefs.h"
#include "Configvars.h"
#include "Guicommon.h"
#include "WinMain.h"
#include "Loadrun.h"
#include "resource.h"
// Status bar vars.
static char StatusText[256];
static int StatusParts[3];
static HWND StatusWnd;
static GUIStatusMetrics StatusMetrics;
static bool MetricsEnabled;
static HICON ICOMetrics[5];
static char *ICOMetricsFilename[5] = {
"cpu_load.ico", "gfx_load.ico", "sfx_load.ico", "dvd_load.ico", "total_load.ico" };
#define ICO_METRICS_W 16
#define ICO_METRICS_H 16
#define PART_TEXT 0 // Status window parts.
#define PART_METRICS 1
#define PART_TOTAL 2
// Toolbar vars.
#define TOOLBAR_IMAGESIZE 24 // Buttons images are 24 x 24
#define TOOLBAR_NUMIMAGES 4
#define TOOLBAR_NUMBUTTONS 4+2
#define TOOLBAR_ENABLED "toolbar_en.bmp"
#define TOOLBAR_DISABLED "toolbar_dis.bmp"
static TBBUTTON ToolbarButtons[TOOLBAR_NUMBUTTONS];
// These strings are in the same order as the images
static char *ToolbarTooltips[TOOLBAR_NUMIMAGES] = {
"Turn Power On/Off",
"Reset",
"Swap Disk",
"",
};
static HWND ToolbarWnd, ToolbarTips;
#define BOUND(x, min, max) \
if(x < min) x = min; \
if(x > max) x = max; \
// For File Open.
static LPCTSTR ofdAll = NULL, ofdDVD = NULL, ofdMap = NULL, ofdPatch = NULL, ofdBin = NULL, ofdThp = NULL;
/* --------------------------------------------------------------------- */
/*
* Popup error/report messages.
*/
void GUIReport(char *message, ...)
{
va_list arg;
char buf[0x1000];
va_start(arg, message);
vsprintf(buf, message, arg);
va_end(arg);
MessageBox(NULL, buf, APPNAME " Report", MB_ICONINFORMATION | MB_OK | MB_TOPMOST);
}
void GUIHalt(char *message, ...)
{
va_list arg;
char buf[0x1000];
va_start(arg, message);
vsprintf(buf, message, arg);
va_end(arg);
MessageBox(NULL, buf, APPNAME " Critical Error", MB_ICONEXCLAMATION | MB_OK | MB_TOPMOST);
// Stop emulaton.
Unload();
//if(g_hwnd) SendMessage(g_hwnd, WM_CLOSE, 0, 0);
}
/*
* Set current icon.
*/
void GUILoadIcon(char *filename)
{
static HICON hIcon = NULL;
if(hIcon)
{
DestroyIcon(hIcon);
hIcon = NULL;
}
hIcon = (HICON)LoadImage(g_hinst, filename, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE | LR_SHARED);
if(hIcon == NULL) { 1; } //GUIReport("GUI: cannot load application icon from [%s]\n", filename);
else
{
DestroyIcon((HICON)GetClassLong(g_hwnd, GCL_HICON));
SetClassLong(g_hwnd, GCL_HICON, (LONG)hIcon);
UpdateWindow(g_hwnd);
}
}
/*
* Status bar.
*/
// Create/destroy status bar. Stable for 800 subsequent enable/disable calls.
void GUIEnableStatusBar(bool enable)
{
// Enable.
if(enable && !IsWindow(StatusWnd))
{
memset(StatusText, 0, sizeof(StatusText));
StatusParts[PART_TEXT] = 640 - (ICO_METRICS_W + 2) * 6;
StatusParts[PART_METRICS] = StatusParts[PART_TEXT] + (ICO_METRICS_W + 2) * 4 + 6;
StatusParts[PART_TOTAL] = -1;
StatusWnd = CreateStatusWindow(WS_CHILD | WS_VISIBLE, NULL, g_hwnd, ID_STATUS_BAR);
if(StatusWnd == NULL) return;
SendMessage( StatusWnd, SB_SETPARTS, 3, (LPARAM)StatusParts );
GUISetStatusText("Idle");
memset(&StatusMetrics, 0, sizeof(StatusMetrics));
// Re-load metrics icons.
for(int i=0; i<5; i++)
{
if(ICOMetrics[i] == NULL)
{
ICOMetrics[i] = (HICON)LoadImage(g_hinst, ICOMetricsFilename[i], IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE | LR_SHARED);
if(ICOMetrics[i] == NULL) ICOMetrics[i] = LoadIcon(NULL, IDI_EXCLAMATION);
}
}
// Set owner draw to metrics and common application load.
SendMessage( StatusWnd, SB_SETTEXT, PART_METRICS | SBT_OWNERDRAW, (LPARAM)NULL );
SendMessage( StatusWnd, SB_SETTEXT, PART_TOTAL | SBT_OWNERDRAW, (LPARAM)NULL );
}
// Disable.
if(!enable && IsWindow(StatusWnd))
{
// Unload metrics icons.
for(int i=0; i<5; i++)
{
if(ICOMetrics[i])
{
DeleteObject((HGDIOBJ)ICOMetrics[i]);
ICOMetrics[i] = NULL;
}
}
DestroyWindow(StatusWnd);
StatusWnd = NULL;
}
}
void GUISetStatusText(char *text, bool post)
{
if(IsWindow(StatusWnd) == FALSE) return;
strncpy(StatusText, text, sizeof(StatusText) - 2);
if(post) PostMessage(StatusWnd, SB_SETTEXT, (WPARAM)(PART_TEXT), (LPARAM)StatusText);
else SendMessage(StatusWnd, SB_SETTEXT, (WPARAM)(PART_TEXT), (LPARAM)StatusText);
}
char *GUIGetStatusText(void)
{
return StatusText;
}
void GUISetStatusMetrics(GUIStatusMetrics *stat)
{
// Clamp metrics.
BOUND(stat->cpu, 0, 100);
BOUND(stat->gfx, 0, 100);
BOUND(stat->sfx, 0, 100);
BOUND(stat->dvd, 0, 100);
StatusMetrics = *stat;
}
void GUIGetStatusMetrics(GUIStatusMetrics *stat)
{
*stat = StatusMetrics;
}
// Draw our icons, then highlight them by current "load" (0-100%).
// Icons are drawed at the middle of th status line.
bool GUIStatusRedraw(void *ptr)
{
if(IsWindow(StatusWnd) == FALSE) return false; // Owner draw failed!!
if(MetricsEnabled == true)
{
DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)ptr;
int part = dis->itemID;
HDC dc = dis->hDC;
RECT rc = dis->rcItem;
int oy = (rc.bottom - rc.top) / 2 - ICO_METRICS_H / 2;
if(part == PART_METRICS)
{
for(int i=0; i<4; i++)
{
if(ICOMetrics[i])
{
DrawIconEx(dc, rc.left + ICO_METRICS_W * i, rc.top + oy, ICOMetrics[i], ICO_METRICS_W, ICO_METRICS_H, 0, NULL, DI_NORMAL);
}
rc.left += 2;
}
}
if(part == PART_TOTAL)
{
if(ICOMetrics[4])
{
DrawIconEx(dc, rc.left, rc.top + oy, ICOMetrics[4], ICO_METRICS_W, ICO_METRICS_H, 0, NULL, DI_NORMAL);
}
}
}
return true; // Owner draw done.
}
void GUIEnableStatusBarMetrics(bool enable)
{
MetricsEnabled = enable;
if(IsWindow(StatusWnd))
{
RedrawWindow(StatusWnd, NULL, NULL, RDW_INVALIDATE);
UpdateWindow(StatusWnd);
}
}
/*
* Toolbar: [POWER] [RESET] [DVD COVER v] | [SETTINGS]
*/
static void CreateToolbar(void)
{
if(IsWindow(ToolbarWnd)) return;
ToolbarButtons[0].idCommand = 0;
ToolbarButtons[0].iBitmap = 0;
ToolbarButtons[0].iString = 0;
ToolbarButtons[0].fsState = TBSTATE_ENABLED;
ToolbarButtons[0].fsStyle = TBSTYLE_SEP;
ToolbarButtons[0].dwData = 0;
ToolbarButtons[1].idCommand = ID_POWER_BUTTON;
ToolbarButtons[1].iBitmap = 0;
ToolbarButtons[1].iString = 1;
ToolbarButtons[1].fsState = TBSTATE_ENABLED;
ToolbarButtons[1].fsStyle = TBSTYLE_AUTOSIZE | TBSTYLE_BUTTON;
ToolbarButtons[1].dwData = 0;
ToolbarButtons[2].idCommand = ID_RESET_BUTTON;
ToolbarButtons[2].iBitmap = 1;
ToolbarButtons[2].iString = 1;
ToolbarButtons[2].fsState = TBSTATE_ENABLED;
ToolbarButtons[2].fsStyle = TBSTYLE_AUTOSIZE | TBSTYLE_BUTTON;
ToolbarButtons[2].dwData = 0;
ToolbarButtons[3].idCommand = ID_DVD_SWITCH;
ToolbarButtons[3].iBitmap = 2;
ToolbarButtons[3].iString = 1;
ToolbarButtons[3].fsState = TBSTATE_ENABLED;
ToolbarButtons[3].fsStyle = TBSTYLE_AUTOSIZE | TBSTYLE_BUTTON | /*BTNS_WHOLEDROPDOWN*/0x80;
ToolbarButtons[3].dwData = 0;
ToolbarButtons[4].idCommand = 0;
ToolbarButtons[4].iBitmap = 0;
ToolbarButtons[4].iString = 0;
ToolbarButtons[4].fsState = TBSTATE_ENABLED;
ToolbarButtons[4].fsStyle = TBSTYLE_SEP;
ToolbarButtons[4].dwData = 0;
ToolbarButtons[5].idCommand = ID_SETTINGS;
ToolbarButtons[5].iBitmap = 3;
ToolbarButtons[5].iString = 2;
ToolbarButtons[5].fsState = TBSTATE_ENABLED;
ToolbarButtons[5].fsStyle = TBSTYLE_BUTTON | /*BTNS_SHOWTEXT*/0x0040;
ToolbarButtons[5].dwData = 0;
ToolbarWnd = CreateWindowEx( /*TBSTYLE_EX_MIXEDBUTTONS*/8, TOOLBARCLASSNAME, (LPSTR)NULL,
WS_CHILD | TBSTYLE_LIST /*| CCS_ADJUSTABLE*/ | TBSTYLE_TOOLTIPS,
0, 0, 0, 0, g_hwnd, (HMENU)ID_TOOL_BAR, g_hinst, NULL);
if(IsWindow(ToolbarWnd) == FALSE) return;
SendMessage(ToolbarWnd, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(ToolbarWnd, TB_ADDSTRING, 0, (LPARAM)(LPSTR)" \0 \0Settings..\0\0");
SendMessage(ToolbarWnd, TB_ADDBUTTONS, (WPARAM)TOOLBAR_NUMBUTTONS, (LPARAM)(LPTBBUTTON)ToolbarButtons);
ShowWindow(ToolbarWnd, SW_SHOW);
UpdateWindow(ToolbarWnd);
#if defined(TB_SETIMAGELIST) && defined(TB_SETDISABLEDIMAGELIST)
HIMAGELIST imlEnabled = ImageList_Create(
TOOLBAR_IMAGESIZE, TOOLBAR_IMAGESIZE,
ILC_COLOR24 | ILC_MASK, TOOLBAR_NUMIMAGES, 0);
HIMAGELIST imlDisabled = ImageList_Create(
TOOLBAR_IMAGESIZE, TOOLBAR_IMAGESIZE,
ILC_COLOR24 | ILC_MASK, TOOLBAR_NUMIMAGES, 0);
if(imlEnabled && imlDisabled)
{
HBITMAP hbmEnabled = (HBITMAP)LoadImage(g_hinst, TOOLBAR_ENABLED, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE | LR_SHARED);
HBITMAP hbmDisabled = (HBITMAP)LoadImage(g_hinst, TOOLBAR_DISABLED, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE | LR_SHARED);
if(hbmEnabled && hbmDisabled)
{
ImageList_AddMasked(imlEnabled, hbmEnabled, RGB(0, 255, 0));
ImageList_AddMasked(imlDisabled, hbmDisabled, RGB(0, 255, 0));
SendMessage(ToolbarWnd, TB_SETIMAGELIST, 0, (LPARAM)imlEnabled);
SendMessage(ToolbarWnd, TB_SETDISABLEDIMAGELIST, 0, (LPARAM)imlDisabled);
DeleteObject(hbmEnabled);
DeleteObject(hbmDisabled);
}
}
#endif // TB_SETHOTIMAGELIST && TB_SETDISABLEDIMAGELIST
SendMessage(ToolbarWnd, TB_SETBUTTONSIZE, 0, (LPARAM)MAKELONG(100, TOOLBAR_IMAGESIZE));
SendMessage(ToolbarWnd, TB_AUTOSIZE, 0, 0);
}
static void DestroyToolbar(void)
{
if(!IsWindow(ToolbarWnd)) return;
DestroyWindow(ToolbarWnd);
ToolbarWnd = NULL;
}
void GUIEnableToolBar(bool enable)
{
if(enable) CreateToolbar();
else DestroyToolbar();
}
/*
* File Open Dialog.
*/
// File filters are loaded from GUI_PATH_FILTER
#define GUI_PATH_FILTER "path_filter.txt"
bool GUIOpenFileDialog(HWND parent, int file_type, char *path, int path_size, char *initial_dir)
{
OPENFILENAME ofn;
char szFileName[1024];
char szFileTitle[1024];
char lastDir[1024], prevDir[1024];
bool result;
if(initial_dir == NULL) initial_dir = ".\\";
GetCurrentDirectory(sizeof(prevDir), prevDir);
strncpy(lastDir, initial_dir, sizeof(lastDir));
memset(szFileName, 0, sizeof(szFileName));
memset(szFileTitle, 0, sizeof(szFileTitle));
if(file_type == FILE_TYPE_DIR)
{
// Dumbiest Windows code. Hell.. I just needed "char * BrowseDirectory(hwnd)"..
BROWSEINFO bi;
LPSTR lpBuffer=NULL;
LPITEMIDLIST pidlRoot=NULL; // PIDL for root folder
LPITEMIDLIST pidlBrowse=NULL; // PIDL selected by user
LPMALLOC g_pMalloc;
// Get the shell's allocator.
if(!SUCCEEDED(SHGetMalloc(&g_pMalloc)))
return false;
// Allocate a buffer to receive browse information.
lpBuffer = (LPSTR)g_pMalloc->Alloc(MAX_PATH);
if(lpBuffer == NULL) return false;
// Get the PIDL for the root folder.
if( !SUCCEEDED(SHGetSpecialFolderLocation(parent, CSIDL_DRIVES, &pidlRoot)) )
{
g_pMalloc->Free(lpBuffer);
return false;
}
// Fill in the BROWSEINFO structure.
bi.hwndOwner = parent;
bi.pidlRoot = pidlRoot;
bi.pszDisplayName = lpBuffer;
bi.lpszTitle = "Choose Directory";
bi.ulFlags = 0;
bi.lpfn = NULL;
bi.lParam = 0;
// Browse for a folder and return its PIDL.
pidlBrowse = SHBrowseForFolder(&bi);
if(result = (pidlBrowse != NULL))
{
SHGetPathFromIDList(pidlBrowse, lpBuffer);
strcpy(szFileName, lpBuffer);
// Free the PIDL returned by SHBrowseForFolder.
g_pMalloc->Free(pidlBrowse);
}
// Clean up.
if(pidlRoot) g_pMalloc->Free(pidlRoot);
if(lpBuffer) g_pMalloc->Free(lpBuffer);
// Release the shell's allocator.
g_pMalloc->Release();
}
else
{
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = parent;
switch(file_type)
{
case FILE_TYPE_ALL:
if(ofdAll) ofn.lpstrFilter = ofdAll;
else ofn.lpstrFilter =
"All supported files (*.gcm, *.gmp, *.thp, *.mth)\0*.gcm;*.gmp;*.thp;*.mth\0"
"Gamecube DVD image files (*.gcm)\0*.gcm\0"
"Compressed DVD image files (*.gmp)\0*.gmp\0"
"Gamecube video files (*.thp, *.mth)\0*.thp;*.mth\0"
"All files (*.*)\0*.*\0";
break;
case FILE_TYPE_DVD:
if(ofdDVD) ofn.lpstrFilter = ofdDVD;
else ofn.lpstrFilter =
"All disk image files (*.gcm, *.gmp)\0*.gcm;*.gmp\0"
"Gamecube DVD image files (*.gcm)\0*.gcm\0"
"Compressed DVD image files (*.gmp)\0*.gmp\0"
"All files (*.*)\0*.*\0";
break;
case FILE_TYPE_MAP:
if(ofdMap) ofn.lpstrFilter = ofdMap;
else ofn.lpstrFilter =
"Symbolic information files (*.map)\0*.map\0"
"All files (*.*)\0*.*\0";
break;
case FILE_TYPE_PATCH:
if(ofdPatch) ofn.lpstrFilter = ofdPatch;
else ofn.lpstrFilter =
"Patch files (*.patch)\0*.patch\0"
"All files (*.*)\0*.*\0";
break;
case FILE_TYPE_BIN:
if(ofdBin) ofn.lpstrFilter = ofdBin;
else ofn.lpstrFilter =
"Binary files (*.bin)\0*.bin\0"
"All files (*.*)\0*.*\0";
break;
case FILE_TYPE_THP:
if(ofdThp) ofn.lpstrFilter = ofdThp;
else ofn.lpstrFilter =
"Gamecube video files (*.thp, *.mth)\0*.thp;*.mth\0"
"All files (*.*)\0*.*\0";
break;
}
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szFileName;
ofn.nMaxFile = sizeof(szFileName);
ofn.lpstrInitialDir = lastDir;
ofn.lpstrFileTitle = szFileTitle;
ofn.nMaxFileTitle = sizeof(szFileTitle);
ofn.lpstrTitle = "Open File\0";
ofn.lpstrDefExt = "";
ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
result = GetOpenFileName(&ofn) == TRUE ? true : false;
}
if(result)
{
strncpy(path, szFileName, path_size);
// save last directory
strcpy(lastDir, path);
int i = (int)strlen(lastDir) - 1;
while(lastDir[i] != '\\') i--;
lastDir[i+1] = '\0';
SetCurrentDirectory(prevDir);
return true;
}
else
{
SetCurrentDirectory(prevDir);
return false;
}
return false;
}
/*
* Client area (where video/gamelist can draw)
*/
#define WIN_STYLE ( WS_OVERLAPPED | WS_SYSMENU )
int GUIGetToolbarHeight(void)
{
RECT rc;
if(IsWindow(ToolbarWnd))
{
GetWindowRect(ToolbarWnd, &rc);
return (rc.bottom - rc.top);
}
else return 0;
}
int GUIGetStatusHeight(void)
{
RECT rc;
if(IsWindow(StatusWnd))
{
GetWindowRect(StatusWnd, &rc);
return (rc.bottom - rc.top);
}
else return 0;
}
void GUIGetClientArea(int *w, int *h)
{
RECT rc;
GetClientRect(g_hwnd, &rc);
*w = rc.right;
*h = rc.bottom - GUIGetToolbarHeight() - GUIGetStatusHeight();
}
void GUISetClientArea(int w, int h)
{
int newx, newy, neww, newh;
RECT rc;
GetWindowRect(g_hwnd, &rc);
newx = rc.left;
newy = rc.top;
neww = w + (GetSystemMetrics(SM_CXFIXEDFRAME) << 1);
newh = GetSystemMetrics(SM_CYSIZE) + GUIGetToolbarHeight() + h + GUIGetStatusHeight() + (GetSystemMetrics(SM_CYFIXEDFRAME) << 1);
// Move window
MoveWindow(g_hwnd, newx, newy, neww, newh, TRUE);
SendMessage(g_hwnd, WM_SIZE, 0, 0);
GUIEnableStatusBar(false);
GUIEnableStatusBar(CVB(StatusBar));
}
// Center the child window into the parent window
void GUICenterChildWindow(HWND hParent, HWND hChild)
{
if(IsWindow(hParent) && IsWindow(hChild))
{
RECT rp, rc;
GetWindowRect(hParent, &rp);
GetWindowRect(hChild, &rc);
MoveWindow(hChild,
rp.left + (rp.right - rp.left - rc.right + rc.left) / 2,
rp.top + (rp.bottom - rp.top - rc.bottom + rc.top) / 2,
rc.right - rc.left, rc.bottom - rc.top, TRUE
);
}
}