2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/FRED2/MessageEditorDlg.cpp $
15 * Old message editor dialog box handling code. This was designed a LONG time ago
16 * and because so much changed, I created a new one from scratch instead. This is
17 * only around just in case it might be useful.
20 * Revision 1.3 2002/06/09 04:41:16 relnev
21 * added copyright header
23 * Revision 1.2 2002/05/07 03:16:44 theoddone33
24 * The Great Newline Fix
26 * Revision 1.1.1.1 2002/05/03 03:28:08 root
30 * 2 10/07/98 6:28p Dave
31 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
32 * Fred. Globalized mission and campaign file extensions. Removed Silent
33 * Threat specific code.
35 * 1 10/07/98 3:01p Dave
37 * 1 10/07/98 3:00p Dave
39 * 26 4/07/98 10:51a Allender
40 * remove any allied from message senders. Make heads for mission
41 * specific messages play appropriately
43 * 25 1/07/98 5:58p Hoffoss
44 * Combined message editor into event editor.
46 * 24 1/06/98 4:19p Hoffoss
47 * Made message editor accept returns instead of closing dialog.
49 * 23 12/30/97 9:39a Allender
50 * minor fixups with message editing
52 * 22 11/17/97 4:57p Allender
53 * added persona support in FreeSpace. A couple of new messages for
54 * message.tbl which Dan didn't have
56 * 21 10/16/97 8:54p Duncan
57 * Fixed bug in adding new message.
59 * 20 10/14/97 10:31a Allender
60 * disable persona field for builtin messages
62 * 19 10/13/97 11:37a Allender
63 * added personas to message editor in Fred
65 * 18 10/10/97 6:21p Hoffoss
66 * Put in Fred support for training object list editing.
68 * 17 10/08/97 7:52p Hoffoss
69 * Made renaming events update all sexp references to it (added a
70 * generalized function to replace all references to a name by argument
73 * 16 10/08/97 4:41p Hoffoss
74 * Changed the way message editor works. Each message is updated
75 * perminently when you switch messages (as if ok button was pressed).
77 * 15 9/09/97 9:19a Hoffoss
78 * Fixed bug in Fred: messages shouldn't be send from the player to the
81 * 14 8/08/97 3:29p Duncan
82 * Fixed bug with strings exceeding their limits.
84 * 13 8/05/97 5:12p Jasen
85 * Added advanced_stricmp() function to handle NULL pointers gracefully,
86 * and utilized it in message editor query update function.
88 * 12 8/01/97 3:47p Hoffoss
89 * Fixed bug where message editor believes modifications occured when
90 * nothing actually changed.
92 * 11 7/24/97 4:34p Hoffoss
95 * 10 7/16/97 6:30p Hoffoss
96 * Added icons to sexp trees, mainly because I think they will be required
99 * 9 7/14/97 9:55p Hoffoss
100 * Making changes to message editor system.
102 * 8 7/10/97 2:32p Hoffoss
103 * Made message editor dialog box modeless.
105 * 7 7/02/97 5:09p Hoffoss
106 * Added browse buttons to message editor.
108 * 6 5/20/97 2:28p Hoffoss
109 * Added message box queries for close window operation on all modal
112 * 5 4/17/97 2:01p Hoffoss
113 * All dialog box window states are saved between sessions now.
115 * 4 4/01/97 5:15p Hoffoss
116 * Fixed errors in max length checks, renaming a wing now renames the
117 * ships in the wing as well, as it should.
119 * 3 3/11/97 2:19p Hoffoss
120 * New message structure support for Fred.
122 * 2 2/17/97 5:28p Hoffoss
123 * Checked RCS headers, added them were missing, changing description to
124 * something better, etc where needed.
131 #include "messageeditordlg.h"
133 #include "management.h"
134 #include "sexp_tree.h"
135 #include "eventeditor.h"
136 #include "missionmessage.h"
139 #define new DEBUG_NEW
141 static char THIS_FILE[] = __FILE__;
144 CMessageEditorDlg *Message_editor_dlg = NULL;
146 /////////////////////////////////////////////////////////////////////////////
147 // CMessageEditorDlg dialog
149 CMessageEditorDlg::CMessageEditorDlg(CWnd* pParent /*=NULL*/)
150 : CDialog(CMessageEditorDlg::IDD, pParent)
152 //{{AFX_DATA_INIT(CMessageEditorDlg)
153 m_avi_filename = _T("");
154 m_wave_filename = _T("");
155 m_message_text = _T("");
156 m_message_name = _T("");
163 m_tree.link_modified(&modified);
168 void CMessageEditorDlg::DoDataExchange(CDataExchange* pDX)
170 CDialog::DoDataExchange(pDX);
171 //{{AFX_DATA_MAP(CMessageEditorDlg)
172 DDX_Control(pDX, IDC_TREE, m_tree);
173 DDX_CBString(pDX, IDC_AVI_FILENAME, m_avi_filename);
174 DDX_CBString(pDX, IDC_WAVE_FILENAME, m_wave_filename);
175 DDX_Text(pDX, IDC_MESSAGE_TEXT, m_message_text);
176 DDX_Text(pDX, IDC_NAME, m_message_name);
177 DDX_LBIndex(pDX, IDC_MESSAGE_LIST, m_cur_msg);
178 DDX_CBIndex(pDX, IDC_PRIORITY, m_priority);
179 DDX_CBIndex(pDX, IDC_SENDER, m_sender);
180 DDX_CBIndex(pDX, IDC_PERSONA_NAME, m_persona);
182 DDV_MaxChars(pDX, m_message_name, NAME_LENGTH - 1);
183 DDV_MaxChars(pDX, m_message_text, MESSAGE_LENGTH - 1);
184 DDV_MaxChars(pDX, m_avi_filename, MAX_FILENAME_LEN - 1);
185 DDV_MaxChars(pDX, m_wave_filename, MAX_FILENAME_LEN - 1);
188 BEGIN_MESSAGE_MAP(CMessageEditorDlg, CDialog)
189 //{{AFX_MSG_MAP(CMessageEditorDlg)
190 ON_LBN_SELCHANGE(IDC_MESSAGE_LIST, OnSelchangeMessageList)
191 ON_EN_UPDATE(IDC_NAME, OnUpdateName)
192 ON_BN_CLICKED(IDC_DELETE, OnDelete)
193 ON_BN_CLICKED(IDC_NEW, OnNew)
195 ON_BN_CLICKED(IDC_BROWSE_AVI, OnBrowseAvi)
196 ON_BN_CLICKED(IDC_BROWSE_WAVE, OnBrowseWave)
197 ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree)
198 ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree)
199 ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree)
200 ON_BN_CLICKED(ID_OK, OnOk)
204 /////////////////////////////////////////////////////////////////////////////
205 // CMessageEditorDlg message handlers
207 BOOL CMessageEditorDlg::OnInitDialog()
213 CDialog::OnInitDialog();
214 theApp.init_window(&Messages_wnd_data, this);
217 ((CEdit *) GetDlgItem(IDC_NAME))->LimitText(NAME_LENGTH - 1);
218 ((CEdit *) GetDlgItem(IDC_MESSAGE_TEXT))->LimitText(MESSAGE_LENGTH - 1);
219 ((CComboBox *) GetDlgItem(IDC_AVI_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
220 ((CComboBox *) GetDlgItem(IDC_WAVE_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
222 list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
223 list->ResetContent();
224 for (i=0; i<Num_messages; i++) {
225 //Assert(list->FindStringExact(-1, Messages[i].name) == CB_ERR);
226 // mwa we should probably not include builtin messages into this list!
227 list->AddString(Messages[i].name);
230 box = (CComboBox *) GetDlgItem(IDC_AVI_FILENAME);
231 for (i=0; i<Num_messages; i++)
232 if (Messages[i].avi_info.name)
233 if (box->FindStringExact(-1, Messages[i].avi_info.name) == CB_ERR)
234 box->AddString(Messages[i].avi_info.name);
236 box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME);
237 for (i=0; i<Num_messages; i++)
238 if (Messages[i].wave_info.name)
239 if (box->FindStringExact(i, Messages[i].wave_info.name) == CB_ERR)
240 box->AddString(Messages[i].wave_info.name);
242 // add the persona names into the combo box
243 box = (CComboBox *)GetDlgItem(IDC_PERSONA_NAME);
245 box->AddString("<None>");
246 for (i = 0; i < Num_personas; i++ )
247 box->AddString( Personas[i].name );
249 box = (CComboBox *) GetDlgItem(IDC_SENDER);
250 for (i=0; i<MAX_SHIPS; i++)
251 if ((Ships[i].objnum >= 0) && (Objects[Ships[i].objnum].type == OBJ_SHIP))
252 box->AddString(Ships[i].ship_name);
254 for (i=0; i<MAX_WINGS; i++)
255 if (Wings[i].wave_count)
256 box->AddString(Wings[i].name);
258 box->AddString("<Any wingman>");
260 // MWA 4/7/98 -- removed any allied
261 //box->AddString("<Any allied>");
263 // set the first message to be the first non-builtin message (if it exists)
264 if ( Num_messages > Num_builtin_messages )
265 m_cur_msg = Num_builtin_messages;
266 else if (Num_messages)
271 if (Num_messages >= MAX_MISSION_MESSAGES)
272 GetDlgItem(IDC_NEW)->EnableWindow(FALSE);
274 update_cur_message();
278 int CMessageEditorDlg::query_modified()
280 char *ptr, buf[MESSAGE_LENGTH];
290 ptr = (char *) (LPCTSTR) m_message_name;
291 for (i=0; i<Num_messages; i++)
292 if ((i != m_cur_msg) && (!stricmp(ptr, Messages[i].name)))
295 if (i < Num_messages)
296 if (stricmp(ptr, Messages[m_cur_msg].name))
297 return 1; // name is different and allowed to update
299 string_copy(buf, m_message_text, MESSAGE_LENGTH - 1);
300 if (stricmp(buf, Messages[m_cur_msg].message))
303 ptr = (char *) (LPCTSTR) m_avi_filename;
304 if (advanced_stricmp(ptr, Messages[m_cur_msg].avi_info.name))
307 ptr = (char *) (LPCTSTR) m_wave_filename;
308 if (advanced_stricmp(ptr, Messages[m_cur_msg].wave_info.name))
311 // check to see if persona changed. use -1 since we stuck a "None" for persona
312 // at the beginning of the list.
313 if ( (m_persona - 1 ) != Messages[m_cur_msg].persona_index )
316 if (m_tree.query_false()) {
317 if (m_event_num >= 0)
328 void CMessageEditorDlg::OnOK()
332 void CMessageEditorDlg::OnOk()
335 theApp.record_window_data(&Messages_wnd_data, this);
336 delete Message_editor_dlg;
337 Message_editor_dlg = NULL;
340 void CMessageEditorDlg::OnCancel()
342 theApp.record_window_data(&Messages_wnd_data, this);
343 delete Message_editor_dlg;
344 Message_editor_dlg = NULL;
347 // load controls with structure data
348 void CMessageEditorDlg::update_cur_message()
350 int node, enable = TRUE, enable2 = TRUE;
354 enable = enable2 = FALSE;
355 m_message_name = _T("");
356 m_message_text = _T("");
357 m_avi_filename = _T("");
358 m_wave_filename = _T("");
361 m_sender = m_priority = -1;
364 m_message_name = Messages[m_cur_msg].name;
365 m_message_text = Messages[m_cur_msg].message;
366 if (Messages[m_cur_msg].avi_info.name)
367 m_avi_filename = _T(Messages[m_cur_msg].avi_info.name);
369 m_avi_filename = _T("");
371 if (Messages[m_cur_msg].wave_info.name)
372 m_wave_filename = _T(Messages[m_cur_msg].wave_info.name);
374 m_wave_filename = _T("");
377 if ( Messages[m_cur_msg].persona_index != -1 )
378 m_persona = Messages[m_cur_msg].persona_index + 1; // add one for the "none" at the beginning of the list
382 m_event_num = find_event();
383 if (m_event_num < 0) {
385 m_sender = m_priority = 0;
388 node = CADR(Mission_events[m_event_num].formula);
390 m_tree.load_tree(node, "false");
393 if (m_cur_msg < Num_builtin_messages)
396 GetDlgItem(IDC_NAME)->EnableWindow(enable);
397 GetDlgItem(IDC_MESSAGE_TEXT)->EnableWindow(enable);
398 GetDlgItem(IDC_AVI_FILENAME)->EnableWindow(enable);
399 GetDlgItem(IDC_WAVE_FILENAME)->EnableWindow(enable);
400 GetDlgItem(IDC_DELETE)->EnableWindow(enable);
401 GetDlgItem(IDC_TREE)->EnableWindow(enable2);
402 GetDlgItem(IDC_SENDER)->EnableWindow(enable2);
403 GetDlgItem(IDC_PRIORITY)->EnableWindow(enable2);
404 GetDlgItem(IDC_PERSONA_NAME)->EnableWindow(enable);
408 int CMessageEditorDlg::find_event()
411 int i, formula, node;
414 for (i=0; i<Num_mission_events; i++) {
415 node = Mission_events[i].formula;
416 if (find_operator(CTEXT(node)) == OP_WHEN) {
418 formula = CAR(node); // bool conditional
419 if (CDR(CDR(node)) == -1) { // only 1 action
421 if ((find_operator(CTEXT(node)) == OP_SEND_MESSAGE) && !stricmp(CTEXT(CDR(CDR(CDR(node)))), m_message_name)) {
422 box = (CComboBox *) GetDlgItem(IDC_SENDER);
423 str = CTEXT(CDR(node));
424 m_sender = box->FindStringExact(-1, str);
425 if (m_sender == CB_ERR)
428 box = (CComboBox *) GetDlgItem(IDC_PRIORITY);
429 str = CTEXT(CDR(CDR(node)));
430 m_priority = box->FindStringExact(-1, str);
431 if (m_priority == CB_ERR)
440 m_sender = m_priority = 0;
444 void CMessageEditorDlg::OnSelchangeMessageList()
452 if (update(m_cur_msg)) {
454 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST)) -> SetCurSel(old);
460 update_cur_message();
463 void CMessageEditorDlg::OnUpdateName()
467 int CMessageEditorDlg::update(int num)
469 char *ptr, buf[4096];
476 ptr = (char *) (LPCTSTR) m_message_name;
477 for (i=0; i<Num_messages; i++)
478 if ((i != num) && (!stricmp(m_message_name, Messages[i].name)))
481 if (i == Num_messages) { // update name if no conflicts, otherwise keep old name
482 update_sexp_references(Messages[num].name, ptr, OPF_MESSAGE);
483 string_copy(Messages[num].name, m_message_name, NAME_LENGTH - 1);
485 list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
486 list->DeleteString(num);
487 list->InsertString(num, m_message_name);
490 string_copy(Messages[num].message, m_message_text, MESSAGE_LENGTH - 1);
491 if (Messages[num].avi_info.name)
492 free(Messages[num].avi_info.name);
494 ptr = (char *) (LPCTSTR) m_avi_filename;
495 if (!ptr || !strlen(ptr))
496 Messages[num].avi_info.name = NULL;
498 Messages[num].avi_info.name = strdup(ptr);
500 if (Messages[num].wave_info.name)
501 free(Messages[num].wave_info.name);
503 ptr = (char *) (LPCTSTR) m_wave_filename;
504 if (!ptr || !strlen(ptr))
505 Messages[num].wave_info.name = NULL;
507 Messages[num].wave_info.name = strdup(ptr);
509 // update the persona to the message. We subtract 1 for the "None" at the beginning of the combo
511 Messages[num].persona_index = m_persona - 1;
513 if (m_tree.query_false()) {
514 if (m_event_num >= 0) { // need to delete event
516 free_sexp2(Mission_events[i].formula);
517 Assert(i < Num_mission_events);
518 while (i < Num_mission_events - 1) {
519 Mission_events[i] = Mission_events[i + 1];
523 Num_mission_events--;
528 if (m_event_num >= 0)
529 free_sexp2(Mission_events[m_event_num].formula);
532 if (Num_mission_events == MAX_MISSION_EVENTS) {
533 MessageBox("You have reached the limit on mission events.\n"
534 "Can't add an event to send this message.");
539 Assert(Num_mission_events < MAX_MISSION_EVENTS);
540 m_event_num = Num_mission_events++;
541 string_copy(Mission_events[m_event_num].name, m_message_name, NAME_LENGTH - 1);
542 Mission_events[m_event_num].repeat_count = 1;
543 Mission_events[m_event_num].interval = 1;
544 Mission_events[m_event_num].score = 0;
545 Mission_events[m_event_num].chain_delay = -1;
546 Mission_events[m_event_num].objective_text = NULL;
547 Mission_events[m_event_num].objective_key_text = NULL;
550 fnode = m_tree.save_tree();
551 ptr = (char *) (LPCTSTR) m_message_name;
552 node = alloc_sexp(ptr, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
553 ((CComboBox *) GetDlgItem(IDC_PRIORITY))->GetLBText(m_priority, buf);
554 node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node);
555 ((CComboBox *) GetDlgItem(IDC_SENDER))->GetLBText(m_sender, buf);
556 node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node);
557 node = alloc_sexp("send-message", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node);
558 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
559 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, fnode, node);
560 node = alloc_sexp("when", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node);
561 Mission_events[m_event_num].formula = node;
566 if (query_modified())
573 void CMessageEditorDlg::OnDelete()
578 Assert((m_cur_msg >= 0) && (m_cur_msg < Num_messages));
579 if (Messages[m_cur_msg].avi_info.name)
580 free(Messages[m_cur_msg].avi_info.name);
581 if (Messages[m_cur_msg].wave_info.name)
582 free(Messages[m_cur_msg].wave_info.name);
584 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->DeleteString(m_cur_msg);
585 sprintf(buf, "<%s>", Messages[m_cur_msg].name);
586 update_sexp_references(Messages[m_cur_msg].name, buf, OPF_MESSAGE);
588 for (i=m_cur_msg; i<Num_messages-1; i++)
589 Messages[i] = Messages[i + 1];
592 if (m_cur_msg >= Num_messages)
593 m_cur_msg = Num_messages - 1;
595 GetDlgItem(IDC_NEW)->EnableWindow(TRUE);
597 update_cur_message();
600 void CMessageEditorDlg::OnNew()
602 if (update(m_cur_msg))
605 Assert(Num_messages < MAX_MISSION_MESSAGES);
606 strcpy(Messages[Num_messages].name, "<new message>");
607 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->AddString("<new message>");
609 strcpy(Messages[Num_messages].message, "<put description here>");
610 Messages[Num_messages].avi_info.name = NULL;
611 Messages[Num_messages].wave_info.name = NULL;
612 Messages[Num_messages].persona_index = -1;
613 m_cur_msg = Num_messages++;
614 if (Num_messages >= MAX_MISSION_MESSAGES)
615 GetDlgItem(IDC_NEW)->EnableWindow(FALSE);
618 update_cur_message();
621 void CMessageEditorDlg::OnClose()
625 modified = query_modified();
627 z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL);
637 theApp.record_window_data(&Messages_wnd_data, this);
638 delete Message_editor_dlg;
639 Message_editor_dlg = NULL;
642 void CMessageEditorDlg::OnBrowseAvi()
647 CFileDialog dlg(TRUE, "ani", m_avi_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
648 "Ani Files (*.ani)|*.ani|Avi Files (*.avi)|*.avi|Both (*.ani, *.avi)|*.ani;*.avi||");
650 if (dlg.DoModal() == IDOK)
652 m_avi_filename = dlg.GetFileName();
658 void CMessageEditorDlg::OnBrowseWave()
663 CFileDialog dlg(TRUE, "wav", m_wave_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
664 "Wave Files (*.wav)|*.wav||");
666 if (dlg.DoModal() == IDOK)
668 m_wave_filename = dlg.GetFileName();
674 void CMessageEditorDlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
676 m_tree.right_clicked();
680 void CMessageEditorDlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
682 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
685 if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1) {
688 edit = m_tree.GetEditControl();
690 edit->SetLimitText(NAME_LENGTH - 1);
696 void CMessageEditorDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
698 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
700 *pResult = m_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText);