/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see .
*
*/
#include "common/textconsole.h"
#include "bagel/mfc/afxwin.h"
#include "bagel/mfc/keyboard.h"
namespace Bagel {
namespace MFC {
IMPLEMENT_DYNAMIC(CDialog, CWnd)
BEGIN_MESSAGE_MAP(CDialog, CWnd)
ON_COMMAND(IDOK, CDialog::OnOK)
ON_COMMAND(IDCANCEL, CDialog::OnCancel)
ON_MESSAGE(WM_INITDIALOG, CDialog::HandleInitDialog)
ON_MESSAGE(WM_SETFONT, CDialog::HandleSetFont)
ON_WM_SYSCHAR()
ON_WM_CLOSE()
ON_WM_ACTIVATE()
END_MESSAGE_MAP()
CDialog::CDialog(const char *lpszTemplateName, CWnd *pParentWnd) {
SetParent(pParentWnd);
m_lpszTemplateName = lpszTemplateName;
if (HIWORD(m_lpszTemplateName) == 0)
m_nIDHelp = LOWORD(m_lpszTemplateName);
}
CDialog::CDialog(unsigned int nIDTemplate, CWnd *pParentWnd) {
SetParent(pParentWnd);
m_lpszTemplateName = MAKEINTRESOURCE(nIDTemplate);
m_nIDHelp = nIDTemplate;
}
bool CDialog::Create(const char *lpszTemplateName,
CWnd *pParentWnd) {
m_lpszTemplateName = lpszTemplateName; // used for help
SetParent(pParentWnd);
if (HIWORD(m_lpszTemplateName) == 0 && m_nIDHelp == 0)
m_nIDHelp = LOWORD(m_lpszTemplateName);
HINSTANCE hInst = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
HRSRC hResource = FindResource(hInst, lpszTemplateName, RT_DIALOG);
HGLOBAL hTemplate = LoadResource(hInst, hResource);
bool bResult = CreateIndirect(hTemplate, m_pParentWnd, hInst);
FreeResource(hTemplate);
return bResult;
}
bool CDialog::Create(unsigned int nIDTemplate, CWnd *pParentWnd) {
return Create(MAKEINTRESOURCE(nIDTemplate), pParentWnd);
}
int CDialog::DoModal() {
// can be constructed with a resource template or InitModalIndirect
assert(m_lpszTemplateName != nullptr || m_hDialogTemplate != nullptr ||
m_lpDialogTemplate != nullptr);
// Save the prior focus, if any
CWnd *oldWin = AfxGetApp()->GetActiveWindow();
CWnd *oldFocus = GetFocus();
if (oldFocus)
AfxGetApp()->SetFocus(nullptr);
// load resource as necessary
LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
HGLOBAL hDialogTemplate = m_hDialogTemplate;
HINSTANCE hInst = AfxGetResourceHandle();
if (m_lpszTemplateName != nullptr) {
hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
HRSRC hResource = FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
assert(hResource);
hDialogTemplate = LoadResource(hInst, hResource);
}
// Set up pointer to template data
if (hDialogTemplate != nullptr)
lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);
// Ensure the template is presenet
if (lpDialogTemplate == nullptr)
return -1;
// Set the dialog parent to be the app window
if (!m_pParentWnd)
m_pParentWnd = AfxGetApp()->m_pMainWnd;
if (CreateDlgIndirect(lpDialogTemplate,
this /*m_pParentWnd*/, hInst)) {
AfxGetApp()->doModal(this);
}
// Finish the dialog
PostModal();
// unlock/free resources as necessary
if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)
UnlockResource(hDialogTemplate);
if (m_lpszTemplateName != NULL)
FreeResource(hDialogTemplate);
// Restore any old focus
if (oldFocus && AfxGetApp()->GetActiveWindow() == oldWin)
oldFocus->SetFocus();
if (_modalResult == DEFAULT_MODAL_RESULT)
_modalResult = -1;
return _modalResult;
}
bool CDialog::CreateDlgIndirect(LPCDLGTEMPLATE lpDialogTemplate,
CWnd *pParentWnd, HINSTANCE hInst) {
assert(lpDialogTemplate != nullptr);
if (hInst == nullptr)
hInst = AfxGetInstanceHandle();
HGLOBAL hTemplate = nullptr;
#if 0
// If no font specified, set the system font.
CString strFace;
uint16 wSize = 0;
bool bSetSysFont = !CDialogTemplate::GetFont(lpDialogTemplate, strFace,
wSize);
if (bSetSysFont) {
CDialogTemplate dlgTemp(lpDialogTemplate);
dlgTemp.SetSystemFont(wSize);
hTemplate = dlgTemp.Detach();
}
#endif
if (hTemplate != nullptr)
lpDialogTemplate = (LPDLGTEMPLATE)GlobalLock(hTemplate);
// setup for modal loop and creation
_modalResult = DEFAULT_MODAL_RESULT;
m_nFlags |= WF_CONTINUEMODAL;
// Create modeless dialog
createDialogIndirect(lpDialogTemplate);
if (hTemplate != nullptr) {
GlobalUnlock(hTemplate);
GlobalFree(hTemplate);
}
return true;
}
void CDialog::SetParent(CWnd *wnd) {
m_pParentWnd = wnd;
}
uint32 CDialog::GetDefID() const {
return _defaultId;
}
void CDialog::SetDefID(unsigned int nID) {
CWnd *oldBtn;
if (_defaultId && (oldBtn = GetDlgItem(_defaultId)) != nullptr) {
uint32 style = oldBtn->GetStyle();
if ((style & 0xf) != BS_OWNERDRAW) {
style &= ~BS_DEFPUSHBUTTON;
oldBtn->SetStyle(style);
}
}
_defaultId = nID;
// Set new default
CWnd *newBtn = GetDlgItem(nID);
if (newBtn) {
uint32 style = newBtn->GetStyle();
if ((style & 0xf) != BS_OWNERDRAW) {
style = (style & ~0xF) | BS_DEFPUSHBUTTON;
newBtn->SetStyle(style);
}
}
}
LRESULT CDialog::HandleInitDialog(WPARAM, LPARAM) {
PreInitDialog();
OnInitDialog();
return 0;
}
LRESULT CDialog::HandleSetFont(WPARAM wParam, LPARAM) {
// Allow the CWnd code to set the _hFont field
CWnd::OnSetFont((HFONT)wParam, true);
// Virtual method dialog descendants can override
OnSetFont(CFont::FromHandle((HFONT)wParam));
return 0;
}
CButton *CDialog::GetDefaultPushButton() const {
for (auto &child : _children) {
CButton *pChild = dynamic_cast(child._value);
if (pChild) {
uint32 style = pChild->GetStyle();
if ((style & BS_DEFPUSHBUTTON) && pChild->GetDlgCtrlID() == IDOK) {
return pChild;
}
}
}
return nullptr;
}
void CDialog::DDX_Control(CDataExchange *pDX, int nIDC, CWnd &rControl) {
rControl.SubclassDlgItem(nIDC, this);
}
void CDialog::DDX_Radio(CDataExchange *pDX,
int nIDCButton1, int &value) {
error("TODO: CDialog::DDX_Radio");
}
void CDialog::DDX_Text(CDataExchange *pDX, int nIDC, int &value) {
error("TODO: CDialog::DDX_Text");
}
void CDialog::DDX_Text(CDataExchange *pDX, int nIDC, CString &value) {
CEdit *edit = dynamic_cast(GetDlgItem(nIDC));
assert(edit);
if (pDX->m_bSaveAndValidate) {
edit->GetWindowText(value);
} else {
edit->SetWindowText(value.c_str());
}
}
void CDialog::DDX_Text(CDataExchange *pDX, int nIDC, unsigned int &value) {
error("TODO: CDialog::DDX_Text");
}
void CDialog::DDX_Text(CDataExchange *pDX, int nIDC, long &value) {
error("TODO: CDialog::DDX_Text");
}
void CDialog::DDX_Text(CDataExchange *pDX, int nIDC, double &value) {
error("TODO: CDialog::DDX_Text");
}
void CDialog::DDX_Check(CDataExchange *pDX,
int nIDC, bool value) {
error("CDialog::DDX_Check");
}
void CDialog::DDV_MinMaxInt(CDataExchange *pDX,
int value, int nMin, int nMax) {
error("TODO: CDialog::DDV_MinMaxInt");
}
void CDialog::EndDialog(int nResult) {
_modalResult = nResult;
}
bool CDialog::UpdateData(bool bSaveAndValidate) {
if (bSaveAndValidate) {
CDataExchange exchange = { true };
DoDataExchange(&exchange);
}
return true;
}
void CDialog::OnOK() {
if (!UpdateData(true))
return;
EndDialog(IDOK);
}
void CDialog::OnCancel() {
EndDialog(IDCANCEL);
}
bool CDialog::CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate,
CWnd *pParentWnd, void *lpDialogInit, HINSTANCE hInst) {
error("TODO: CDialog::CreateIndirect");
}
bool CDialog::CreateIndirect(HGLOBAL hDialogTemplate,
CWnd *pParentWnd, HINSTANCE hInst) {
error("TODO: CDialog::CreateIndirect");
}
void CDialog::OnSysChar(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags) {
// Convert input character to uppercase for case-insensitive match
char pressedChar = toupper(nChar);
for (auto &child : _children) {
CWnd *pChild = child._value;
CString text;
pChild->GetWindowText(text);
// Check for ampersand for hotkey underlining
uint idx = text.findFirstOf('&');
if (idx != Common::String::npos &&
pressedChar == toupper(text[idx + 1])) {
// Found match - simulate button click
pChild->SendMessage(BM_CLICK);
return;
}
}
// If no match found, pass to base class
CWnd::OnSysChar(nChar, nRepCnt, nFlags);
}
void CDialog::OnActivate(unsigned int nState, CWnd *pWndOther, bool bMinimized) {
if (nState != WA_INACTIVE) {
// Invalidate the dialog and its children
Invalidate(true);
for (auto child : _children)
child._value->Invalidate(true);
}
CWnd::OnActivate(nState, pWndOther, bMinimized);
}
bool CDialog::IsDialogMessage(LPMSG lpMsg) {
if (lpMsg->message != WM_KEYDOWN)
return false;
switch (lpMsg->wParam) {
case VK_RETURN:
return handleEnterKey(lpMsg);
case VK_ESCAPE:
return handleEscapeKey(lpMsg);
default:
break;
}
return false;
}
bool CDialog::handleEnterKey(LPMSG lpMsg) {
HWND hFocus = GetFocus();
if (!hFocus)
return false;
// Ask control what it wants
LRESULT dlgCode = MFC::SendMessage(hFocus, WM_GETDLGCODE,
lpMsg->wParam, (LPARAM)lpMsg);
// If control wants Enter, do nothing
if (dlgCode & DLGC_WANTMESSAGE)
return false;
// Multiline edits implicitly want Enter
if (dlgCode & DLGC_HASSETSEL)
return false;
// Find default push button
CButton *pDefault = GetDefaultPushButton();
if (!pDefault)
return false;
if (!pDefault->IsWindowEnabled())
// consume Enter, do nothing
return true;
// Simulate button click
sendButtonClicked(pDefault);
return true;
}
bool CDialog::handleEscapeKey(LPMSG lpMsg) {
CButton *pCancel = dynamic_cast(GetDlgItem(IDCANCEL));
if (!pCancel || !pCancel->IsWindowEnabled())
return true;
sendButtonClicked(pCancel);
return true;
}
void CDialog::sendButtonClicked(CButton *btn) {
int id = btn->GetDlgCtrlID();
SendMessage(WM_COMMAND, MAKEWPARAM(id, BN_CLICKED), (LPARAM)btn->m_hWnd);
}
} // namespace MFC
} // namespace Bagel