1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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 the
6  * source.
7  *
8  */
9 
10 
11 
12 #include "stdafx.h"
13 #include "FRED.h"
14 
15 #include "MainFrm.h"
16 #include "FREDDoc.h"
17 #include "FREDView.h"
18 #include "MessageEditorDlg.h"
19 #include "ShipClassEditorDlg.h"
20 #include "MissionNotesDlg.h"
21 #include "Grid.h"
22 #include "dialog1.h"
23 
24 #include "species_defs/species_defs.h"
25 #include "iff_defs/iff_defs.h"
26 
27 #ifdef _DEBUG
28 #undef THIS_FILE
29 static char THIS_FILE[] = __FILE__;
30 #endif
31 
32 IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
33 
34 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
35 	ON_MESSAGE(WM_MENU_POPUP_EDIT, OnMenuPopupTest)
36 	ON_CBN_SELCHANGE(ID_NEW_SHIP_TYPE, OnNewShipTypeChange)
37 
38 	//{{AFX_MSG_MAP(CMainFrame)
39 	ON_WM_CREATE()
40 	ON_COMMAND(ID_FILE_MISSIONNOTES, OnFileMissionnotes)
41 	ON_WM_LBUTTONUP()
42 	ON_WM_DESTROY()
43 	ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar)
44 	ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR, OnUpdateViewStatusBar)
45 	ON_UPDATE_COMMAND_UI(ID_INDICATOR_LEFT, OnUpdateLeft)
46 	ON_UPDATE_COMMAND_UI(ID_INDICATOR_RIGHT, OnUpdateRight)
47 	ON_COMMAND(ID_MIKE_GRIDCONTROL, OnMikeGridcontrol)
48 	ON_COMMAND(IDR_MENU_POPUP_TOGGLE1, OnMenuPopupToggle1)
49 	ON_UPDATE_COMMAND_UI(IDR_MENU_POPUP_TOGGLE1, OnUpdateMenuPopupToggle1)
50 	ON_WM_RBUTTONDOWN()
51 	ON_COMMAND(ID_HELP_INPUT_INTERFACE, OnHelpInputInterface)
52 	ON_WM_CLOSE()
53 	ON_WM_INITMENU()
54 	ON_COMMAND(ID_HELP_FINDER, OnFredHelp)
55 	ON_COMMAND(ID_HELP, OnFredHelp)
56 	//ON_COMMAND(ID_CONTEXT_HELP, OnFredHelp)
57 	//ON_COMMAND(ID_DEFAULT_HELP, OnFredHelp)
58 	//}}AFX_MSG_MAP
59 	// Global help commands
60 END_MESSAGE_MAP()
61 
62 #define FRED_HELP_URL "\\data\\freddocs\\index.html"
63 
64 static UINT indicators[] =
65 {
66 	ID_SEPARATOR,           // status line indicator
67 	ID_SEPARATOR,
68 	ID_SEPARATOR,
69 	ID_INDICATOR_MODIFIED,
70 	ID_SEPARATOR,
71 	//	ID_INDICATOR_LEFT,
72 	//	ID_INDICATOR_RIGHT,
73 	//	ID_INDICATOR_CAPS,
74 	//	ID_INDICATOR_NUM,
75 	//	ID_INDICATOR_SCRL,
76 };
77 
78 CMainFrame *Fred_main_wnd;
79 color_combo_box m_new_ship_type_combo_box;
80 int Toggle1_var = 0;
81 CPoint Global_point2;
82 
83 /**
84  * @brief Launches the default browser to open the given URL
85  */
86 void url_launch(const char *url);
87 
88 
89 #ifdef _DEBUG
AssertValid() const90 void CMainFrame::AssertValid() const {
91 	CFrameWnd::AssertValid();
92 }
93 
Dump(CDumpContext & dc) const94 void CMainFrame::Dump(CDumpContext& dc) const {
95 	CFrameWnd::Dump(dc);
96 }
97 #endif // _DEBUG
98 
CMainFrame()99 CMainFrame::CMainFrame() {}
100 
~CMainFrame()101 CMainFrame::~CMainFrame() {}
102 
init_tools()103 void CMainFrame::init_tools() {
104 	//int highest_terran_index;
105 	//char ship_name[256];
106 	//int ship_index;
107 
108 	// some bizarre Volition check:
109 	static int count = 0;
110 	count++;
111 	if (count == 1) {
112 		return;
113 	} else if (count >= 3) {
114 		Warning(LOCATION, "CMainFrame::init_tools was called more than twice!  Trace out and fix.");
115 		return;
116 	}
117 
118     for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
119         // don't add the pirate ship
120         if (it->flags[Ship::Info_Flags::No_fred]) {
121             m_new_ship_type_combo_box.AddString("");
122             continue;
123         }
124 
125         m_new_ship_type_combo_box.AddString(it->name);
126     }
127 
128 	//	m_new_ship_type_combo_box.AddString("Player Start");
129 	m_new_ship_type_combo_box.AddString("Jump Node");
130 	m_new_ship_type_combo_box.AddString("Waypoint");
131 
132 	/*
133 	// now we want to sort special ships (mission disk) ----------------------
134 	highest_terran_index = 0;
135 	memset(ship_name, 0, 256);
136 	while(m_new_ship_type_combo_box.GetLBText(highest_terran_index, ship_name) != CB_ERR){
137 	ship_index = ship_info_lookup(ship_name);
138 	if((ship_index < 0) || (ship_index >= Num_ship_classes) || (Ship_info[ship_index].species != 0)){
139 	break;
140 	}
141 	highest_terran_index++;
142 	}
143 	*/
144 	m_new_ship_type_combo_box.SetCurSel(0);
145 }
146 
OnClose()147 void CMainFrame::OnClose()
148 {
149 	// CFrameWnd::OnClose() doesn't provide a way for the caller to tell that the close has been cancelled,
150 	// so let's extract that particular logic and do it manually here
151 	if (!FREDDoc_ptr->SaveModified())
152 		return;
153 
154 	// and now if we *are* closing, prevent the dialog from coming up
155 	FREDDoc_ptr->SetModifiedFlag(FALSE);
156 
157 	// do the closing stuff
158 	theApp.write_ini_file();
159 	SaveBarState("Tools state");
160 	CFrameWnd::OnClose();
161 	gr_close();
162 }
163 
OnCreate(LPCREATESTRUCT lpCreateStruct)164 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
165 	int z;
166 	CRect rect;
167 
168 	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
169 		return -1;
170 
171 	if (!m_wndToolBar.Create(this) ||
172 		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) {
173 		TRACE0("Failed to create toolbar\n");
174 		return -1;      // fail to create
175 	}
176 
177 	// Create the combo box
178 	z = m_wndToolBar.CommandToIndex(ID_NEW_SHIP_TYPE);
179 	Assert(z != -1);
180 	m_wndToolBar.SetButtonInfo(z, ID_NEW_SHIP_TYPE, TBBS_SEPARATOR, 230);
181 
182 	// Design guide advises 12 pixel gap between combos and buttons
183 	//	m_wndToolBar.SetButtonInfo(1, ID_SEPARATOR, TBBS_SEPARATOR, 12);
184 	m_wndToolBar.GetItemRect(z, &rect);
185 	rect.top = 3;
186 	rect.bottom = rect.top + 550;
187 	if (!m_new_ship_type_combo_box.Create(CBS_DROPDOWNLIST | WS_VISIBLE | WS_VSCROLL | CBS_HASSTRINGS | LBS_OWNERDRAWFIXED,
188 		rect, &m_wndToolBar, ID_NEW_SHIP_TYPE)) {
189 		TRACE0("Failed to create new ship type combo-box\n");
190 		return FALSE;
191 	}
192 
193 	/*	if (!m_wndStatusBar.Create(this) ||
194 			!m_wndStatusBar.SetIndicators(indicators,
195 			sizeof(indicators)/sizeof(UINT)))
196 			{
197 			TRACE0("Failed to create status bar\n");
198 			return -1;      // fail to create
199 			}
200 			*/
201 
202 	/*	if (!m_wndStatusBar.Create(this,
203 			WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, ID_MY_STATUS_BAR) ||
204 			!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)))*/
205 
206 	if (!m_wndStatusBar.Create(this) ||
207 		!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators) / sizeof(UINT))) {
208 		TRACE0("Failed to create status bar\n");
209 		return -1;
210 
211 	} else {
212 		m_wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0);
213 		m_wndStatusBar.SetPaneInfo(1, 0, SBPS_NORMAL, 80);
214 		m_wndStatusBar.SetPaneInfo(2, 0, SBPS_NORMAL, 180);
215 		//		m_wndStatusBar.SetPaneInfo(3, 0, SBPS_NORMAL, 100);
216 		m_wndStatusBar.SetPaneInfo(4, 0, SBPS_NORMAL, 130);
217 	}
218 
219 	// TODO: Remove this if you don't want tool tips or a resizeable toolbar
220 	m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
221 							 CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
222 
223 	// TODO: Delete these three lines if you don't want the toolbar to
224 	//  be dockable
225 	m_wndToolBar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);
226 	EnableDocking(CBRS_ALIGN_ANY);
227 	DockControlBar(&m_wndToolBar);
228 
229 	Fred_main_wnd = this;
230 	Ship_editor_dialog.Create();
231 	Wing_editor_dialog.Create();
232 	Waypoint_editor_dialog.Create();
233 	init_tools();
234 	LoadBarState("Tools state");
235 	return 0;
236 }
237 
OnDestroy()238 void CMainFrame::OnDestroy() {
239 	Fred_main_wnd = NULL;
240 	CFrameWnd::OnDestroy();
241 }
242 
OnFileMissionnotes()243 void CMainFrame::OnFileMissionnotes() {
244 	CMissionNotesDlg	dlg;
245 
246 	dlg.DoModal();
247 }
248 
OnFredHelp()249 void CMainFrame::OnFredHelp() {
250 	auto res = cf_find_file_location("index.html", CF_TYPE_FREDDOCS);
251 	if (!res.found) {
252 		ReleaseWarning(LOCATION, "Could not find FRED help files!");
253 		return;
254 	}
255 
256 	if (res.offset != 0) {
257 		// We need an actual file location so VP files are not valid
258 		Error(LOCATION, "The FRED documentation was found in a pack (VP) file. This is not valid!");
259 		return;
260 	}
261 
262 	// shell_open url
263 	url_launch(res.full_name.c_str());
264 }
265 
OnHelpInputInterface()266 void CMainFrame::OnHelpInputInterface() {
267 	dialog1	dlg;
268 
269 	dlg.DoModal();
270 }
271 
OnInitMenu(CMenu * pMenu)272 void CMainFrame::OnInitMenu(CMenu* pMenu) {
273 	int i;
274 	CString str;
275 	extern int ID_SHOW_IFF[MAX_IFFS];
276 
277 	if (Undo_available && !FREDDoc_ptr->undo_desc[1].IsEmpty())
278 		str = "Undo " + FREDDoc_ptr->undo_desc[1] + "\tCtrl+Z";
279 	else
280 		str = "Undo\tCtrl+Z";
281 
282 	if (pMenu->GetMenuState(ID_EDIT_UNDO, MF_BYCOMMAND) != -1)
283 		pMenu->ModifyMenu(ID_EDIT_UNDO, MF_BYCOMMAND, ID_EDIT_UNDO, str);
284 
285 	// Goober5000 - do the IFF menu options
286 	for (i = 0; i < MAX_IFFS; i++) {
287 		if (i < Num_iffs) {
288 			char text[NAME_LENGTH + 7];
289 			sprintf(text, "Show %s", Iff_info[i].iff_name);
290 
291 			pMenu->ModifyMenu(ID_SHOW_IFF[i], MF_BYCOMMAND | MF_STRING, ID_SHOW_IFF[i], text);
292 		} else {
293 			pMenu->DeleteMenu(ID_SHOW_IFF[i], MF_BYCOMMAND);
294 		}
295 	}
296 
297 	CFrameWnd::OnInitMenu(pMenu);
298 }
299 
OnLButtonUp(UINT nFlags,CPoint point)300 void CMainFrame::OnLButtonUp(UINT nFlags, CPoint point) {
301 	CFrameWnd::OnLButtonUp(nFlags, point);
302 }
303 
OnMenuPopupTest(WPARAM wParam,LPARAM lParam)304 LRESULT CMainFrame::OnMenuPopupTest(WPARAM wParam, LPARAM lParam) {
305 	CMenu	menu;
306 	CPoint	point;
307 
308 	point = *((CPoint*) lParam);
309 
310 	ClientToScreen(&point);
311 
312 	menu.LoadMenu(IDR_MENU1);
313 	menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
314 
315 	return 0L;
316 }
317 
OnMenuPopupToggle1()318 void CMainFrame::OnMenuPopupToggle1() {
319 	if (Toggle1_var == 0)
320 		Toggle1_var = 1;
321 	else
322 		Toggle1_var = 0;
323 
324 }
325 
OnMikeGridcontrol()326 void CMainFrame::OnMikeGridcontrol() {
327 	CGrid	dlg;
328 
329 	dlg.DoModal();
330 }
331 
OnNewShipTypeChange()332 void CMainFrame::OnNewShipTypeChange() {
333 	if (Fred_view_wnd)
334 		Fred_view_wnd->SetFocus();
335 }
336 
OnRButtonDown(UINT nFlags,CPoint point)337 void CMainFrame::OnRButtonDown(UINT nFlags, CPoint point) {
338 	Global_point2 = point;
339 
340 	PostMessage(WM_MENU_POPUP_TEST, nFlags, (int) &Global_point2);
341 	CFrameWnd::OnRButtonDown(nFlags, point);
342 }
343 
OnUpdateLeft(CCmdUI * pCmdUI)344 void CMainFrame::OnUpdateLeft(CCmdUI* pCmdUI) {
345 	pCmdUI->Enable(::GetKeyState(VK_LBUTTON) < 0);
346 }
347 
OnUpdateMenuPopupToggle1(CCmdUI * pCmdUI)348 void CMainFrame::OnUpdateMenuPopupToggle1(CCmdUI* pCmdUI) {
349 	pCmdUI->SetCheck(Toggle1_var);
350 }
351 
OnUpdateRight(CCmdUI * pCmdUI)352 void CMainFrame::OnUpdateRight(CCmdUI* pCmdUI) {
353 	pCmdUI->Enable(::GetKeyState(VK_RBUTTON) < 0);
354 }
355 
OnUpdateViewStatusBar(CCmdUI * pCmdUI)356 void CMainFrame::OnUpdateViewStatusBar(CCmdUI* pCmdUI) {
357 	pCmdUI->SetCheck((m_wndStatusBar.GetStyle() & WS_VISIBLE) != 0);
358 }
359 
OnViewStatusBar()360 void CMainFrame::OnViewStatusBar() {
361 	m_wndStatusBar.ShowWindow((m_wndStatusBar.GetStyle() & WS_VISIBLE) == 0);
362 	RecalcLayout();
363 }
364 
PreCreateWindow(CREATESTRUCT & cs)365 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) {
366 	// TODO: Modify the Window class or styles here by modifying
367 	//  the CREATESTRUCT cs
368 
369 	return CFrameWnd::PreCreateWindow(cs);
370 }
371 
372 
CalcMinimumItemHeight()373 int color_combo_box::CalcMinimumItemHeight() {
374 	int nResult = 1;
375 
376 	if ((GetStyle() & (LBS_HASSTRINGS | LBS_OWNERDRAWFIXED)) ==
377 		(LBS_HASSTRINGS | LBS_OWNERDRAWFIXED)) {
378 		CClientDC dc(this);
379 		CFont* pOldFont = dc.SelectObject(GetFont());
380 		TEXTMETRIC tm;
381 		VERIFY(dc.GetTextMetrics(&tm));
382 		dc.SelectObject(pOldFont);
383 
384 		nResult = tm.tmHeight;
385 	}
386 
387 	return nResult;
388 }
389 
DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)390 void color_combo_box::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
391 	int m_cyText = 24, z;
392 	CString strText;
393 	char ship_name[256];
394 
395 	// You must override DrawItem and MeasureItem for LBS_OWNERDRAWVARIABLE
396 	ASSERT((GetStyle() & (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS)) ==
397 		   (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS));
398 
399 	CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
400 
401 	// I think we need to do a lookup by ship name here
402 	if (lpDrawItemStruct->itemID >= Ship_info.size()) {
403 		z = lpDrawItemStruct->itemID;
404 	} else {
405 		memset(ship_name, 0, 256);
406 		GetLBText(lpDrawItemStruct->itemID, ship_name);
407 		z = ship_info_lookup(ship_name);
408 	}
409 
410 	if ((z >= 0) && (lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT))) {
411 		int cyItem = GetItemHeight(z);
412 		BOOL fDisabled = !IsWindowEnabled();
413 
414 		COLORREF newTextColor = RGB(0x80, 0x80, 0x80);  // light gray
415 		if (!fDisabled) {
416 			if (z >= ship_info_size())
417 				newTextColor = RGB(0, 0, 0);
418 			else {
419 				species_info *sinfo = &Species_info[Ship_info[z].species];
420 				newTextColor = RGB(sinfo->fred_color.rgb.r, sinfo->fred_color.rgb.g, sinfo->fred_color.rgb.b);
421 			}
422 		}
423 
424 		COLORREF oldTextColor = pDC->SetTextColor(newTextColor);
425 		COLORREF newBkColor = GetSysColor(COLOR_WINDOW);
426 		COLORREF oldBkColor = pDC->SetBkColor(newBkColor);
427 
428 		if (newTextColor == newBkColor)
429 			newTextColor = RGB(0xC0, 0xC0, 0xC0);   // dark gray
430 
431 		if (!fDisabled && ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0)) {
432 			pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT));
433 			pDC->SetBkColor(GetSysColor(COLOR_HIGHLIGHT));
434 		}
435 
436 		if (m_cyText == 0)
437 			VERIFY(cyItem >= CalcMinimumItemHeight());
438 
439 		if (z == Id_select_type_jump_node)
440 			strText = _T("Jump Node");
441 		else if (z == Id_select_type_start)
442 			strText = _T("Player Start");
443 		else if (z == Id_select_type_waypoint)
444 			strText = _T("Waypoint");
445 		else
446 			strText = _T(Ship_info[z].name);
447 		//		GetLBText(lpDrawItemStruct->itemID, strText);
448 
449 		pDC->ExtTextOut(lpDrawItemStruct->rcItem.left,
450 			lpDrawItemStruct->rcItem.top + std::max(0, (cyItem - m_cyText) / 2),
451 			ETO_OPAQUE, &(lpDrawItemStruct->rcItem), strText, strText.GetLength(), NULL);
452 
453 		pDC->SetTextColor(oldTextColor);
454 		pDC->SetBkColor(oldBkColor);
455 	}
456 
457 	if ((lpDrawItemStruct->itemAction & ODA_FOCUS) != 0)
458 		pDC->DrawFocusRect(&(lpDrawItemStruct->rcItem));
459 }
460 
GetCurSelNEW()461 int color_combo_box::GetCurSelNEW() {
462 	int cur_sel;
463 	int ship_info;
464 	char ship_name[256];
465 	char *hmmm = ship_name;
466 
467 	// see if we have a special item (>= Ship_info.size())
468 	cur_sel = GetCurSel();
469 	if (cur_sel >= ship_info_size()) {
470 		return cur_sel;
471 	}
472 
473 	// otherwise lookup the ship by name
474 	memset(ship_name, 0, 256);
475 	if (GetLBText(cur_sel, hmmm) == CB_ERR) {
476 		return CB_ERR;
477 	}
478 	ship_info = ship_info_lookup(ship_name);
479 	if ((ship_info < 0) || (ship_info >= ship_info_size())) {
480 		return CB_ERR;
481 	}
482 	return ship_info;
483 }
484 
MeasureItem(LPMEASUREITEMSTRUCT)485 void color_combo_box::MeasureItem(LPMEASUREITEMSTRUCT) {
486 	// You must override DrawItem and MeasureItem for LBS_OWNERDRAWVARIABLE
487 	ASSERT((GetStyle() & (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS)) ==
488 		   (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS));
489 }
490 
SetCurSelNEW(int model_index)491 int color_combo_box::SetCurSelNEW(int model_index) {
492 	if ((model_index < 0) || (model_index >= ship_info_size())) {
493 		return SetCurSel(model_index);
494 	}
495 
496 	// lookup the ship name
497 	return FindString(0, Ship_info[model_index].name);
498 }
499 
500 
url_launch(const char * url)501 void url_launch(const char *url) {
502 	int r;
503 
504 	r = (int) ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOW);
505 	if (r < 32) {
506 		const char *txt = NULL;
507 
508 		switch (r) {
509 		case 0:	txt = XSTR("The operating system is out of memory or resources.", 1107); break;
510 		case ERROR_BAD_FORMAT: txt = XSTR("The .EXE file is invalid (non-Win32 .EXE or error in .EXE image).", 1108); break;
511 		case SE_ERR_ACCESSDENIED: txt = XSTR("The operating system denied access to the specified file. ", 1109); break;
512 		case SE_ERR_ASSOCINCOMPLETE: txt = XSTR("The filename association is incomplete or invalid.\r\n(You need to have a default Internet browser installed)", 1110); break;
513 		case SE_ERR_DDEBUSY: txt = XSTR("The DDE transaction could not be completed because other DDE transactions were being processed.", 1111); break;
514 		case SE_ERR_DDEFAIL: txt = XSTR("The DDE transaction failed.", 1112); break;
515 		case SE_ERR_DDETIMEOUT: txt = XSTR("The DDE transaction could not be completed because the request timed out.", 1113); break;
516 		case SE_ERR_DLLNOTFOUND: txt = XSTR("The specified dynamic-link library was not found.", 1114); break;
517 		case SE_ERR_OOM: txt = XSTR("There was not enough memory to complete the operation.", 1115); break;
518 		case SE_ERR_SHARE: txt = XSTR("A sharing violation occurred.", 1116); break;
519 
520 			// No browser installed message
521 		case SE_ERR_NOASSOC:
522 		case ERROR_FILE_NOT_FOUND:
523 		case ERROR_PATH_NOT_FOUND: txt = XSTR("\r\nUnable to locate Fred Help file: \\data\\freddocs\\index.html\r\n", 1479); break;
524 
525 		default: txt = XSTR("Unknown error occurred.", 1118); break;
526 		}
527 		AfxMessageBox(txt, MB_OK | MB_ICONERROR);
528 	}
529 }
530