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.1 2002/05/03 03:28:08 root
16 * 2 10/07/98 6:28p Dave
17 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
18 * Fred. Globalized mission and campaign file extensions. Removed Silent
19 * Threat specific code.
21 * 1 10/07/98 3:01p Dave
23 * 1 10/07/98 3:00p Dave
25 * 26 4/07/98 10:51a Allender
26 * remove any allied from message senders. Make heads for mission
27 * specific messages play appropriately
29 * 25 1/07/98 5:58p Hoffoss
30 * Combined message editor into event editor.
32 * 24 1/06/98 4:19p Hoffoss
33 * Made message editor accept returns instead of closing dialog.
35 * 23 12/30/97 9:39a Allender
36 * minor fixups with message editing
38 * 22 11/17/97 4:57p Allender
39 * added persona support in FreeSpace. A couple of new messages for
40 * message.tbl which Dan didn't have
42 * 21 10/16/97 8:54p Duncan
43 * Fixed bug in adding new message.
45 * 20 10/14/97 10:31a Allender
46 * disable persona field for builtin messages
48 * 19 10/13/97 11:37a Allender
49 * added personas to message editor in Fred
51 * 18 10/10/97 6:21p Hoffoss
52 * Put in Fred support for training object list editing.
54 * 17 10/08/97 7:52p Hoffoss
55 * Made renaming events update all sexp references to it (added a
56 * generalized function to replace all references to a name by argument
59 * 16 10/08/97 4:41p Hoffoss
60 * Changed the way message editor works. Each message is updated
61 * perminently when you switch messages (as if ok button was pressed).
63 * 15 9/09/97 9:19a Hoffoss
64 * Fixed bug in Fred: messages shouldn't be send from the player to the
67 * 14 8/08/97 3:29p Duncan
68 * Fixed bug with strings exceeding their limits.
70 * 13 8/05/97 5:12p Jasen
71 * Added advanced_stricmp() function to handle NULL pointers gracefully,
72 * and utilized it in message editor query update function.
74 * 12 8/01/97 3:47p Hoffoss
75 * Fixed bug where message editor believes modifications occured when
76 * nothing actually changed.
78 * 11 7/24/97 4:34p Hoffoss
81 * 10 7/16/97 6:30p Hoffoss
82 * Added icons to sexp trees, mainly because I think they will be required
85 * 9 7/14/97 9:55p Hoffoss
86 * Making changes to message editor system.
88 * 8 7/10/97 2:32p Hoffoss
89 * Made message editor dialog box modeless.
91 * 7 7/02/97 5:09p Hoffoss
92 * Added browse buttons to message editor.
94 * 6 5/20/97 2:28p Hoffoss
95 * Added message box queries for close window operation on all modal
98 * 5 4/17/97 2:01p Hoffoss
99 * All dialog box window states are saved between sessions now.
101 * 4 4/01/97 5:15p Hoffoss
102 * Fixed errors in max length checks, renaming a wing now renames the
103 * ships in the wing as well, as it should.
105 * 3 3/11/97 2:19p Hoffoss
106 * New message structure support for Fred.
108 * 2 2/17/97 5:28p Hoffoss
109 * Checked RCS headers, added them were missing, changing description to
110 * something better, etc where needed.
117 #include "messageeditordlg.h"
119 #include "management.h"
120 #include "sexp_tree.h"
121 #include "eventeditor.h"
122 #include "missionmessage.h"
125 #define new DEBUG_NEW
127 static char THIS_FILE[] = __FILE__;
130 CMessageEditorDlg *Message_editor_dlg = NULL;
132 /////////////////////////////////////////////////////////////////////////////
133 // CMessageEditorDlg dialog
135 CMessageEditorDlg::CMessageEditorDlg(CWnd* pParent /*=NULL*/)
136 : CDialog(CMessageEditorDlg::IDD, pParent)
138 //{{AFX_DATA_INIT(CMessageEditorDlg)
139 m_avi_filename = _T("");
140 m_wave_filename = _T("");
141 m_message_text = _T("");
142 m_message_name = _T("");
149 m_tree.link_modified(&modified);
154 void CMessageEditorDlg::DoDataExchange(CDataExchange* pDX)
156 CDialog::DoDataExchange(pDX);
157 //{{AFX_DATA_MAP(CMessageEditorDlg)
158 DDX_Control(pDX, IDC_TREE, m_tree);
159 DDX_CBString(pDX, IDC_AVI_FILENAME, m_avi_filename);
160 DDX_CBString(pDX, IDC_WAVE_FILENAME, m_wave_filename);
161 DDX_Text(pDX, IDC_MESSAGE_TEXT, m_message_text);
162 DDX_Text(pDX, IDC_NAME, m_message_name);
163 DDX_LBIndex(pDX, IDC_MESSAGE_LIST, m_cur_msg);
164 DDX_CBIndex(pDX, IDC_PRIORITY, m_priority);
165 DDX_CBIndex(pDX, IDC_SENDER, m_sender);
166 DDX_CBIndex(pDX, IDC_PERSONA_NAME, m_persona);
168 DDV_MaxChars(pDX, m_message_name, NAME_LENGTH - 1);
169 DDV_MaxChars(pDX, m_message_text, MESSAGE_LENGTH - 1);
170 DDV_MaxChars(pDX, m_avi_filename, MAX_FILENAME_LEN - 1);
171 DDV_MaxChars(pDX, m_wave_filename, MAX_FILENAME_LEN - 1);
174 BEGIN_MESSAGE_MAP(CMessageEditorDlg, CDialog)
175 //{{AFX_MSG_MAP(CMessageEditorDlg)
176 ON_LBN_SELCHANGE(IDC_MESSAGE_LIST, OnSelchangeMessageList)
177 ON_EN_UPDATE(IDC_NAME, OnUpdateName)
178 ON_BN_CLICKED(IDC_DELETE, OnDelete)
179 ON_BN_CLICKED(IDC_NEW, OnNew)
181 ON_BN_CLICKED(IDC_BROWSE_AVI, OnBrowseAvi)
182 ON_BN_CLICKED(IDC_BROWSE_WAVE, OnBrowseWave)
183 ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree)
184 ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree)
185 ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree)
186 ON_BN_CLICKED(ID_OK, OnOk)
190 /////////////////////////////////////////////////////////////////////////////
191 // CMessageEditorDlg message handlers
193 BOOL CMessageEditorDlg::OnInitDialog()
199 CDialog::OnInitDialog();
200 theApp.init_window(&Messages_wnd_data, this);
203 ((CEdit *) GetDlgItem(IDC_NAME))->LimitText(NAME_LENGTH - 1);
204 ((CEdit *) GetDlgItem(IDC_MESSAGE_TEXT))->LimitText(MESSAGE_LENGTH - 1);
205 ((CComboBox *) GetDlgItem(IDC_AVI_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
206 ((CComboBox *) GetDlgItem(IDC_WAVE_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
208 list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
209 list->ResetContent();
210 for (i=0; i<Num_messages; i++) {
211 //Assert(list->FindStringExact(-1, Messages[i].name) == CB_ERR);
212 // mwa we should probably not include builtin messages into this list!
213 list->AddString(Messages[i].name);
216 box = (CComboBox *) GetDlgItem(IDC_AVI_FILENAME);
217 for (i=0; i<Num_messages; i++)
218 if (Messages[i].avi_info.name)
219 if (box->FindStringExact(-1, Messages[i].avi_info.name) == CB_ERR)
220 box->AddString(Messages[i].avi_info.name);
222 box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME);
223 for (i=0; i<Num_messages; i++)
224 if (Messages[i].wave_info.name)
225 if (box->FindStringExact(i, Messages[i].wave_info.name) == CB_ERR)
226 box->AddString(Messages[i].wave_info.name);
228 // add the persona names into the combo box
229 box = (CComboBox *)GetDlgItem(IDC_PERSONA_NAME);
231 box->AddString("<None>");
232 for (i = 0; i < Num_personas; i++ )
233 box->AddString( Personas[i].name );
235 box = (CComboBox *) GetDlgItem(IDC_SENDER);
236 for (i=0; i<MAX_SHIPS; i++)
237 if ((Ships[i].objnum >= 0) && (Objects[Ships[i].objnum].type == OBJ_SHIP))
238 box->AddString(Ships[i].ship_name);
240 for (i=0; i<MAX_WINGS; i++)
241 if (Wings[i].wave_count)
242 box->AddString(Wings[i].name);
244 box->AddString("<Any wingman>");
246 // MWA 4/7/98 -- removed any allied
247 //box->AddString("<Any allied>");
249 // set the first message to be the first non-builtin message (if it exists)
250 if ( Num_messages > Num_builtin_messages )
251 m_cur_msg = Num_builtin_messages;
252 else if (Num_messages)
257 if (Num_messages >= MAX_MISSION_MESSAGES)
258 GetDlgItem(IDC_NEW)->EnableWindow(FALSE);
260 update_cur_message();
264 int CMessageEditorDlg::query_modified()
266 char *ptr, buf[MESSAGE_LENGTH];
276 ptr = (char *) (LPCTSTR) m_message_name;
277 for (i=0; i<Num_messages; i++)
278 if ((i != m_cur_msg) && (!stricmp(ptr, Messages[i].name)))
281 if (i < Num_messages)
282 if (stricmp(ptr, Messages[m_cur_msg].name))
283 return 1; // name is different and allowed to update
285 string_copy(buf, m_message_text, MESSAGE_LENGTH - 1);
286 if (stricmp(buf, Messages[m_cur_msg].message))
289 ptr = (char *) (LPCTSTR) m_avi_filename;
290 if (advanced_stricmp(ptr, Messages[m_cur_msg].avi_info.name))
293 ptr = (char *) (LPCTSTR) m_wave_filename;
294 if (advanced_stricmp(ptr, Messages[m_cur_msg].wave_info.name))
297 // check to see if persona changed. use -1 since we stuck a "None" for persona
298 // at the beginning of the list.
299 if ( (m_persona - 1 ) != Messages[m_cur_msg].persona_index )
302 if (m_tree.query_false()) {
303 if (m_event_num >= 0)
314 void CMessageEditorDlg::OnOK()
318 void CMessageEditorDlg::OnOk()
321 theApp.record_window_data(&Messages_wnd_data, this);
322 delete Message_editor_dlg;
323 Message_editor_dlg = NULL;
326 void CMessageEditorDlg::OnCancel()
328 theApp.record_window_data(&Messages_wnd_data, this);
329 delete Message_editor_dlg;
330 Message_editor_dlg = NULL;
333 // load controls with structure data
334 void CMessageEditorDlg::update_cur_message()
336 int node, enable = TRUE, enable2 = TRUE;
340 enable = enable2 = FALSE;
341 m_message_name = _T("");
342 m_message_text = _T("");
343 m_avi_filename = _T("");
344 m_wave_filename = _T("");
347 m_sender = m_priority = -1;
350 m_message_name = Messages[m_cur_msg].name;
351 m_message_text = Messages[m_cur_msg].message;
352 if (Messages[m_cur_msg].avi_info.name)
353 m_avi_filename = _T(Messages[m_cur_msg].avi_info.name);
355 m_avi_filename = _T("");
357 if (Messages[m_cur_msg].wave_info.name)
358 m_wave_filename = _T(Messages[m_cur_msg].wave_info.name);
360 m_wave_filename = _T("");
363 if ( Messages[m_cur_msg].persona_index != -1 )
364 m_persona = Messages[m_cur_msg].persona_index + 1; // add one for the "none" at the beginning of the list
368 m_event_num = find_event();
369 if (m_event_num < 0) {
371 m_sender = m_priority = 0;
374 node = CADR(Mission_events[m_event_num].formula);
376 m_tree.load_tree(node, "false");
379 if (m_cur_msg < Num_builtin_messages)
382 GetDlgItem(IDC_NAME)->EnableWindow(enable);
383 GetDlgItem(IDC_MESSAGE_TEXT)->EnableWindow(enable);
384 GetDlgItem(IDC_AVI_FILENAME)->EnableWindow(enable);
385 GetDlgItem(IDC_WAVE_FILENAME)->EnableWindow(enable);
386 GetDlgItem(IDC_DELETE)->EnableWindow(enable);
387 GetDlgItem(IDC_TREE)->EnableWindow(enable2);
388 GetDlgItem(IDC_SENDER)->EnableWindow(enable2);
389 GetDlgItem(IDC_PRIORITY)->EnableWindow(enable2);
390 GetDlgItem(IDC_PERSONA_NAME)->EnableWindow(enable);
394 int CMessageEditorDlg::find_event()
397 int i, formula, node;
400 for (i=0; i<Num_mission_events; i++) {
401 node = Mission_events[i].formula;
402 if (find_operator(CTEXT(node)) == OP_WHEN) {
404 formula = CAR(node); // bool conditional
405 if (CDR(CDR(node)) == -1) { // only 1 action
407 if ((find_operator(CTEXT(node)) == OP_SEND_MESSAGE) && !stricmp(CTEXT(CDR(CDR(CDR(node)))), m_message_name)) {
408 box = (CComboBox *) GetDlgItem(IDC_SENDER);
409 str = CTEXT(CDR(node));
410 m_sender = box->FindStringExact(-1, str);
411 if (m_sender == CB_ERR)
414 box = (CComboBox *) GetDlgItem(IDC_PRIORITY);
415 str = CTEXT(CDR(CDR(node)));
416 m_priority = box->FindStringExact(-1, str);
417 if (m_priority == CB_ERR)
426 m_sender = m_priority = 0;
430 void CMessageEditorDlg::OnSelchangeMessageList()
438 if (update(m_cur_msg)) {
440 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST)) -> SetCurSel(old);
446 update_cur_message();
449 void CMessageEditorDlg::OnUpdateName()
453 int CMessageEditorDlg::update(int num)
455 char *ptr, buf[4096];
462 ptr = (char *) (LPCTSTR) m_message_name;
463 for (i=0; i<Num_messages; i++)
464 if ((i != num) && (!stricmp(m_message_name, Messages[i].name)))
467 if (i == Num_messages) { // update name if no conflicts, otherwise keep old name
468 update_sexp_references(Messages[num].name, ptr, OPF_MESSAGE);
469 string_copy(Messages[num].name, m_message_name, NAME_LENGTH - 1);
471 list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
472 list->DeleteString(num);
473 list->InsertString(num, m_message_name);
476 string_copy(Messages[num].message, m_message_text, MESSAGE_LENGTH - 1);
477 if (Messages[num].avi_info.name)
478 free(Messages[num].avi_info.name);
480 ptr = (char *) (LPCTSTR) m_avi_filename;
481 if (!ptr || !strlen(ptr))
482 Messages[num].avi_info.name = NULL;
484 Messages[num].avi_info.name = strdup(ptr);
486 if (Messages[num].wave_info.name)
487 free(Messages[num].wave_info.name);
489 ptr = (char *) (LPCTSTR) m_wave_filename;
490 if (!ptr || !strlen(ptr))
491 Messages[num].wave_info.name = NULL;
493 Messages[num].wave_info.name = strdup(ptr);
495 // update the persona to the message. We subtract 1 for the "None" at the beginning of the combo
497 Messages[num].persona_index = m_persona - 1;
499 if (m_tree.query_false()) {
500 if (m_event_num >= 0) { // need to delete event
502 free_sexp2(Mission_events[i].formula);
503 Assert(i < Num_mission_events);
504 while (i < Num_mission_events - 1) {
505 Mission_events[i] = Mission_events[i + 1];
509 Num_mission_events--;
514 if (m_event_num >= 0)
515 free_sexp2(Mission_events[m_event_num].formula);
518 if (Num_mission_events == MAX_MISSION_EVENTS) {
519 MessageBox("You have reached the limit on mission events.\n"
520 "Can't add an event to send this message.");
525 Assert(Num_mission_events < MAX_MISSION_EVENTS);
526 m_event_num = Num_mission_events++;
527 string_copy(Mission_events[m_event_num].name, m_message_name, NAME_LENGTH - 1);
528 Mission_events[m_event_num].repeat_count = 1;
529 Mission_events[m_event_num].interval = 1;
530 Mission_events[m_event_num].score = 0;
531 Mission_events[m_event_num].chain_delay = -1;
532 Mission_events[m_event_num].objective_text = NULL;
533 Mission_events[m_event_num].objective_key_text = NULL;
536 fnode = m_tree.save_tree();
537 ptr = (char *) (LPCTSTR) m_message_name;
538 node = alloc_sexp(ptr, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
539 ((CComboBox *) GetDlgItem(IDC_PRIORITY))->GetLBText(m_priority, buf);
540 node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node);
541 ((CComboBox *) GetDlgItem(IDC_SENDER))->GetLBText(m_sender, buf);
542 node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node);
543 node = alloc_sexp("send-message", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node);
544 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
545 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, fnode, node);
546 node = alloc_sexp("when", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node);
547 Mission_events[m_event_num].formula = node;
552 if (query_modified())
559 void CMessageEditorDlg::OnDelete()
564 Assert((m_cur_msg >= 0) && (m_cur_msg < Num_messages));
565 if (Messages[m_cur_msg].avi_info.name)
566 free(Messages[m_cur_msg].avi_info.name);
567 if (Messages[m_cur_msg].wave_info.name)
568 free(Messages[m_cur_msg].wave_info.name);
570 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->DeleteString(m_cur_msg);
571 sprintf(buf, "<%s>", Messages[m_cur_msg].name);
572 update_sexp_references(Messages[m_cur_msg].name, buf, OPF_MESSAGE);
574 for (i=m_cur_msg; i<Num_messages-1; i++)
575 Messages[i] = Messages[i + 1];
578 if (m_cur_msg >= Num_messages)
579 m_cur_msg = Num_messages - 1;
581 GetDlgItem(IDC_NEW)->EnableWindow(TRUE);
583 update_cur_message();
586 void CMessageEditorDlg::OnNew()
588 if (update(m_cur_msg))
591 Assert(Num_messages < MAX_MISSION_MESSAGES);
592 strcpy(Messages[Num_messages].name, "<new message>");
593 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->AddString("<new message>");
595 strcpy(Messages[Num_messages].message, "<put description here>");
596 Messages[Num_messages].avi_info.name = NULL;
597 Messages[Num_messages].wave_info.name = NULL;
598 Messages[Num_messages].persona_index = -1;
599 m_cur_msg = Num_messages++;
600 if (Num_messages >= MAX_MISSION_MESSAGES)
601 GetDlgItem(IDC_NEW)->EnableWindow(FALSE);
604 update_cur_message();
607 void CMessageEditorDlg::OnClose()
611 modified = query_modified();
613 z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL);
623 theApp.record_window_data(&Messages_wnd_data, this);
624 delete Message_editor_dlg;
625 Message_editor_dlg = NULL;
628 void CMessageEditorDlg::OnBrowseAvi()
633 CFileDialog dlg(TRUE, "ani", m_avi_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
634 "Ani Files (*.ani)|*.ani|Avi Files (*.avi)|*.avi|Both (*.ani, *.avi)|*.ani;*.avi||");
636 if (dlg.DoModal() == IDOK)
638 m_avi_filename = dlg.GetFileName();
644 void CMessageEditorDlg::OnBrowseWave()
649 CFileDialog dlg(TRUE, "wav", m_wave_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
650 "Wave Files (*.wav)|*.wav||");
652 if (dlg.DoModal() == IDOK)
654 m_wave_filename = dlg.GetFileName();
660 void CMessageEditorDlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
662 m_tree.right_clicked();
666 void CMessageEditorDlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
668 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
671 if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1) {
674 edit = m_tree.GetEditControl();
676 edit->SetLimitText(NAME_LENGTH - 1);
682 void CMessageEditorDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
684 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
686 *pResult = m_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText);