/* * Descent 3 * Copyright (C) 2024 Parallax Software * * 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 . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/editor/ScriptStudio.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2003-08-26 03:57:39 $ * $Author: kevinb $ * * Script Studio New Editor * * $Log: not supported by cvs2svn $ * * 17 10/13/98 11:42a Samir * this may fix things? * * 16 10/08/98 4:24p Kevin * Changed code to comply with memory library usage. Always use mem_malloc * , mem_free and mem_strdup * * 15 7/13/98 11:13a Chris * Fixed the 65535 character limit with Kevin's help * * 14 6/23/98 2:43p Matt * Changed calls to OutrageMessageBox() & Debug_MessageBox() to deal with * int return value (instead of bool). * * 13 4/06/98 4:55p Samir * no bold text. * * 12 4/06/98 2:49p Samir * Fixed width font. * * 11 3/24/98 6:35p Samir * added script studio exit * * 10 3/24/98 4:29p Samir * report correct line number. * * 9 3/04/98 5:54p Samir * Added line number gauge to script studio. * * 8 3/02/98 4:03p Samir * Created a function to open up a script studio window. * * 7 10/15/97 3:05p Samir * Improved paste a little bit. * * 6 9/26/97 4:15p Samir * Mostly working system to go to a certain script in a file. * * 5 9/25/97 5:28p Samir * Even newer script code due to more changes in ObjCScript.cpp * * 4 9/04/97 4:39p Matt * Added includes needed as a result of removing includes from d3edit.h * * 3 8/20/97 3:35p Samir * Enhanced ScriptStudio to use tabs! Use new ScriptStudio instead of * ScriptEditor. * * $NoKeywords: $ */ // ScriptStudio.cpp : implementation file // #include "mfc_compatibility.h" #include "editor.h" #include "ScriptStudio.h" #include "OsirisStatusDlg.h" #include "ObjCScript.h" #include "mem.h" #include "mono.h" #include "pserror.h" #include "d3x.h" #include "ddio.h" #include "cfile.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define MAX_SCRIPT_LINE_SIZE 800 static UINT WM_FINDREPLACE = RegisterWindowMessage(FINDMSGSTRING); ///////////////////////////////////////////////////////////////////////////// // CScriptStudio dialog CScriptStudio::CScriptStudio(CWnd *pParent /*=NULL*/) : CDialog(CScriptStudio::IDD, pParent) { //{{AFX_DATA_INIT(CScriptStudio) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_pdlgFindReplace = NULL; } void CScriptStudio::DoDataExchange(CDataExchange *pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CScriptStudio) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } void CScriptStudio::SetText(const CString &text) { m_EditText = text; m_EditInitial = text; } void CScriptStudio::SetD3XObject(tD3XProgram *prog) { m_D3XProgram = prog; } void CScriptStudio::GetText(CString &text) { text = m_EditText; } void CScriptStudio::UpdateEditText() { CHARRANGE chr; m_RichEdit.HideCaret(); m_RichEdit.GetSel(chr); m_RichEdit.SetSel(0, -1); m_EditText = m_RichEdit.GetSelText(); m_RichEdit.SetSel(chr); m_RichEdit.ShowCaret(); // m_RichEdit.GetWindowText(m_EditText); } void CScriptStudio::SelectText(const char *txt) { m_SelectText = txt; } BEGIN_MESSAGE_MAP(CScriptStudio, CDialog) //{{AFX_MSG_MAP(CScriptStudio) ON_COMMAND(ID_OSIRIS_COMPILE_SCRIPT, OnOsirisCompileScript) ON_COMMAND(ID_OSIRIS_COPY, OnOsirisCopy) ON_COMMAND(ID_OSIRIS_CUT, OnOsirisCut) ON_COMMAND(ID_OSIRIS_EXPORT_SCRIPT, OnOsirisExportScript) ON_COMMAND(ID_OSIRIS_FIND, OnOsirisFind) ON_COMMAND(ID_OSIRIS_FIND_AND_REPLACE, OnOsirisFindAndReplace) ON_COMMAND(ID_OSIRIS_IMPORT_SCRIPT, OnOsirisImportScript) ON_COMMAND(ID_OSIRIS_PASTE, OnOsirisPaste) ON_COMMAND(ID_OSIRIS_SAVE_EXIT, OnOsirisSaveExit) ON_COMMAND(ID_OSIRIS_SET_EXTERNAL, OnOsirisSetExternal) ON_COMMAND(ID_OSIRIS_USE_EXTERNAL, OnOsirisUseExternal) ON_UPDATE_COMMAND_UI(ID_OSIRIS_PASTE, OnUpdateOsirisPaste) ON_NOTIFY(EN_MSGFILTER, IDC_SCRIPTVIEW, OnMsgfilterScriptView) ON_WM_DESTROY() ON_COMMAND(ID_EXIT, OnExit) //}}AFX_MSG_MAP ON_REGISTERED_MESSAGE(WM_FINDREPLACE, OnFindReplace) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CScriptStudio message handlers BOOL CScriptStudio::OnInitDialog() { RECT rect; CWnd *wnd; PARAFORMAT pf; CHARFORMAT cf; m_RichEditInit = false; CDialog::OnInitDialog(); // Create our Rich Edit Control add keyboard handler for those SPECIAL keys like TAB. wnd = GetDlgItem(IDC_SCRMOD_BOX); wnd->GetWindowRect(&rect); rect.left += 2; rect.right -= 2; rect.top += 2; rect.bottom -= 2; ScreenToClient(&rect); m_RichEdit.Create(WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_SAVESEL | ES_NOHIDESEL | WS_VSCROLL, rect, this, IDC_SCRIPTVIEW); m_RichEdit.SetEventMask(ENM_KEYEVENTS | ENM_MOUSEEVENTS); m_RichEdit.LimitText(0); m_RichEdit.GetParaFormat(pf); pf.cbSize = sizeof(pf); pf.dwMask = PFM_TABSTOPS; pf.cTabCount = MAX_TAB_STOPS; for (int i = 0; i < pf.cTabCount; i++) pf.rgxTabs[i] = 360 * (i + 1); m_RichEdit.SetParaFormat(pf); m_RichEdit.SetWindowText((LPCSTR)m_EditText); FindNext((char *)(LPCSTR)m_SelectText); // sets character format ZeroMemory(&cf, sizeof(cf)); cf.dwMask = CFM_FACE | CFM_CHARSET | CFM_BOLD; strcpy(cf.szFaceName, "Courier"); cf.bCharSet = DEFAULT_CHARSET; cf.bPitchAndFamily = FIXED_PITCH | FF_DONTCARE; m_RichEdit.SetDefaultCharFormat(cf); // set linenumber CStatic *linenum = (CStatic *)GetDlgItem(IDC_LINENUM); char buf[8]; sprintf(buf, "%d", m_RichEdit.LineFromChar(-1) + 1); linenum->SetWindowText(buf); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CScriptStudio::OnDestroy() { CDialog::OnDestroy(); } // The Key handler for the editor. Handles TAB chars. void CScriptStudio::OnMsgfilterScriptView(NMHDR *pNMHDR, LRESULT *pResult) { MSGFILTER *pMsgFilter = reinterpret_cast(pNMHDR); // TODO: The control will not send this notification unless you override the // CDialog::OnInitDialog() function to send the EM_SETEVENTMASK message // to the control with either the ENM_KEYEVENTS or ENM_MOUSEEVENTS flag // ORed into the lParam mask. switch (pMsgFilter->msg) { case WM_KEYDOWN: if (pMsgFilter->wParam == VK_TAB) m_RichEdit.ReplaceSel("\t"); *pResult = 0; break; } CStatic *linenum = (CStatic *)GetDlgItem(IDC_LINENUM); char buf[8]; sprintf(buf, "%d", m_RichEdit.LineFromChar(-1) + 1); linenum->SetWindowText(buf); } // Process all commands to and from Rich Edit Control here. BOOL CScriptStudio::OnCommand(WPARAM wParam, LPARAM lParam) { int nCode = HIWORD(wParam); int nID = LOWORD(wParam); switch (nID) { case IDC_SCRIPTVIEW: HandleEditControl(nID, nCode); break; } return CDialog::OnCommand(wParam, lParam); } void CScriptStudio::OnOK() { ddio_KeyFlush(); UpdateEditText(); CDialog::OnOK(); } void CScriptStudio::OnCancel() { ddio_KeyFlush(); if (m_EditInitial != m_EditText) if (OutrageMessageBox(MBOX_YESNO, "You have made changes to this script that won't be saved. Are you sure?") != IDYES) return; CDialog::OnCancel(); } ///////////////////////////////////////////////////////////////////////////////////////////// // Menu functions. void CScriptStudio::OnOsirisSaveExit() { m_EditInitial = m_EditText; OnOK(); } void CScriptStudio::OnOsirisSetExternal() { // OnSetExternalViewer(); } void CScriptStudio::OnOsirisUseExternal() { // OnUseExternal(); } void CScriptStudio::OnOsirisCompileScript() { COsirisStatusDlg progress_status; UpdateEditText(); progress_status.SetScript(m_EditText.GetBuffer(1), m_D3XProgram); progress_status.DoModal(); } void CScriptStudio::OnOsirisImportScript() { CString path; CString filter = "All Files (*.*)|*.*||"; CFileDialog dlg(true, 0, 0, OFN_FILEMUSTEXIST, (LPCTSTR)filter, this); if (dlg.DoModal() == IDOK) { path = dlg.GetPathName(); ImportScript(path.GetBuffer(1)); } } void CScriptStudio::OnOsirisExportScript() { CString path; CString filter = "All Files (*.*)|*.*||"; CFileDialog dlg(false, 0, 0, OFN_OVERWRITEPROMPT, (LPCTSTR)filter, this); if (dlg.DoModal() == IDOK) { path = dlg.GetPathName(); ExportScript(path.GetBuffer(1)); } } ////////////////////////////////////////////////////////////////////////////// // Editing and Clipboard functionality void CScriptStudio::OnOsirisFind() { InitFindReplace(true); } void CScriptStudio::OnOsirisFindAndReplace() { // TODO: Add your command handler code here InitFindReplace(false); } void CScriptStudio::OnOsirisPaste() { m_RichEdit.Paste(); m_RichEdit.RedrawWindow(); } void CScriptStudio::OnOsirisCopy() { m_RichEdit.Copy(); } void CScriptStudio::OnOsirisCut() { m_RichEdit.Cut(); } void CScriptStudio::OnUpdateOsirisPaste(CCmdUI *pCmdUI) { // this doesn't work right...don't know why if (IsClipboardFormatAvailable(CF_TEXT)) pCmdUI->Enable(true); else pCmdUI->Enable(false); } ///////////////////////////////////////////////////////////////////////////////////////////// // These Functions deal with user interface issues within the editor like Find-Replace. // This is the message handler of sorts for the Find/Replace dialog, it interprets the message and passes the control // to the appropriate function LONG CScriptStudio::OnFindReplace(WPARAM wParam, LPARAM lParam) { // Get a pointer to the calling dialog CFindReplaceDialog *pDlg = CFindReplaceDialog::GetNotifier(lParam); ASSERT(pDlg != NULL); // See what the user is up to out there... if (pDlg->IsTerminating()) { // Time to kill the dialog box return 0; } if (pDlg->ReplaceAll()) { // Put a call to your ReplaceAll() method here... CString find, repl; find = pDlg->GetFindString(); repl = pDlg->GetReplaceString(); if (pDlg->MatchCase()) m_FindMatchCase = true; else m_FindMatchCase = false; ReplaceAll(find.GetBuffer(1), repl.GetBuffer(1)); return 0; } if (pDlg->ReplaceCurrent()) { // Put a call to your ReplaceCurrent() method here... CString repl; repl = pDlg->GetReplaceString(); if (pDlg->MatchCase()) m_FindMatchCase = true; else m_FindMatchCase = false; ReplaceCurrent(repl.GetBuffer(1)); return 0; } CString str; if (pDlg->MatchCase()) m_FindMatchCase = true; else m_FindMatchCase = false; str = pDlg->GetFindString(); FindNext(str.GetBuffer(1)); return 0; } // This initializes a Find/Replace dialog box (if bFind==false it sets up a Find and Replace, else it's just Find) void CScriptStudio::InitFindReplace(BOOL bFind) { // // If the dialog has been called already, and then closed, // the pointer is still non-NULL, but the object is already // dead, so reset the pointer! // m_pdlgFindReplace = NULL; m_LastWord = " "; // Construct the dialog m_pdlgFindReplace = new CFindReplaceDialog; ASSERT(m_pdlgFindReplace != NULL); // Initialize the dialog if (!m_pdlgFindReplace->Create(bFind, 0, 0, FR_DOWN, this)) { mprintf(0, "Error allocating find/replace dialog!"); m_pdlgFindReplace = NULL; return; } // Display the dialog ASSERT(m_pdlgFindReplace != NULL); m_pdlgFindReplace->SetActiveWindow(); m_pdlgFindReplace->ShowWindow(SW_SHOW); } // This will look for the next series of charaters (given by the null terminated w) // If it finds the string, it will highlight/select it and move the caret to the beginning of the word // returns true if it found the string // else false bool CScriptStudio::FindNext(char *w) { int index, max; bool word_found; bool new_word; static int start = 0; word_found = false; // See if this is a new word to look for (as compared to the last searched word) if (m_LastWord != w) { m_LastWord = w; start = 0; new_word = true; } else new_word = false; // This will update the m_sScript string so it has the current script in it UpdateEditText(); // Make a copy of the script to work with char *text; text = mem_rmalloc(m_EditText.GetLength() + 1); if (!text) Int3(); strcpy(text, (LPCSTR)m_EditText); max = m_EditText.GetLength(); // Make sure we aren't gonna try to go past the end of the buffer, start at 0 if we are if (start + (signed)strlen(w) >= max - 1) start = 0; // Here we begin the search for (index = start; index < max; index++) { // check the character it's at in the buffer to the first character of the find string, if they are the same // it's a possibility, so Check the word if (toupper(text[index]) == toupper(w[0])) if (CheckWord(index, text, w)) { // We got a match, so move the caret, and select the word SetCurrentIndex(index); SetSelection(index, strlen(w)); word_found = true; // adjust start so on the next FindNext() it will start from the next character start = index + 1; index = max; } } if (text) mem_free(text); if (!word_found) { // This can mean 2 things. We searched to the end of the file and either never found a word, or we did at one time // either way, start back at the beginning next time start = 0; // if it was a new word then we never found it if (new_word) { OutrageMessageBox("Word Not Found!"); m_LastWord = " "; } else { OutrageMessageBox("Searched to end of file"); } return false; } return true; } // This selects a word/string in the edit box, given the starting zero based character index and the length of the // string void CScriptStudio::SetSelection(int index, int length) { m_RichEdit.SetActiveWindow(); m_RichEdit.SetFocus(); m_RichEdit.ShowCaret(); m_RichEdit.SetSel(-1, 0); m_RichEdit.SetSel(index, index + length); } // This function clears all selections void CScriptStudio::RemoveAllSelections() { m_RichEdit.SetFocus(); m_RichEdit.ShowCaret(); m_RichEdit.SetSel(-1, 0); } // Replaces the selected word with the passed word void CScriptStudio::ReplaceSelected(char *replace_word) { long start_index, end_index; UpdateData(false); m_RichEdit.SetFocus(); m_RichEdit.GetSel(start_index, end_index); m_RichEdit.ReplaceSel(replace_word); UpdateData(); SetCurrentIndex(start_index); } // This will start from the beginning and replace all the occurences void CScriptStudio::ReplaceAll(char *find_word, char *replace_word) { SetCurrentIndex(0); m_LastWord = " "; while (FindNext(find_word)) { ReplaceSelected(replace_word); } } // This only replaces the selected string with the replace_word void CScriptStudio::ReplaceCurrent(char *replace_word) { ReplaceSelected(replace_word); } // This function moves the caret to the position specified in index void CScriptStudio::SetCurrentIndex(int index) { CPoint point; m_RichEdit.SetFocus(); m_RichEdit.ShowCaret(); point = m_RichEdit.GetCharPos(index); m_RichEdit.SetCaretPos(point); } // This function checks to see if the string w is the same as the string that begins in text[index] // returns true if it is bool CScriptStudio::CheckWord(int index, char *text, char *w) { int word_length = strlen(w); int buffer_size = strlen(text); int count; if (m_FindMatchCase) { for (count = 0; count < word_length; count++) { if (index + count >= buffer_size) return false; if (text[index + count] != w[count]) return false; } } else { for (count = 0; count < word_length; count++) { if (index + count >= buffer_size) return false; if (toupper(text[index + count]) != toupper(w[count])) return false; } } return true; } ///////////////////////////////////////////////////////////////////////////////////////////// // These function deal with importing and exporting script files. void CScriptStudio::ExportScript(char *filename) { CFILE *file; UpdateEditText(); file = cfopen(filename, "wt"); mprintf(0, "Exporting Script to file %s\n", filename); cf_WriteString(file, m_EditText.GetBuffer(1)); cfclose(file); } void CScriptStudio::ImportScript(char *filename) { CFILE *file; CString temp; int size; char buffer[MAX_SCRIPT_LINE_SIZE]; memset(buffer, 0, MAX_SCRIPT_LINE_SIZE); if (!cfexist(filename)) { OutrageMessageBox("File not found!"); return; } file = cfopen(filename, "rt"); mprintf(0, "Importing Script from file %s\n", filename); do { size = cf_ReadString(buffer, MAX_SCRIPT_LINE_SIZE - 2, file); buffer[size] = '\xd'; buffer[size + 1] = '\xa'; buffer[size + 2] = '\0'; temp += buffer; memset(buffer, 0, size); } while (!cfeof(file)); cfclose(file); m_EditText = temp; m_RichEdit.SetWindowText((LPCSTR)m_EditText); } void CScriptStudio::HandleEditControl(int id, int code) { CRichEditCtrl *rec = (CRichEditCtrl *)GetDlgItem(id); switch (code) { case EN_UPDATE: break; } } bool InvokeScriptStudio(const char *filename, const char *scrname) { #if 0 // LGT: D3XReallocProgram undefined char *source; CScriptStudio studio; tD3XProgram *script; bool ret = false; // initialize script studio. script = D3XReallocProgram(NULL, 0, 0, 0); if (!script) { OutrageMessageBox("Failure to compile script! Something is wrong!"); return false; } source = LoadScript(filename); if (!source) { OutrageMessageBox("Unable to find script for level. Something is wrong!"); return false; } studio.SelectText(scrname); studio.SetD3XObject(script); studio.SetText(CString(source)); FreeScript(source); if (studio.DoModal() == IDOK) { CString tempstr; studio.GetText(tempstr); source = (char *) mem_malloc(tempstr.GetLength()+1); strcpy(source, tempstr); SaveScript(filename, source); mem_free(source); ret = true; } else { OutrageMessageBox("The level script could not compile and is invalid until corrected."); ret = false; } D3XFreeProgram(script); return ret; #else return false; #endif } void CScriptStudio::OnExit() { // TODO: Add your command handler code here CDialog::OnOK(); }