mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-12 19:06:09 -10:00
Any DDV (Dialog Data Validation) routine must appear immediately after the field it validates in order for the enforcement to work in real-time. If the DDV_ routine is not linked to the field, it will perform validation after-the-fact by displaying a dialog box and returning an error. This is an alternate way of handling validation, but in order for the dialog box not to generate an endless loop, it would be necessary to check the return value of every `UpdateData(TRUE)` call in the FRED codebase (or at least the first `UpdateData(TRUE)` in a pair of them) and then, if the return value is `FALSE`, doing an early return from the enclosing function. Fixes #6427 as well as all similar cases in FRED.
333 lines
9.2 KiB
C++
333 lines
9.2 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "FRED.h"
|
|
#include "AddVariableDlg.h"
|
|
#include <parse/sexp.h>
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define NO_RESET_FOCUS 0
|
|
#define RESET_FOCUS 1
|
|
|
|
BEGIN_MESSAGE_MAP(CAddVariableDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CAddVariableDlg)
|
|
ON_BN_CLICKED(IDC_TYPE_NUMBER, OnTypeNumber)
|
|
ON_BN_CLICKED(IDC_TYPE_STRING, OnTypeString)
|
|
ON_BN_CLICKED(IDC_TYPE_CAMPAIGN_PERSISTENT, OnTypeMissionProgress)
|
|
ON_BN_CLICKED(IDC_TYPE_PLAYER_PERSISTENT, OnTypeMissionClose)
|
|
ON_BN_CLICKED(IDC_TYPE_NETWORK_VARIABLE, OnTypeNetworkVariable)
|
|
ON_BN_CLICKED(IDC_TYPE_ETERNAL, OnTypeEternal)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
CAddVariableDlg::CAddVariableDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CAddVariableDlg::IDD, pParent) {
|
|
//{{AFX_DATA_INIT(CAddVariableDlg)
|
|
m_default_value = _T("");
|
|
m_variable_name = _T("");
|
|
//}}AFX_DATA_INIT
|
|
}
|
|
|
|
void CAddVariableDlg::DoDataExchange(CDataExchange* pDX) {
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CAddVariableDlg)
|
|
DDX_Text(pDX, IDC_ADD_VARIABLE_DEFAULT_VALUE, m_default_value);
|
|
DDV_MaxChars(pDX, m_default_value, TOKEN_LENGTH - 1);
|
|
DDX_Text(pDX, IDC_ADD_VARIABLE_NAME, m_variable_name);
|
|
DDV_MaxChars(pDX, m_variable_name, TOKEN_LENGTH - 1);
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BOOL CAddVariableDlg::OnInitDialog() {
|
|
CDialog::OnInitDialog();
|
|
|
|
// TODO: Add extra initialization here
|
|
m_variable_name = "<Variable Name>";
|
|
m_default_value = "<Default Value>";
|
|
|
|
// Set variable type to number
|
|
m_type_number = true;
|
|
m_type_on_mission_progress = false;
|
|
m_type_on_mission_close = false;
|
|
m_type_eternal = false;
|
|
m_type_network_variable = false;
|
|
set_variable_type();
|
|
|
|
m_name_validated = false;
|
|
m_data_validated = false;
|
|
m_create = false;
|
|
|
|
//create tool tip controls
|
|
m_ProgressToolTip = new CToolTipCtrl();
|
|
m_ProgressToolTip->Create(this);
|
|
m_CloseToolTip = new CToolTipCtrl();
|
|
m_CloseToolTip->Create(this);
|
|
m_EternalToolTip = new CToolTipCtrl();
|
|
m_EternalToolTip->Create(this);
|
|
|
|
CWnd* pWnd = GetDlgItem(IDC_TYPE_CAMPAIGN_PERSISTENT);
|
|
m_ProgressToolTip->AddTool(pWnd, "This type of variable will save when the player clicks Accept to go to the next mission");
|
|
m_ProgressToolTip->Activate(TRUE);
|
|
|
|
pWnd = GetDlgItem(IDC_TYPE_PLAYER_PERSISTENT);
|
|
m_CloseToolTip->AddTool(pWnd, "This type of variable will save when the player leaves the mission");
|
|
m_CloseToolTip->Activate(TRUE);
|
|
|
|
pWnd = GetDlgItem(IDC_TYPE_ETERNAL);
|
|
m_EternalToolTip->AddTool(pWnd, "This type of variable is saved to the player file. So it can be referred to by other campaigns");
|
|
m_EternalToolTip->Activate(TRUE);
|
|
|
|
// Send default name and values into dialog box
|
|
UpdateData(FALSE);
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
// EXCEPTION: OCX Property Pages should return FALSE
|
|
}
|
|
|
|
BOOL CAddVariableDlg::PreTranslateMessage(MSG* pMsg) {
|
|
|
|
m_ProgressToolTip->RelayEvent(pMsg);
|
|
m_CloseToolTip->RelayEvent(pMsg);
|
|
m_EternalToolTip->RelayEvent(pMsg);
|
|
|
|
return CDialog::PreTranslateMessage(pMsg);
|
|
}
|
|
|
|
void CAddVariableDlg::OnOK() {
|
|
// validation name
|
|
validate_variable_name(RESET_FOCUS);
|
|
|
|
// validate data
|
|
if (m_name_validated) {
|
|
validate_data(RESET_FOCUS);
|
|
}
|
|
|
|
// both ok, then store results
|
|
if (m_name_validated && m_data_validated) {
|
|
// int sexp_add_variable(char *text, char*, int type);
|
|
// char temp_name[32];
|
|
// char temp_value[32];
|
|
// strcpy_s(temp_name, m_variable_name);
|
|
// strcpy_s(temp_value, m_default_value);
|
|
// SEXP_VARIABLE_NUMBER SEXP_VARIABLE_STRING
|
|
// int type;
|
|
//
|
|
// if (m_type_number) {
|
|
// type = SEXP_VARIABLE_NUMBER;
|
|
// } else {
|
|
// type = SEXP_VARIABLE_STRING;
|
|
// }
|
|
//
|
|
// // Goober5000
|
|
// if (m_type_player_persistent) {
|
|
// type |= SEXP_VARIABLE_PLAYER_PERSISTENT;
|
|
// } else if (m_type_campaign_persistent) {
|
|
// type |= SEXP_VARIABLE_CAMPAIGN_PERSISTENT;
|
|
// }
|
|
//
|
|
// m_sexp_var_index = sexp_add_variable(temp_value, temp_name, type);
|
|
// this get done for free CDialog::OnOK() UpdateData(TRUE);
|
|
m_create = true;
|
|
|
|
CDialog::OnOK();
|
|
}
|
|
}
|
|
|
|
void CAddVariableDlg::OnTypeMissionProgress() {
|
|
m_type_on_mission_progress = ((CButton *) GetDlgItem(IDC_TYPE_CAMPAIGN_PERSISTENT))->GetCheck() ? true : false;
|
|
|
|
if (m_type_on_mission_progress)
|
|
m_type_on_mission_close = false;
|
|
|
|
// if the variable isn't persistent, it can't be eternal
|
|
if (!m_type_on_mission_progress && !m_type_on_mission_close)
|
|
m_type_eternal = false;
|
|
|
|
set_variable_type();
|
|
}
|
|
|
|
void CAddVariableDlg::OnTypeNetworkVariable() {
|
|
m_type_network_variable = ((CButton *) GetDlgItem(IDC_TYPE_NETWORK_VARIABLE))->GetCheck() ? true : false;
|
|
set_variable_type();
|
|
}
|
|
|
|
void CAddVariableDlg::OnTypeNumber() {
|
|
m_type_number = true;
|
|
set_variable_type();
|
|
}
|
|
|
|
void CAddVariableDlg::OnTypeMissionClose() {
|
|
m_type_on_mission_close = ((CButton *) GetDlgItem(IDC_TYPE_PLAYER_PERSISTENT))->GetCheck() ? true : false;
|
|
|
|
if (m_type_on_mission_close)
|
|
m_type_on_mission_progress = false;
|
|
|
|
// if the variable isn't persistent, it can't be eternal
|
|
if (!m_type_on_mission_progress && !m_type_on_mission_close)
|
|
m_type_eternal = false;
|
|
|
|
// if the variable isn't persistent, it can't be eternal
|
|
if (!m_type_on_mission_close && !m_type_on_mission_progress) {
|
|
m_type_eternal = false;
|
|
}
|
|
|
|
set_variable_type();
|
|
}
|
|
|
|
void CAddVariableDlg::OnTypeEternal() {
|
|
m_type_eternal = ((CButton *)GetDlgItem(IDC_TYPE_ETERNAL))->GetCheck() ? true : false;
|
|
|
|
|
|
// if the variable isn't persistent, it can't be eternal
|
|
if (!m_type_on_mission_close && !m_type_on_mission_progress) {
|
|
m_type_eternal = false;
|
|
MessageBox("Eternal variables must have a different persistence type set first!");
|
|
}
|
|
|
|
set_variable_type();
|
|
}
|
|
|
|
void CAddVariableDlg::OnTypeString() {
|
|
m_type_number = false;
|
|
set_variable_type();
|
|
}
|
|
|
|
void CAddVariableDlg::set_variable_type() {
|
|
|
|
CButton *button_string = (CButton *) GetDlgItem(IDC_TYPE_STRING);
|
|
CButton *button_number = (CButton *) GetDlgItem(IDC_TYPE_NUMBER);
|
|
|
|
button_number->SetCheck(m_type_number);
|
|
button_string->SetCheck(!m_type_number);
|
|
|
|
((CButton *) GetDlgItem(IDC_TYPE_CAMPAIGN_PERSISTENT))->SetCheck(m_type_on_mission_progress);
|
|
((CButton *) GetDlgItem(IDC_TYPE_PLAYER_PERSISTENT))->SetCheck(m_type_on_mission_close);
|
|
((CButton *) GetDlgItem(IDC_TYPE_NETWORK_VARIABLE))->SetCheck(m_type_network_variable);
|
|
((CButton *) GetDlgItem(IDC_TYPE_ETERNAL))->SetCheck(m_type_eternal);
|
|
}
|
|
|
|
void CAddVariableDlg::validate_data(int set_focus) {
|
|
CString temp_data;
|
|
|
|
CEdit *edit = (CEdit *) GetDlgItem(IDC_ADD_VARIABLE_DEFAULT_VALUE);
|
|
edit->GetWindowText(temp_data);
|
|
|
|
// check for 0 string length
|
|
if (strlen(temp_data) == 0) { //-V805
|
|
m_data_validated = false;
|
|
} else {
|
|
if (m_type_number) {
|
|
// verify valid number
|
|
int temp_num = atoi(temp_data);
|
|
char buf[TOKEN_LENGTH];
|
|
sprintf(buf, "%d", temp_num);
|
|
|
|
if (stricmp(buf, temp_data)) {
|
|
m_data_validated = false;
|
|
} else {
|
|
m_data_validated = true;
|
|
}
|
|
} else {
|
|
m_data_validated = true;
|
|
}
|
|
}
|
|
|
|
// Display message and reset focus
|
|
if ((!m_data_validated) && (set_focus == RESET_FOCUS)) {
|
|
MessageBox("Invalid Default Value.");
|
|
edit->SetFocus();
|
|
edit->SetSel(0, -1);
|
|
}
|
|
}
|
|
|
|
void CAddVariableDlg::validate_variable_name(int set_focus) {
|
|
CString temp_name;
|
|
|
|
CEdit *edit = (CEdit *) GetDlgItem(IDC_ADD_VARIABLE_NAME);
|
|
edit->GetWindowText(temp_name);
|
|
|
|
// Check if any change and not already in list
|
|
if (stricmp(temp_name, "<Variable Name>")) {
|
|
size_t len = strlen(temp_name);
|
|
|
|
if ((len > 0) && (get_index_sexp_variable_name(LPCTSTR(temp_name)) == -1)) { //not already in list and length > 0 { //-V805
|
|
// Goober5000 - replace spaces with hyphens
|
|
if (strchr(temp_name, ' ') != NULL) {
|
|
// display msg
|
|
MessageBox("Variable names cannot contain spaces. Replacing with hyphens.");
|
|
|
|
// replace chars
|
|
//Modified to make the code build. - Thunderbird
|
|
temp_name.Replace((TCHAR) ' ', (TCHAR) '-');
|
|
//temp_name.Replace((TCHAR) ' ', (TCHAR) '-'));
|
|
|
|
// fix what's displayed
|
|
edit->SetFocus();
|
|
edit->SetWindowText(temp_name);
|
|
edit->SetSel(0, -1);
|
|
}
|
|
|
|
// check for invalid characters
|
|
auto rval = strcspn(temp_name, "@()");
|
|
if (rval != len) {
|
|
char message_text[256];
|
|
sprintf(message_text, "Invalid char '%c' in variable name", temp_name[(int)rval]);
|
|
MessageBox(message_text);
|
|
|
|
m_name_validated = false;
|
|
}
|
|
else {
|
|
m_name_validated = true;
|
|
}
|
|
} else {
|
|
// conflicting variable name
|
|
if (len == 0) {
|
|
edit->SetWindowText("<Variable Name>");
|
|
}
|
|
m_name_validated = false;
|
|
if (set_focus == RESET_FOCUS) {
|
|
if (len == 0) {
|
|
MessageBox("Variables must have a unique name.");
|
|
} else {
|
|
MessageBox("Another variable has this name already. Please choose another.");
|
|
}
|
|
edit->SetFocus();
|
|
edit->SetSel(0, -1);
|
|
}
|
|
}
|
|
} else {
|
|
// name unchanged from default
|
|
m_name_validated = false;
|
|
if (set_focus == RESET_FOCUS) {
|
|
MessageBox("Invalid variable name");
|
|
edit->SetFocus();
|
|
edit->SetSel(0, -1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool is_sexp_variable_name(const char* temp_name) {
|
|
for (int i = 0; i < MAX_SEXP_VARIABLES; i++) {
|
|
if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
|
|
if (!strcmp(Sexp_variables[i].text, temp_name)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// name not found
|
|
return true;
|
|
}
|