2 * $Logfile: /Freespace2/code/FRED2/MessageEditorDlg.cpp $
7 * Old message editor dialog box handling code. This was designed a LONG time ago
8 * and because so much changed, I created a new one from scratch instead. This is
9 * only around just in case it might be useful.
12 * Revision 1.2 2002/05/07 03:16:44 theoddone33
13 * The Great Newline Fix
15 * Revision 1.1.1.1 2002/05/03 03:28:08 root
19 * 2 10/07/98 6:28p Dave
20 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
21 * Fred. Globalized mission and campaign file extensions. Removed Silent
22 * Threat specific code.
24 * 1 10/07/98 3:01p Dave
26 * 1 10/07/98 3:00p Dave
28 * 26 4/07/98 10:51a Allender
29 * remove any allied from message senders. Make heads for mission
30 * specific messages play appropriately
32 * 25 1/07/98 5:58p Hoffoss
33 * Combined message editor into event editor.
35 * 24 1/06/98 4:19p Hoffoss
36 * Made message editor accept returns instead of closing dialog.
38 * 23 12/30/97 9:39a Allender
39 * minor fixups with message editing
41 * 22 11/17/97 4:57p Allender
42 * added persona support in FreeSpace. A couple of new messages for
43 * message.tbl which Dan didn't have
45 * 21 10/16/97 8:54p Duncan
46 * Fixed bug in adding new message.
48 * 20 10/14/97 10:31a Allender
49 * disable persona field for builtin messages
51 * 19 10/13/97 11:37a Allender
52 * added personas to message editor in Fred
54 * 18 10/10/97 6:21p Hoffoss
55 * Put in Fred support for training object list editing.
57 * 17 10/08/97 7:52p Hoffoss
58 * Made renaming events update all sexp references to it (added a
59 * generalized function to replace all references to a name by argument
62 * 16 10/08/97 4:41p Hoffoss
63 * Changed the way message editor works. Each message is updated
64 * perminently when you switch messages (as if ok button was pressed).
66 * 15 9/09/97 9:19a Hoffoss
67 * Fixed bug in Fred: messages shouldn't be send from the player to the
70 * 14 8/08/97 3:29p Duncan
71 * Fixed bug with strings exceeding their limits.
73 * 13 8/05/97 5:12p Jasen
74 * Added advanced_stricmp() function to handle NULL pointers gracefully,
75 * and utilized it in message editor query update function.
77 * 12 8/01/97 3:47p Hoffoss
78 * Fixed bug where message editor believes modifications occured when
79 * nothing actually changed.
81 * 11 7/24/97 4:34p Hoffoss
84 * 10 7/16/97 6:30p Hoffoss
85 * Added icons to sexp trees, mainly because I think they will be required
88 * 9 7/14/97 9:55p Hoffoss
89 * Making changes to message editor system.
91 * 8 7/10/97 2:32p Hoffoss
92 * Made message editor dialog box modeless.
94 * 7 7/02/97 5:09p Hoffoss
95 * Added browse buttons to message editor.
97 * 6 5/20/97 2:28p Hoffoss
98 * Added message box queries for close window operation on all modal
101 * 5 4/17/97 2:01p Hoffoss
102 * All dialog box window states are saved between sessions now.
104 * 4 4/01/97 5:15p Hoffoss
105 * Fixed errors in max length checks, renaming a wing now renames the
106 * ships in the wing as well, as it should.
108 * 3 3/11/97 2:19p Hoffoss
109 * New message structure support for Fred.
111 * 2 2/17/97 5:28p Hoffoss
112 * Checked RCS headers, added them were missing, changing description to
113 * something better, etc where needed.
120 #include "messageeditordlg.h"
122 #include "management.h"
123 #include "sexp_tree.h"
124 #include "eventeditor.h"
125 #include "missionmessage.h"
128 #define new DEBUG_NEW
130 static char THIS_FILE[] = __FILE__;
133 CMessageEditorDlg *Message_editor_dlg = NULL;
135 /////////////////////////////////////////////////////////////////////////////
136 // CMessageEditorDlg dialog
138 CMessageEditorDlg::CMessageEditorDlg(CWnd* pParent /*=NULL*/)
139 : CDialog(CMessageEditorDlg::IDD, pParent)
141 //{{AFX_DATA_INIT(CMessageEditorDlg)
142 m_avi_filename = _T("");
143 m_wave_filename = _T("");
144 m_message_text = _T("");
145 m_message_name = _T("");
152 m_tree.link_modified(&modified);
157 void CMessageEditorDlg::DoDataExchange(CDataExchange* pDX)
159 CDialog::DoDataExchange(pDX);
160 //{{AFX_DATA_MAP(CMessageEditorDlg)
161 DDX_Control(pDX, IDC_TREE, m_tree);
162 DDX_CBString(pDX, IDC_AVI_FILENAME, m_avi_filename);
163 DDX_CBString(pDX, IDC_WAVE_FILENAME, m_wave_filename);
164 DDX_Text(pDX, IDC_MESSAGE_TEXT, m_message_text);
165 DDX_Text(pDX, IDC_NAME, m_message_name);
166 DDX_LBIndex(pDX, IDC_MESSAGE_LIST, m_cur_msg);
167 DDX_CBIndex(pDX, IDC_PRIORITY, m_priority);
168 DDX_CBIndex(pDX, IDC_SENDER, m_sender);
169 DDX_CBIndex(pDX, IDC_PERSONA_NAME, m_persona);
171 DDV_MaxChars(pDX, m_message_name, NAME_LENGTH - 1);
172 DDV_MaxChars(pDX, m_message_text, MESSAGE_LENGTH - 1);
173 DDV_MaxChars(pDX, m_avi_filename, MAX_FILENAME_LEN - 1);
174 DDV_MaxChars(pDX, m_wave_filename, MAX_FILENAME_LEN - 1);
177 BEGIN_MESSAGE_MAP(CMessageEditorDlg, CDialog)
178 //{{AFX_MSG_MAP(CMessageEditorDlg)
179 ON_LBN_SELCHANGE(IDC_MESSAGE_LIST, OnSelchangeMessageList)
180 ON_EN_UPDATE(IDC_NAME, OnUpdateName)
181 ON_BN_CLICKED(IDC_DELETE, OnDelete)
182 ON_BN_CLICKED(IDC_NEW, OnNew)
184 ON_BN_CLICKED(IDC_BROWSE_AVI, OnBrowseAvi)
185 ON_BN_CLICKED(IDC_BROWSE_WAVE, OnBrowseWave)
186 ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree)
187 ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree)
188 ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree)
189 ON_BN_CLICKED(ID_OK, OnOk)
193 /////////////////////////////////////////////////////////////////////////////
194 // CMessageEditorDlg message handlers
196 BOOL CMessageEditorDlg::OnInitDialog()
202 CDialog::OnInitDialog();
203 theApp.init_window(&Messages_wnd_data, this);
206 ((CEdit *) GetDlgItem(IDC_NAME))->LimitText(NAME_LENGTH - 1);
207 ((CEdit *) GetDlgItem(IDC_MESSAGE_TEXT))->LimitText(MESSAGE_LENGTH - 1);
208 ((CComboBox *) GetDlgItem(IDC_AVI_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
209 ((CComboBox *) GetDlgItem(IDC_WAVE_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
211 list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
212 list->ResetContent();
213 for (i=0; i<Num_messages; i++) {
214 //Assert(list->FindStringExact(-1, Messages[i].name) == CB_ERR);
215 // mwa we should probably not include builtin messages into this list!
216 list->AddString(Messages[i].name);
219 box = (CComboBox *) GetDlgItem(IDC_AVI_FILENAME);
220 for (i=0; i<Num_messages; i++)
221 if (Messages[i].avi_info.name)
222 if (box->FindStringExact(-1, Messages[i].avi_info.name) == CB_ERR)
223 box->AddString(Messages[i].avi_info.name);
225 box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME);
226 for (i=0; i<Num_messages; i++)
227 if (Messages[i].wave_info.name)
228 if (box->FindStringExact(i, Messages[i].wave_info.name) == CB_ERR)
229 box->AddString(Messages[i].wave_info.name);
231 // add the persona names into the combo box
232 box = (CComboBox *)GetDlgItem(IDC_PERSONA_NAME);
234 box->AddString("<None>");
235 for (i = 0; i < Num_personas; i++ )
236 box->AddString( Personas[i].name );
238 box = (CComboBox *) GetDlgItem(IDC_SENDER);
239 for (i=0; i<MAX_SHIPS; i++)
240 if ((Ships[i].objnum >= 0) && (Objects[Ships[i].objnum].type == OBJ_SHIP))
241 box->AddString(Ships[i].ship_name);
243 for (i=0; i<MAX_WINGS; i++)
244 if (Wings[i].wave_count)
245 box->AddString(Wings[i].name);
247 box->AddString("<Any wingman>");
249 // MWA 4/7/98 -- removed any allied
250 //box->AddString("<Any allied>");
252 // set the first message to be the first non-builtin message (if it exists)
253 if ( Num_messages > Num_builtin_messages )
254 m_cur_msg = Num_builtin_messages;
255 else if (Num_messages)
260 if (Num_messages >= MAX_MISSION_MESSAGES)
261 GetDlgItem(IDC_NEW)->EnableWindow(FALSE);
263 update_cur_message();
267 int CMessageEditorDlg::query_modified()
269 char *ptr, buf[MESSAGE_LENGTH];
279 ptr = (char *) (LPCTSTR) m_message_name;
280 for (i=0; i<Num_messages; i++)
281 if ((i != m_cur_msg) && (!stricmp(ptr, Messages[i].name)))
284 if (i < Num_messages)
285 if (stricmp(ptr, Messages[m_cur_msg].name))
286 return 1; // name is different and allowed to update
288 string_copy(buf, m_message_text, MESSAGE_LENGTH - 1);
289 if (stricmp(buf, Messages[m_cur_msg].message))
292 ptr = (char *) (LPCTSTR) m_avi_filename;
293 if (advanced_stricmp(ptr, Messages[m_cur_msg].avi_info.name))
296 ptr = (char *) (LPCTSTR) m_wave_filename;
297 if (advanced_stricmp(ptr, Messages[m_cur_msg].wave_info.name))
300 // check to see if persona changed. use -1 since we stuck a "None" for persona
301 // at the beginning of the list.
302 if ( (m_persona - 1 ) != Messages[m_cur_msg].persona_index )
305 if (m_tree.query_false()) {
306 if (m_event_num >= 0)
317 void CMessageEditorDlg::OnOK()
321 void CMessageEditorDlg::OnOk()
324 theApp.record_window_data(&Messages_wnd_data, this);
325 delete Message_editor_dlg;
326 Message_editor_dlg = NULL;
329 void CMessageEditorDlg::OnCancel()
331 theApp.record_window_data(&Messages_wnd_data, this);
332 delete Message_editor_dlg;
333 Message_editor_dlg = NULL;
336 // load controls with structure data
337 void CMessageEditorDlg::update_cur_message()
339 int node, enable = TRUE, enable2 = TRUE;
343 enable = enable2 = FALSE;
344 m_message_name = _T("");
345 m_message_text = _T("");
346 m_avi_filename = _T("");
347 m_wave_filename = _T("");
350 m_sender = m_priority = -1;
353 m_message_name = Messages[m_cur_msg].name;
354 m_message_text = Messages[m_cur_msg].message;
355 if (Messages[m_cur_msg].avi_info.name)
356 m_avi_filename = _T(Messages[m_cur_msg].avi_info.name);
358 m_avi_filename = _T("");
360 if (Messages[m_cur_msg].wave_info.name)
361 m_wave_filename = _T(Messages[m_cur_msg].wave_info.name);
363 m_wave_filename = _T("");
366 if ( Messages[m_cur_msg].persona_index != -1 )
367 m_persona = Messages[m_cur_msg].persona_index + 1; // add one for the "none" at the beginning of the list
371 m_event_num = find_event();
372 if (m_event_num < 0) {
374 m_sender = m_priority = 0;
377 node = CADR(Mission_events[m_event_num].formula);
379 m_tree.load_tree(node, "false");
382 if (m_cur_msg < Num_builtin_messages)
385 GetDlgItem(IDC_NAME)->EnableWindow(enable);
386 GetDlgItem(IDC_MESSAGE_TEXT)->EnableWindow(enable);
387 GetDlgItem(IDC_AVI_FILENAME)->EnableWindow(enable);
388 GetDlgItem(IDC_WAVE_FILENAME)->EnableWindow(enable);
389 GetDlgItem(IDC_DELETE)->EnableWindow(enable);
390 GetDlgItem(IDC_TREE)->EnableWindow(enable2);
391 GetDlgItem(IDC_SENDER)->EnableWindow(enable2);
392 GetDlgItem(IDC_PRIORITY)->EnableWindow(enable2);
393 GetDlgItem(IDC_PERSONA_NAME)->EnableWindow(enable);
397 int CMessageEditorDlg::find_event()
400 int i, formula, node;
403 for (i=0; i<Num_mission_events; i++) {
404 node = Mission_events[i].formula;
405 if (find_operator(CTEXT(node)) == OP_WHEN) {
407 formula = CAR(node); // bool conditional
408 if (CDR(CDR(node)) == -1) { // only 1 action
410 if ((find_operator(CTEXT(node)) == OP_SEND_MESSAGE) && !stricmp(CTEXT(CDR(CDR(CDR(node)))), m_message_name)) {
411 box = (CComboBox *) GetDlgItem(IDC_SENDER);
412 str = CTEXT(CDR(node));
413 m_sender = box->FindStringExact(-1, str);
414 if (m_sender == CB_ERR)
417 box = (CComboBox *) GetDlgItem(IDC_PRIORITY);
418 str = CTEXT(CDR(CDR(node)));
419 m_priority = box->FindStringExact(-1, str);
420 if (m_priority == CB_ERR)
429 m_sender = m_priority = 0;
433 void CMessageEditorDlg::OnSelchangeMessageList()
441 if (update(m_cur_msg)) {
443 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST)) -> SetCurSel(old);
449 update_cur_message();
452 void CMessageEditorDlg::OnUpdateName()
456 int CMessageEditorDlg::update(int num)
458 char *ptr, buf[4096];
465 ptr = (char *) (LPCTSTR) m_message_name;
466 for (i=0; i<Num_messages; i++)
467 if ((i != num) && (!stricmp(m_message_name, Messages[i].name)))
470 if (i == Num_messages) { // update name if no conflicts, otherwise keep old name
471 update_sexp_references(Messages[num].name, ptr, OPF_MESSAGE);
472 string_copy(Messages[num].name, m_message_name, NAME_LENGTH - 1);
474 list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
475 list->DeleteString(num);
476 list->InsertString(num, m_message_name);
479 string_copy(Messages[num].message, m_message_text, MESSAGE_LENGTH - 1);
480 if (Messages[num].avi_info.name)
481 free(Messages[num].avi_info.name);
483 ptr = (char *) (LPCTSTR) m_avi_filename;
484 if (!ptr || !strlen(ptr))
485 Messages[num].avi_info.name = NULL;
487 Messages[num].avi_info.name = strdup(ptr);
489 if (Messages[num].wave_info.name)
490 free(Messages[num].wave_info.name);
492 ptr = (char *) (LPCTSTR) m_wave_filename;
493 if (!ptr || !strlen(ptr))
494 Messages[num].wave_info.name = NULL;
496 Messages[num].wave_info.name = strdup(ptr);
498 // update the persona to the message. We subtract 1 for the "None" at the beginning of the combo
500 Messages[num].persona_index = m_persona - 1;
502 if (m_tree.query_false()) {
503 if (m_event_num >= 0) { // need to delete event
505 free_sexp2(Mission_events[i].formula);
506 Assert(i < Num_mission_events);
507 while (i < Num_mission_events - 1) {
508 Mission_events[i] = Mission_events[i + 1];
512 Num_mission_events--;
517 if (m_event_num >= 0)
518 free_sexp2(Mission_events[m_event_num].formula);
521 if (Num_mission_events == MAX_MISSION_EVENTS) {
522 MessageBox("You have reached the limit on mission events.\n"
523 "Can't add an event to send this message.");
528 Assert(Num_mission_events < MAX_MISSION_EVENTS);
529 m_event_num = Num_mission_events++;
530 string_copy(Mission_events[m_event_num].name, m_message_name, NAME_LENGTH - 1);
531 Mission_events[m_event_num].repeat_count = 1;
532 Mission_events[m_event_num].interval = 1;
533 Mission_events[m_event_num].score = 0;
534 Mission_events[m_event_num].chain_delay = -1;
535 Mission_events[m_event_num].objective_text = NULL;
536 Mission_events[m_event_num].objective_key_text = NULL;
539 fnode = m_tree.save_tree();
540 ptr = (char *) (LPCTSTR) m_message_name;
541 node = alloc_sexp(ptr, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
542 ((CComboBox *) GetDlgItem(IDC_PRIORITY))->GetLBText(m_priority, buf);
543 node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node);
544 ((CComboBox *) GetDlgItem(IDC_SENDER))->GetLBText(m_sender, buf);
545 node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node);
546 node = alloc_sexp("send-message", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node);
547 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
548 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, fnode, node);
549 node = alloc_sexp("when", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node);
550 Mission_events[m_event_num].formula = node;
555 if (query_modified())
562 void CMessageEditorDlg::OnDelete()
567 Assert((m_cur_msg >= 0) && (m_cur_msg < Num_messages));
568 if (Messages[m_cur_msg].avi_info.name)
569 free(Messages[m_cur_msg].avi_info.name);
570 if (Messages[m_cur_msg].wave_info.name)
571 free(Messages[m_cur_msg].wave_info.name);
573 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->DeleteString(m_cur_msg);
574 sprintf(buf, "<%s>", Messages[m_cur_msg].name);
575 update_sexp_references(Messages[m_cur_msg].name, buf, OPF_MESSAGE);
577 for (i=m_cur_msg; i<Num_messages-1; i++)
578 Messages[i] = Messages[i + 1];
581 if (m_cur_msg >= Num_messages)
582 m_cur_msg = Num_messages - 1;
584 GetDlgItem(IDC_NEW)->EnableWindow(TRUE);
586 update_cur_message();
589 void CMessageEditorDlg::OnNew()
591 if (update(m_cur_msg))
594 Assert(Num_messages < MAX_MISSION_MESSAGES);
595 strcpy(Messages[Num_messages].name, "<new message>");
596 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->AddString("<new message>");
598 strcpy(Messages[Num_messages].message, "<put description here>");
599 Messages[Num_messages].avi_info.name = NULL;
600 Messages[Num_messages].wave_info.name = NULL;
601 Messages[Num_messages].persona_index = -1;
602 m_cur_msg = Num_messages++;
603 if (Num_messages >= MAX_MISSION_MESSAGES)
604 GetDlgItem(IDC_NEW)->EnableWindow(FALSE);
607 update_cur_message();
610 void CMessageEditorDlg::OnClose()
614 modified = query_modified();
616 z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL);
626 theApp.record_window_data(&Messages_wnd_data, this);
627 delete Message_editor_dlg;
628 Message_editor_dlg = NULL;
631 void CMessageEditorDlg::OnBrowseAvi()
636 CFileDialog dlg(TRUE, "ani", m_avi_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
637 "Ani Files (*.ani)|*.ani|Avi Files (*.avi)|*.avi|Both (*.ani, *.avi)|*.ani;*.avi||");
639 if (dlg.DoModal() == IDOK)
641 m_avi_filename = dlg.GetFileName();
647 void CMessageEditorDlg::OnBrowseWave()
652 CFileDialog dlg(TRUE, "wav", m_wave_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
653 "Wave Files (*.wav)|*.wav||");
655 if (dlg.DoModal() == IDOK)
657 m_wave_filename = dlg.GetFileName();
663 void CMessageEditorDlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
665 m_tree.right_clicked();
669 void CMessageEditorDlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
671 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
674 if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1) {
677 edit = m_tree.GetEditControl();
679 edit->SetLimitText(NAME_LENGTH - 1);
685 void CMessageEditorDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
687 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
689 *pResult = m_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText);