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