1 // winmain.cpp
2 //
3 // Copyright (C) 2001-2007, Chris Laurel <claurel@shatters.net>
4 //
5 // Windows front end for Celestia.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11
12 #include <iostream>
13 #include <fstream>
14 #include <sstream>
15 #include <algorithm>
16 #include <set>
17 #include <cstdlib>
18 #include <cctype>
19 #include <cstring>
20 #include <cassert>
21 #include <process.h>
22 #include <time.h>
23 #include <windows.h>
24 #include <commctrl.h>
25 #include <mmsystem.h>
26
27 #include <celmath/vecmath.h>
28 #include <celmath/quaternion.h>
29 #include <celmath/mathlib.h>
30 #include <celutil/debug.h>
31 #include <celutil/util.h>
32 #include <celutil/winutil.h>
33 #include <celutil/filetype.h>
34 #include <celengine/celestia.h>
35 #include <celengine/astro.h>
36 #include <celengine/cmdparser.h>
37 #include <celengine/axisarrow.h>
38 #include <celengine/planetgrid.h>
39
40 #include "../celengine/gl.h"
41 #include "../celengine/glext.h"
42 #include "celestiacore.h"
43 #include "imagecapture.h"
44 #include "avicapture.h"
45 #include "url.h"
46 #include "winstarbrowser.h"
47 #include "winssbrowser.h"
48 #include "wintourguide.h"
49 #include "wingotodlg.h"
50 #include "winviewoptsdlg.h"
51 #include "winlocations.h"
52 #include "winbookmarks.h"
53 #include "wineclipses.h"
54 #include "winhyperlinks.h"
55 #include "wintime.h"
56 #include "winsplash.h"
57 #include "odmenu.h"
58 #include "scriptmenu.h"
59
60 #include "res/resource.h"
61 #include "wglext.h"
62
63 #include <locale.h>
64
65 using namespace std;
66
67 typedef pair<int,string> IntStrPair;
68 typedef vector<IntStrPair> IntStrPairVec;
69
70 char AppName[] = "Celestia";
71
72 static CelestiaCore* appCore = NULL;
73
74 // Display modes for full screen operation
75 static vector<DEVMODE>* displayModes = NULL;
76
77 // Display mode indices
78 static int currentScreenMode = 0;
79 static int newScreenMode = 0;
80
81 // The last fullscreen mode set; saved and restored from the registry
82 static int lastFullScreenMode = 0;
83 // A fullscreen mode guaranteed to work
84 static int fallbackFullScreenMode = 0;
85 static RECT windowRect;
86
87 static HGLRC glContext;
88 static HDC deviceContext;
89
90 static bool bReady = false;
91
92 static LPTSTR CelestiaRegKey = "Software\\Shatters.net\\Celestia";
93
94 HINSTANCE appInstance;
95 HMODULE hRes;
96 HWND mainWindow = 0;
97
98 static SolarSystemBrowser* solarSystemBrowser = NULL;
99 static StarBrowser* starBrowser = NULL;
100 static TourGuide* tourGuide = NULL;
101 static GotoObjectDialog* gotoObjectDlg = NULL;
102 static ViewOptionsDialog* viewOptionsDlg = NULL;
103 static EclipseFinderDialog* eclipseFinder = NULL;
104 static LocationsDialog* locationsDlg = NULL;
105 static SplashWindow* s_splash = NULL;
106
107 static HMENU menuBar = 0;
108 ODMenu odAppMenu;
109 static HACCEL acceleratorTable = 0;
110 static bool hideMenuBar = false;
111
112 // Joystick info
113 static bool useJoystick = false;
114 static bool joystickAvailable = false;
115 static JOYCAPS joystickCaps;
116
117 static HCURSOR hDefaultCursor = 0;
118 bool cursorVisible = true;
119 static POINT saveCursorPos;
120 static POINT lastMouseMove;
121 class WinCursorHandler;
122 WinCursorHandler* cursorHandler = NULL;
123
124 static int MovieSizes[8][2] = {
125 { 160, 120 },
126 { 320, 240 },
127 { 640, 480 },
128 { 720, 480 },
129 { 720, 576 },
130 { 1024, 768 },
131 { 1280, 720 },
132 { 1920, 1080 }
133 };
134
135 static float MovieFramerates[5] = { 15.0f, 24.0f, 25.0f, 29.97f, 30.0f };
136
137 static int movieSize = 1;
138 static int movieFramerate = 1;
139
140
141 astro::Date newTime(0.0);
142
143 #define REFMARKS 1
144
145 #define INFINITE_MOUSE
146 static int lastX = 0;
147 static int lastY = 0;
148 static bool ignoreNextMoveEvent = false;
149
150 static const WPARAM ID_GOTO_URL = 62000;
151
152 HWND hBookmarkTree;
153 char bookmarkName[33];
154
155 static const string ScriptsDirectory = "scripts";
156 static vector<ScriptMenuItem>* ScriptMenuItems = NULL;
157
158
159 static LRESULT CALLBACK MainWindowProc(HWND hWnd,
160 UINT uMsg,
161 WPARAM wParam, LPARAM lParam);
162
163
164 #define MENU_CHOOSE_PLANET 32000
165 #define MENU_CHOOSE_SURFACE 31000
166
167
168 struct AppPreferences
169 {
170 int winWidth;
171 int winHeight;
172 int winX;
173 int winY;
174 int renderFlags;
175 int labelMode;
176 int locationFilter;
177 int orbitMask;
178 float visualMagnitude;
179 float ambientLight;
180 float galaxyLightGain;
181 int showLocalTime;
182 int dateFormat;
183 int hudDetail;
184 int fullScreenMode;
185 uint32 lastVersion;
186 string altSurfaceName;
187 uint32 textureResolution;
188 Renderer::StarStyle starStyle;
189 GLContext::GLRenderPath renderPath;
190 bool renderPathSet;
191 };
192
ChangeDisplayMode()193 void ChangeDisplayMode()
194 {
195 DEVMODE device_mode;
196
197 memset(&device_mode, 0, sizeof(DEVMODE));
198
199 device_mode.dmSize = sizeof(DEVMODE);
200
201 device_mode.dmPelsWidth = 800;
202 device_mode.dmPelsHeight = 600;
203 device_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
204
205 ChangeDisplaySettings(&device_mode, CDS_FULLSCREEN);
206 }
207
RestoreDisplayMode()208 void RestoreDisplayMode()
209 {
210 ChangeDisplaySettings(0, 0);
211 }
212
213 //
214 // A very minimal IDropTarget interface implementation
215 //
216
217 class CelestiaDropTarget : public IDropTarget
218 {
219 public:
220 CelestiaDropTarget();
221 ~CelestiaDropTarget();
222
223 STDMETHOD (QueryInterface)(REFIID idd, void** ppvObject);
224 STDMETHOD_ (ULONG, AddRef) (void);
225 STDMETHOD_ (ULONG, Release) (void);
226
227 // IDropTarget methods
228 STDMETHOD (DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
229 STDMETHOD (DragOver) (DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
230 STDMETHOD (DragLeave)(void);
231 STDMETHOD (Drop) (LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
232
233 private:
234 ULONG refCount;
235 };
236
237 static CelestiaDropTarget* dropTarget = NULL;
238
CelestiaDropTarget()239 CelestiaDropTarget::CelestiaDropTarget() :
240 refCount(0)
241 {
242 }
243
~CelestiaDropTarget()244 CelestiaDropTarget::~CelestiaDropTarget()
245 {
246 }
247
QueryInterface(REFIID iid,void ** ppvObject)248 HRESULT CelestiaDropTarget::QueryInterface(REFIID iid, void** ppvObject)
249 {
250 if (iid == IID_IUnknown || iid == IID_IDropTarget)
251 {
252 *ppvObject = this;
253 AddRef();
254 return ResultFromScode(S_OK);
255 }
256 else
257 {
258 *ppvObject = NULL;
259 return ResultFromScode(E_NOINTERFACE);
260 }
261 }
262
AddRef(void)263 ULONG CelestiaDropTarget::AddRef(void)
264 {
265 return ++refCount;
266 }
267
Release(void)268 ULONG CelestiaDropTarget::Release(void)
269 {
270 if (--refCount == 0)
271 {
272 delete this;
273 return 0;
274 }
275
276 return refCount;
277 }
278
279 STDMETHODIMP
DragEnter(IDataObject * pDataObject,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)280 CelestiaDropTarget::DragEnter(IDataObject* pDataObject,
281 DWORD grfKeyState,
282 POINTL pt,
283 DWORD* pdwEffect)
284 {
285 return S_OK;
286 }
287
288 STDMETHODIMP
DragOver(DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)289 CelestiaDropTarget::DragOver(DWORD grfKeyState,
290 POINTL pt,
291 DWORD* pdwEffect)
292 {
293 return S_OK;
294 }
295
296 STDMETHODIMP
DragLeave(void)297 CelestiaDropTarget::DragLeave(void)
298 {
299 return S_OK;
300 }
301
302
303 STDMETHODIMP
Drop(IDataObject * pDataObject,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)304 CelestiaDropTarget::Drop(IDataObject* pDataObject,
305 DWORD grfKeyState,
306 POINTL pt,
307 DWORD* pdwEffect)
308 {
309
310 IEnumFORMATETC* enumFormat = NULL;
311 HRESULT hr = pDataObject->EnumFormatEtc(DATADIR_GET, &enumFormat);
312 if (FAILED(hr) || enumFormat == NULL)
313 return E_FAIL;
314
315 FORMATETC format;
316 ULONG nFetched;
317 while (enumFormat->Next(1, &format, &nFetched) == S_OK)
318 {
319 char buf[512];
320 if (GetClipboardFormatName(format.cfFormat, buf, 511) != 0 &&
321 !strcmp(buf, "UniformResourceLocator"))
322 {
323 STGMEDIUM medium;
324 if (pDataObject->GetData(&format, &medium) == S_OK)
325 {
326 if (medium.tymed == TYMED_HGLOBAL && medium.hGlobal != 0)
327 {
328 char* s = (char*) GlobalLock(medium.hGlobal);
329 appCore->goToUrl(s);
330 GlobalUnlock(medium.hGlobal);
331 break;
332 }
333 }
334 }
335 }
336
337 enumFormat->Release();
338
339 return E_FAIL;
340 }
341
342
343 // Cursor handler callback class for Windows. We pass an instance to
344 // the app core which the calls the setCursorShape method to change
345 // the cursor icon.
346 class WinCursorHandler : public CelestiaCore::CursorHandler
347 {
348 public:
349 WinCursorHandler(HCURSOR _defaultCursor);
350 virtual ~WinCursorHandler();
351 virtual void setCursorShape(CelestiaCore::CursorShape);
352 virtual CelestiaCore::CursorShape getCursorShape() const;
353
354 private:
355 CelestiaCore::CursorShape shape;
356 HCURSOR defaultCursor;
357 HCURSOR sizeVertical;
358 HCURSOR sizeHorizontal;
359 };
360
361
WinCursorHandler(HCURSOR _defaultCursor)362 WinCursorHandler::WinCursorHandler(HCURSOR _defaultCursor) :
363 shape(CelestiaCore::ArrowCursor),
364 defaultCursor(_defaultCursor)
365 {
366 sizeVertical = LoadCursor(NULL, IDC_SIZENS);
367 sizeHorizontal = LoadCursor(NULL, IDC_SIZEWE);
368 }
369
370
~WinCursorHandler()371 WinCursorHandler::~WinCursorHandler()
372 {
373 }
374
375
setCursorShape(CelestiaCore::CursorShape _shape)376 void WinCursorHandler::setCursorShape(CelestiaCore::CursorShape _shape)
377 {
378 shape = _shape;
379 switch (shape)
380 {
381 case CelestiaCore::SizeVerCursor:
382 SetCursor(sizeVertical);
383 break;
384 case CelestiaCore::SizeHorCursor:
385 SetCursor(sizeHorizontal);
386 break;
387 default:
388 SetCursor(defaultCursor);
389 break;
390 }
391 }
392
393
getCursorShape() const394 CelestiaCore::CursorShape WinCursorHandler::getCursorShape() const
395 {
396 return shape;
397 }
398
399 // end WinCursorHandler methods
400
ShowUniversalTime(CelestiaCore * appCore)401 static void ShowUniversalTime(CelestiaCore* appCore)
402 {
403 appCore->setTimeZoneBias(0);
404 appCore->setTimeZoneName("UTC");
405 }
406
407
ShowLocalTime(CelestiaCore * appCore)408 static void ShowLocalTime(CelestiaCore* appCore)
409 {
410 TIME_ZONE_INFORMATION tzi;
411 DWORD dst = GetTimeZoneInformation(&tzi);
412 if (dst != TIME_ZONE_ID_INVALID)
413 {
414 LONG dstBias = 0;
415 WCHAR* tzName = NULL;
416
417 if (dst == TIME_ZONE_ID_STANDARD)
418 {
419 dstBias = tzi.StandardBias;
420 tzName = tzi.StandardName;
421 }
422 else if (dst == TIME_ZONE_ID_DAYLIGHT)
423 {
424 dstBias = tzi.DaylightBias;
425 tzName = tzi.DaylightName;
426 }
427
428 appCore->setTimeZoneName(" ");
429 appCore->setTimeZoneBias((tzi.Bias + dstBias) * -60);
430 }
431 }
432
433
BeginMovieCapture(const std::string & filename,int width,int height,float framerate)434 static bool BeginMovieCapture(const std::string& filename,
435 int width, int height,
436 float framerate)
437 {
438 MovieCapture* movieCapture = new AVICapture();
439
440 bool success = movieCapture->start(filename, width, height, framerate);
441 if (success)
442 appCore->initMovieCapture(movieCapture);
443 else
444 delete movieCapture;
445
446 return success;
447 }
448
449
CopyStateURLToClipboard()450 static bool CopyStateURLToClipboard()
451 {
452 BOOL b;
453 b = OpenClipboard(mainWindow);
454 if (!b)
455 return false;
456
457 CelestiaState appState;
458 appState.captureState(appCore);
459
460 Url url(appState, Url::CurrentVersion);
461 string urlString = url.getAsString();
462
463 char* s = const_cast<char*>(urlString.c_str());
464 HGLOBAL clipboardDataHandle = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
465 strlen(s) + 1);
466 char* clipboardData = (char*) GlobalLock(clipboardDataHandle);
467 if (clipboardData != NULL)
468 {
469 strcpy(clipboardData, s);
470 GlobalUnlock(clipboardDataHandle);
471 EmptyClipboard();
472 HANDLE h = SetClipboardData(CF_TEXT, clipboardDataHandle);
473 CloseClipboard();
474 return h != NULL;
475 }
476 else
477 {
478 CloseClipboard();
479 return false;
480 }
481 }
482
483
ToggleMenuItem(HMENU menu,int id)484 static bool ToggleMenuItem(HMENU menu, int id)
485 {
486 MENUITEMINFO menuInfo;
487 menuInfo.cbSize = sizeof(MENUITEMINFO);
488 menuInfo.fMask = MIIM_STATE;
489 if (GetMenuItemInfo(menu, id, FALSE, &menuInfo))
490 {
491 bool isChecked = ((menuInfo.fState & MFS_CHECKED) != 0);
492 CheckMenuItem(menu, id, isChecked ? MF_UNCHECKED : MF_CHECKED);
493 return !isChecked;
494 }
495 return false;
496 }
497
LoadItemTextFromFile(HWND hWnd,int item,char * filename)498 bool LoadItemTextFromFile(HWND hWnd,
499 int item,
500 char* filename)
501 {
502 // ifstream textFile(filename, ios::in | ios::binary);
503 ifstream textFile(filename, ios::in);
504 string s;
505
506 if (!textFile.good())
507 {
508 SetDlgItemText(hWnd, item, "License file missing!\r\r\nSee http://www.gnu.org/copyleft/gpl.html");
509 return true;
510 }
511
512 char c;
513 while (textFile.get(c))
514 {
515 if (c == '\n')
516 s += "\r\r\n";
517 else
518 s += c;
519 }
520
521 SetDlgItemText(hWnd, item, UTF8ToCurrentCP(s).c_str());
522
523 return true;
524 }
525
526
AboutProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)527 BOOL APIENTRY AboutProc(HWND hDlg,
528 UINT message,
529 UINT wParam,
530 LONG lParam)
531 {
532 switch (message)
533 {
534 case WM_INITDIALOG:
535 MakeHyperlinkFromStaticCtrl(hDlg, IDC_CELESTIALINK);
536 return(TRUE);
537
538 case WM_COMMAND:
539 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
540 {
541 EndDialog(hDlg, 0);
542 return TRUE;
543 }
544 else if (LOWORD(wParam) == IDC_CELESTIALINK)
545 {
546 char urlBuf[256];
547 HWND hCtrl = GetDlgItem(hDlg, IDC_CELESTIALINK);
548 if (hCtrl)
549 {
550 if (GetWindowText(hCtrl, urlBuf, sizeof(urlBuf)) > 0)
551 {
552 ShellExecute(hDlg, "open", urlBuf, NULL, NULL, SW_SHOWNORMAL);
553 return TRUE;
554 }
555 }
556 }
557 break;
558 }
559
560 return FALSE;
561 }
562
563
ControlsHelpProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)564 BOOL APIENTRY ControlsHelpProc(HWND hDlg,
565 UINT message,
566 UINT wParam,
567 LONG lParam)
568 {
569 switch (message)
570 {
571 case WM_INITDIALOG:
572 LoadItemTextFromFile(hDlg, IDC_TEXT_CONTROLSHELP, const_cast<char*>(LocaleFilename("controls.txt").c_str()));
573 return(TRUE);
574
575 case WM_COMMAND:
576 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
577 {
578 EndDialog(hDlg, 0);
579 return TRUE;
580 }
581 break;
582 }
583
584 return FALSE;
585 }
586
587
LicenseProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)588 BOOL APIENTRY LicenseProc(HWND hDlg,
589 UINT message,
590 UINT wParam,
591 LONG lParam)
592 {
593 switch (message)
594 {
595 case WM_INITDIALOG:
596 LoadItemTextFromFile(hDlg, IDC_LICENSE_TEXT, const_cast<char*>(LocaleFilename("COPYING").c_str()));
597 return(TRUE);
598
599 case WM_COMMAND:
600 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
601 {
602 EndDialog(hDlg, 0);
603 return TRUE;
604 }
605 break;
606 }
607
608 return FALSE;
609 }
610
611
GLInfoProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)612 BOOL APIENTRY GLInfoProc(HWND hDlg,
613 UINT message,
614 UINT wParam,
615 LONG lParam)
616 {
617 switch (message)
618 {
619 case WM_INITDIALOG:
620 {
621 const char* vendor = (char*) glGetString(GL_VENDOR);
622 const char* render = (char*) glGetString(GL_RENDERER);
623 const char* version = (char*) glGetString(GL_VERSION);
624 const char* ext = (char*) glGetString(GL_EXTENSIONS);
625 string s;
626 s += UTF8ToCurrentCP(_("Vendor: "));
627 if (vendor != NULL)
628 s += vendor;
629 s += "\r\r\n";
630
631 s += UTF8ToCurrentCP(_("Renderer: "));
632 if (render != NULL)
633 s += render;
634 s += "\r\r\n";
635
636 s += UTF8ToCurrentCP(_("Version: "));
637 if (version != NULL)
638 s += version;
639 s += "\r\r\n";
640
641 if (ExtensionSupported("GL_ARB_shading_language_100"))
642 {
643 const char* versionString = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION_ARB);
644 if (versionString != NULL)
645 {
646 s += UTF8ToCurrentCP(_("GLSL version: "));
647 s += versionString;
648 s += "\r\r\n";
649 }
650 }
651
652 char buf[1024];
653 GLint simTextures = 1;
654 if (ExtensionSupported("GL_ARB_multitexture"))
655 glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &simTextures);
656 sprintf(buf, "%s%d\r\r\n",
657 UTF8ToCurrentCP(_("Max simultaneous textures: ")).c_str(),
658 simTextures);
659 s += buf;
660
661 GLint maxTextureSize = 0;
662 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
663 sprintf(buf, "%s%d\r\r\n",
664 UTF8ToCurrentCP(_("Max texture size: ")).c_str(),
665 maxTextureSize);
666 s += buf;
667
668 if (ExtensionSupported("GL_EXT_texture_cube_map"))
669 {
670 GLint maxCubeMapSize = 0;
671 glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &maxCubeMapSize);
672 sprintf(buf, "%s%d\r\r\n",
673 UTF8ToCurrentCP(_("Max cube map size: ")).c_str(),
674 maxTextureSize);
675 s += buf;
676 }
677
678 GLfloat pointSizeRange[2];
679 glGetFloatv(GL_POINT_SIZE_RANGE, pointSizeRange);
680 sprintf(buf, "%s%f - %f\r\r\n",
681 UTF8ToCurrentCP(_("Point size range: ")).c_str(),
682 pointSizeRange[0], pointSizeRange[1]);
683 s += buf;
684
685 s += "\r\r\n";
686 s += UTF8ToCurrentCP(_("Supported Extensions:")).c_str();
687 s += "\r\r\n";
688
689 if (ext != NULL)
690 {
691 string extString(ext);
692 int pos = extString.find(' ', 0);
693 while (pos != string::npos)
694 {
695 extString.replace(pos, 1, "\r\r\n");
696 pos = extString.find(' ', pos);
697 }
698 s += extString;
699 }
700
701 SetDlgItemText(hDlg, IDC_GLINFO_TEXT, s.c_str());
702 }
703 return(TRUE);
704
705 case WM_COMMAND:
706 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
707 {
708 EndDialog(hDlg, 0);
709 return TRUE;
710 }
711 break;
712 }
713
714 return FALSE;
715 }
716
717
ChooseMovieParamsProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)718 UINT CALLBACK ChooseMovieParamsProc(HWND hDlg, UINT message,
719 WPARAM wParam, LPARAM lParam)
720 {
721 switch (message)
722 {
723 case WM_INITDIALOG:
724 {
725 char buf[100];
726 HWND hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_SIZE);
727 int nSizes = sizeof MovieSizes / sizeof MovieSizes[0];
728
729 int i;
730 for (i = 0; i < nSizes; i++)
731 {
732 sprintf(buf, "%d x %d", MovieSizes[i][0], MovieSizes[i][1]);
733 SendMessage(hwnd, CB_INSERTSTRING, -1,
734 reinterpret_cast<LPARAM>(buf));
735
736 }
737 SendMessage(hwnd, CB_SETCURSEL, movieSize, 0);
738
739 hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_FRAMERATE);
740 int nFramerates = sizeof MovieFramerates / sizeof MovieFramerates[0];
741 for (i = 0; i < nFramerates; i++)
742 {
743 sprintf(buf, "%.2f", MovieFramerates[i]);
744 SendMessage(hwnd, CB_INSERTSTRING, -1,
745 reinterpret_cast<LPARAM>(buf));
746 }
747 SendMessage(hwnd, CB_SETCURSEL, movieFramerate, 0);
748 }
749 return TRUE;
750
751 case WM_COMMAND:
752 if (LOWORD(wParam) == IDC_COMBO_MOVIE_SIZE)
753 {
754 if (HIWORD(wParam) == CBN_SELCHANGE)
755 {
756 HWND hwnd = reinterpret_cast<HWND>(lParam);
757 int item = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
758 if (item != CB_ERR)
759 movieSize = item;
760 }
761 return TRUE;
762 }
763 else if (LOWORD(wParam) == IDC_COMBO_MOVIE_FRAMERATE)
764 {
765 if (HIWORD(wParam) == CBN_SELCHANGE)
766 {
767 HWND hwnd = reinterpret_cast<HWND>(lParam);
768 int item = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
769 if (item != CB_ERR)
770 movieFramerate = item;
771 }
772 return TRUE;
773 }
774 }
775
776 return FALSE;
777 }
778
779
FindObjectProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)780 BOOL APIENTRY FindObjectProc(HWND hDlg,
781 UINT message,
782 UINT wParam,
783 LONG lParam)
784 {
785 switch (message)
786 {
787 case WM_INITDIALOG:
788 return(TRUE);
789
790 case WM_COMMAND:
791 if (LOWORD(wParam) == IDOK)
792 {
793 char buf[1024], out[1024];
794 wchar_t wbuff[1024];
795 int len = GetDlgItemText(hDlg, IDC_FINDOBJECT_EDIT, buf, sizeof(buf));
796 if (len > 0)
797 {
798 int wlen = MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuff, sizeof(wbuff));
799 WideCharToMultiByte(CP_UTF8, 0, wbuff, wlen, out, sizeof(out), NULL, NULL);
800 Selection sel = appCore->getSimulation()->findObject(string(out), true);
801 if (!sel.empty())
802 appCore->getSimulation()->setSelection(sel);
803 }
804 EndDialog(hDlg, 0);
805 return TRUE;
806 }
807 else if (LOWORD(wParam) == IDCANCEL)
808 {
809 EndDialog(hDlg, 0);
810 return FALSE;
811 }
812 break;
813 }
814
815 return FALSE;
816 }
817
818
AddBookmarkFolderProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)819 BOOL APIENTRY AddBookmarkFolderProc(HWND hDlg,
820 UINT message,
821 UINT wParam,
822 LONG lParam)
823 {
824 switch (message)
825 {
826 case WM_INITDIALOG:
827 {
828 // Center dialog directly over parent
829 HWND hParent = GetParent(hDlg);
830 CenterWindow(hParent, hDlg);
831
832 // Limit text of folder name to 32 chars
833 HWND hEdit = GetDlgItem(hDlg, IDC_BOOKMARKFOLDER);
834 SendMessage(hEdit, EM_LIMITTEXT, 32, 0);
835
836 // Set initial button states
837 HWND hOK = GetDlgItem(hDlg, IDOK);
838 HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
839 EnableWindow(hOK, FALSE);
840 RemoveButtonDefaultStyle(hOK);
841 AddButtonDefaultStyle(hCancel);
842 }
843 return TRUE;
844
845 case WM_COMMAND:
846 {
847 if (HIWORD(wParam) == EN_CHANGE)
848 {
849 HWND hOK = GetDlgItem(hDlg, IDOK);
850 HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
851
852 if (hOK && hCancel)
853 {
854 // If edit control contains text, enable OK button
855 char name[33];
856 GetWindowText((HWND)lParam, name, sizeof(name));
857 if (name[0])
858 {
859 // Remove Cancel button default style
860 RemoveButtonDefaultStyle(hCancel);
861
862 // Enable OK button
863 EnableWindow(hOK, TRUE);
864
865 // Make OK button default
866 AddButtonDefaultStyle(hOK);
867 }
868 else
869 {
870 // Disable OK button
871 EnableWindow(hOK, FALSE);
872
873 // Remove OK button default style
874 RemoveButtonDefaultStyle(hOK);
875
876 // Make Cancel button default
877 AddButtonDefaultStyle(hCancel);
878 }
879 }
880 }
881 }
882
883 if (LOWORD(wParam) == IDOK)
884 {
885 // Get text entered in Folder Name Edit box
886 char name[33];
887 HWND hEdit = GetDlgItem(hDlg, IDC_BOOKMARKFOLDER);
888 if (hEdit)
889 {
890 if (GetWindowText(hEdit, name, sizeof(name)))
891 {
892 // Create new folder in parent dialog tree control.
893 AddNewBookmarkFolderInTree(hBookmarkTree, appCore, name);
894 }
895 }
896
897 EndDialog(hDlg, 0);
898 return TRUE;
899 }
900 else if (LOWORD(wParam) == IDCANCEL)
901 {
902 EndDialog(hDlg, 0);
903 return FALSE;
904 }
905 }
906
907 return FALSE;
908 }
909
AddBookmarkProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)910 BOOL APIENTRY AddBookmarkProc(HWND hDlg,
911 UINT message,
912 UINT wParam,
913 LONG lParam)
914 {
915 switch (message)
916 {
917 case WM_INITDIALOG:
918 {
919 RECT dlgRect, treeRect;
920 HWND hCtrl;
921 if (GetWindowRect(hDlg, &dlgRect))
922 {
923 if (hCtrl = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
924 {
925 if (GetWindowRect(hCtrl, &treeRect))
926 {
927 int width = dlgRect.right - dlgRect.left;
928 int height = treeRect.top - dlgRect.top;
929 SetWindowPos(hDlg, HWND_TOP, 0, 0, width, height,
930 SWP_NOMOVE | SWP_NOZORDER);
931 }
932
933 HTREEITEM hParent;
934 if (hParent = PopulateBookmarkFolders(hCtrl, appCore, appInstance))
935 {
936 //Expand bookmarks item
937 TreeView_Expand(hCtrl, hParent, TVE_EXPAND);
938 }
939 }
940 }
941
942 //Set initial button states
943 HWND hOK = GetDlgItem(hDlg, IDOK);
944 HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
945 EnableWindow(hOK, FALSE);
946 RemoveButtonDefaultStyle(hOK);
947 AddButtonDefaultStyle(hCancel);
948
949 // Set bookmark text to selection text
950 if (hCtrl = GetDlgItem(hDlg, IDC_BOOKMARK_EDIT))
951 {
952 //If this is a body, set the text.
953 Selection sel = appCore->getSimulation()->getSelection();
954 switch (sel.getType())
955 {
956 case Selection::Type_Body:
957 {
958 string name = UTF8ToCurrentCP(sel.body()->getName(true));
959 SetWindowText(hCtrl, (char*)name.c_str());
960 }
961 break;
962
963 default:
964 break;
965 }
966 }
967
968 return(TRUE);
969 }
970
971 case WM_COMMAND:
972 {
973 if (HIWORD(wParam) == EN_CHANGE)
974 {
975 HWND hOK = GetDlgItem(hDlg, IDOK);
976 HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
977
978 if (hOK && hCancel)
979 {
980 //If edit control contains text, enable OK button
981 char name[33];
982 GetWindowText((HWND)lParam, name, sizeof(name));
983 if (name[0])
984 {
985 //Remove Cancel button default style
986 RemoveButtonDefaultStyle(hCancel);
987
988 //Enable OK button
989 EnableWindow(hOK, TRUE);
990
991 //Make OK button default
992 AddButtonDefaultStyle(hOK);
993 }
994 else
995 {
996 //Disable OK button
997 EnableWindow(hOK, FALSE);
998
999 //Remove OK button default style
1000 RemoveButtonDefaultStyle(hOK);
1001
1002 //Make Cancel button default
1003 AddButtonDefaultStyle(hCancel);
1004 }
1005 }
1006 }
1007 if (LOWORD(wParam) == IDOK)
1008 {
1009 char name[33];
1010 int len = GetDlgItemText(hDlg, IDC_BOOKMARK_EDIT, name, sizeof(name));
1011 if (len > 0)
1012 {
1013 HWND hTree;
1014 if(hTree = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
1015 {
1016 InsertBookmarkInFavorites(hTree, name, appCore);
1017
1018 appCore->writeFavoritesFile();
1019
1020 // Rebuild bookmarks menu.
1021 BuildFavoritesMenu(menuBar, appCore, appInstance, &odAppMenu);
1022 }
1023 }
1024 EndDialog(hDlg, 0);
1025 return TRUE;
1026 }
1027 else if (LOWORD(wParam) == IDCANCEL)
1028 {
1029 EndDialog(hDlg, 0);
1030 return FALSE;
1031 }
1032 else if (LOWORD(wParam) == IDC_BOOKMARK_CREATEIN)
1033 {
1034 HWND button;
1035 RECT dlgRect, treeRect;
1036 HWND hTree;
1037 char text[16];
1038 if (GetWindowRect(hDlg, &dlgRect))
1039 {
1040 if (hTree = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
1041 {
1042 if (GetWindowRect(hTree, &treeRect))
1043 {
1044 if (button = GetDlgItem(hDlg, IDC_BOOKMARK_CREATEIN))
1045 {
1046 if (GetWindowText(button, text, sizeof(text)))
1047 {
1048 int width = dlgRect.right - dlgRect.left;
1049 if (strstr(text, ">>"))
1050 {
1051 //Increase size of dialog
1052 int height = treeRect.bottom - dlgRect.top + 12;
1053 SetWindowPos(hDlg, HWND_TOP, 0, 0, width, height,
1054 SWP_NOMOVE | SWP_NOZORDER);
1055 //Change text in button
1056 strcpy(text + strlen(text) - 2, "<<");
1057 SetWindowText(button, text);
1058 }
1059 else
1060 {
1061 //Decrease size of dialog
1062 int height = treeRect.top - dlgRect.top;
1063 SetWindowPos(hDlg, HWND_TOP, 0, 0, width, height,
1064 SWP_NOMOVE | SWP_NOZORDER);
1065 //Change text in button
1066 strcpy(text + strlen(text) - 2, ">>");
1067 SetWindowText(button, text);
1068 }
1069 }
1070 }
1071 }
1072 }
1073 }
1074 }
1075 else if (LOWORD(wParam) == IDC_BOOKMARK_NEWFOLDER)
1076 {
1077 if(hBookmarkTree = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
1078 {
1079 DialogBox(hRes, MAKEINTRESOURCE(IDD_ADDBOOKMARK_FOLDER),
1080 hDlg, AddBookmarkFolderProc);
1081 }
1082 }
1083 break;
1084 }
1085 }
1086
1087 return FALSE;
1088 }
1089
RenameBookmarkProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)1090 BOOL APIENTRY RenameBookmarkProc(HWND hDlg,
1091 UINT message,
1092 UINT wParam,
1093 LONG lParam)
1094 {
1095 switch (message)
1096 {
1097 case WM_INITDIALOG:
1098 {
1099 //Center dialog directly over parent
1100 HWND hParent = GetParent(hDlg);
1101 CenterWindow(hParent, hDlg);
1102
1103 //Limit text of folder name to 32 chars
1104 HWND hEdit = GetDlgItem(hDlg, IDC_NEWBOOKMARK);
1105 SendMessage(hEdit, EM_LIMITTEXT, 32, 0);
1106
1107 //Set text in edit control to current bookmark name
1108 SetWindowText(hEdit, bookmarkName);
1109
1110 return(TRUE);
1111 }
1112
1113 case WM_COMMAND:
1114 {
1115 if (HIWORD(wParam) == EN_CHANGE)
1116 {
1117 HWND hOK = GetDlgItem(hDlg, IDOK);
1118 HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
1119
1120 if (hOK && hCancel)
1121 {
1122 //If edit control contains text, enable OK button
1123 char name[33];
1124 GetWindowText((HWND)lParam, name, sizeof(name));
1125 if (name[0])
1126 {
1127 //Remove Cancel button default style
1128 RemoveButtonDefaultStyle(hCancel);
1129
1130 //Enable OK button
1131 EnableWindow(hOK, TRUE);
1132
1133 //Make OK button default
1134 AddButtonDefaultStyle(hOK);
1135 }
1136 else
1137 {
1138 //Disable OK button
1139 EnableWindow(hOK, FALSE);
1140
1141 //Remove OK button default style
1142 RemoveButtonDefaultStyle(hOK);
1143
1144 //Make Cancel button default
1145 AddButtonDefaultStyle(hCancel);
1146 }
1147 }
1148 }
1149 if (LOWORD(wParam) == IDOK)
1150 {
1151 //Get text entered in Folder Name Edit box
1152 char name[33];
1153 HWND hEdit = GetDlgItem(hDlg, IDC_NEWBOOKMARK);
1154 if (hEdit)
1155 {
1156 if (GetWindowText(hEdit, name, sizeof(name)))
1157 RenameBookmarkInFavorites(hBookmarkTree, name, appCore);
1158 }
1159
1160 EndDialog(hDlg, 0);
1161 return TRUE;
1162 }
1163 else if (LOWORD(wParam) == IDCANCEL)
1164 {
1165 EndDialog(hDlg, 0);
1166 return FALSE;
1167 }
1168 }
1169 }
1170
1171 return FALSE;
1172 }
1173
OrganizeBookmarksProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)1174 BOOL APIENTRY OrganizeBookmarksProc(HWND hDlg,
1175 UINT message,
1176 UINT wParam,
1177 LONG lParam)
1178 {
1179 static UINT_PTR dragDropTimer;
1180 switch (message)
1181 {
1182 case WM_INITDIALOG:
1183 {
1184 HWND hCtrl;
1185 if (hCtrl = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1186 {
1187 HTREEITEM hParent;
1188 if (hParent = PopulateBookmarksTree(hCtrl, appCore, hRes))
1189 {
1190 // Expand bookmarks item
1191 TreeView_Expand(hCtrl, hParent, TVE_EXPAND);
1192 }
1193 }
1194 if (hCtrl = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_DELETE))
1195 EnableWindow(hCtrl, FALSE);
1196 if (hCtrl = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_RENAME))
1197 EnableWindow(hCtrl, FALSE);
1198
1199 return(TRUE);
1200 }
1201
1202 case WM_COMMAND:
1203 {
1204 if (LOWORD(wParam) == IDOK)
1205 {
1206 #if 0
1207 HWND hTree;
1208 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1209 SyncTreeFoldersWithFavoriteFolders(hTree, appCore);
1210 #endif
1211
1212 // Write any change to bookmarks
1213 appCore->writeFavoritesFile();
1214
1215 // Rebuild bookmarks menu
1216 BuildFavoritesMenu(menuBar, appCore, hRes, &odAppMenu);
1217
1218 EndDialog(hDlg, 0);
1219 return TRUE;
1220 }
1221 else if (LOWORD(wParam) == IDCANCEL)
1222 {
1223 //Refresh from file
1224 appCore->readFavoritesFile();
1225
1226 EndDialog(hDlg, 0);
1227 return FALSE;
1228 }
1229 else if (LOWORD(wParam) == IDC_ORGANIZE_BOOKMARKS_NEWFOLDER)
1230 {
1231 if (hBookmarkTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1232 {
1233 DialogBox(hRes, MAKEINTRESOURCE(IDD_ADDBOOKMARK_FOLDER), hDlg, AddBookmarkFolderProc);
1234 }
1235 }
1236 else if (LOWORD(wParam) == IDC_ORGANIZE_BOOKMARKS_RENAME)
1237 {
1238 if (hBookmarkTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1239 {
1240 HTREEITEM hItem;
1241 TVITEM tvItem;
1242 if (hItem = TreeView_GetSelection(hBookmarkTree))
1243 {
1244 tvItem.hItem = hItem;
1245 tvItem.mask = TVIF_TEXT | TVIF_HANDLE;
1246 tvItem.pszText = bookmarkName;
1247 tvItem.cchTextMax = sizeof(bookmarkName);
1248 if (TreeView_GetItem(hBookmarkTree, &tvItem))
1249 {
1250 DialogBox(hRes,
1251 MAKEINTRESOURCE(IDD_RENAME_BOOKMARK),
1252 hDlg, RenameBookmarkProc);
1253 }
1254 }
1255 }
1256 }
1257 else if (LOWORD(wParam) == IDC_ORGANIZE_BOOKMARKS_DELETE)
1258 {
1259 HWND hTree;
1260 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1261 DeleteBookmarkFromFavorites(hTree, appCore);
1262 }
1263 break;
1264 }
1265 case WM_NOTIFY:
1266 {
1267 if (((LPNMHDR)lParam)->code == TVN_SELCHANGED)
1268 {
1269 HWND hTree;
1270 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1271 {
1272 //Enable buttons as necessary
1273 HTREEITEM hItem;
1274 if (hItem = TreeView_GetSelection(hTree))
1275 {
1276 HWND hDelete, hRename;
1277 hDelete = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_DELETE);
1278 hRename = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_RENAME);
1279 if (hDelete && hRename)
1280 {
1281 if (TreeView_GetParent(hTree, hItem))
1282 {
1283 EnableWindow(hDelete, TRUE);
1284 EnableWindow(hRename, TRUE);
1285 }
1286 else
1287 {
1288 EnableWindow(hDelete, FALSE);
1289 EnableWindow(hRename, FALSE);
1290 }
1291 }
1292 }
1293 }
1294 }
1295 else if(((LPNMHDR)lParam)->code == TVN_BEGINDRAG)
1296 {
1297 //Do not allow folders to be dragged
1298 HWND hTree;
1299 TVITEM tvItem;
1300 LPNMTREEVIEW nm = (LPNMTREEVIEW)lParam;
1301 HTREEITEM hItem = nm->itemNew.hItem;
1302
1303 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1304 {
1305 tvItem.hItem = hItem;
1306 tvItem.mask = TVIF_PARAM | TVIF_HANDLE;
1307 if (TreeView_GetItem(hTree, &tvItem))
1308 {
1309 if(tvItem.lParam != 1)
1310 {
1311 //Start a timer to handle auto-scrolling
1312 dragDropTimer = SetTimer(hDlg, 1, 100, NULL);
1313 OrganizeBookmarksOnBeginDrag(hTree, (LPNMTREEVIEW)lParam);
1314 }
1315 }
1316 }
1317 }
1318 }
1319 break;
1320 case WM_MOUSEMOVE:
1321 {
1322 if(isOrganizeBookmarksDragDropActive())
1323 {
1324 HWND hTree;
1325 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1326 {
1327 OrganizeBookmarksOnMouseMove(hTree, GET_X_LPARAM(lParam),
1328 GET_Y_LPARAM(lParam));
1329 }
1330 }
1331 }
1332 break;
1333 case WM_LBUTTONUP:
1334 {
1335 if(isOrganizeBookmarksDragDropActive())
1336 {
1337 HWND hTree;
1338 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1339 {
1340 //Kill the auto-scroll timer
1341 KillTimer(hDlg, dragDropTimer);
1342
1343 OrganizeBookmarksOnLButtonUp(hTree);
1344 MoveBookmarkInFavorites(hTree, appCore);
1345 }
1346 }
1347 }
1348 break;
1349 case WM_TIMER:
1350 {
1351 if(isOrganizeBookmarksDragDropActive())
1352 {
1353 if(wParam == 1)
1354 {
1355 //Handle
1356 HWND hTree;
1357 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
1358 {
1359 DragDropAutoScroll(hTree);
1360 }
1361 }
1362 }
1363 }
1364 break;
1365 }
1366
1367 return FALSE;
1368 }
1369
1370
1371 int selectedScreenMode = 0;
SelectDisplayModeProc(HWND hDlg,UINT message,UINT wParam,LONG lParam)1372 BOOL APIENTRY SelectDisplayModeProc(HWND hDlg,
1373 UINT message,
1374 UINT wParam,
1375 LONG lParam)
1376 {
1377 switch (message)
1378 {
1379 case WM_INITDIALOG:
1380 {
1381 char buf[100];
1382 HWND hwnd = GetDlgItem(hDlg, IDC_COMBO_RESOLUTION);
1383
1384 // Add windowed mode as the first item on the menu
1385 bind_textdomain_codeset("celestia", CurrentCP());
1386 SendMessage(hwnd, CB_INSERTSTRING, -1,
1387 reinterpret_cast<LPARAM>(_("Windowed Mode")));
1388 bind_textdomain_codeset("celestia", "UTF8");
1389
1390 for (vector<DEVMODE>::const_iterator iter= displayModes->begin();
1391 iter != displayModes->end(); iter++)
1392 {
1393 sprintf(buf, "%d x %d x %d",
1394 iter->dmPelsWidth, iter->dmPelsHeight,
1395 iter->dmBitsPerPel);
1396 SendMessage(hwnd, CB_INSERTSTRING, -1,
1397 reinterpret_cast<LPARAM>(buf));
1398 }
1399 SendMessage(hwnd, CB_SETCURSEL, currentScreenMode, 0);
1400 }
1401 return TRUE;
1402
1403 case WM_COMMAND:
1404 if (LOWORD(wParam) == IDOK)
1405 {
1406 newScreenMode = selectedScreenMode;
1407 EndDialog(hDlg, 0);
1408 return TRUE;
1409 }
1410 else if (LOWORD(wParam) == IDCANCEL)
1411 {
1412 EndDialog(hDlg, 0);
1413 return TRUE;
1414 }
1415 else if (LOWORD(wParam) == IDC_COMBO_RESOLUTION)
1416 {
1417 if (HIWORD(wParam) == CBN_SELCHANGE)
1418 {
1419 HWND hwnd = reinterpret_cast<HWND>(lParam);
1420 int item = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
1421 if (item != CB_ERR)
1422 selectedScreenMode = item;
1423 }
1424 return TRUE;
1425 }
1426 }
1427
1428 return FALSE;
1429 }
1430
1431
CreateMenuBar()1432 HMENU CreateMenuBar()
1433 {
1434 return LoadMenu(hRes, MAKEINTRESOURCE(IDR_MAIN_MENU));
1435 }
1436
setMenuItemCheck(int menuItem,bool checked)1437 static void setMenuItemCheck(int menuItem, bool checked)
1438 {
1439 CheckMenuItem(menuBar, menuItem, checked ? MF_CHECKED : MF_UNCHECKED);
1440 }
1441
1442 struct IntStrPairComparePredicate
1443 {
IntStrPairComparePredicateIntStrPairComparePredicate1444 IntStrPairComparePredicate() : dummy(0)
1445 {
1446 }
1447
operator ()IntStrPairComparePredicate1448 bool operator()(const IntStrPair pair1, const IntStrPair pair2) const
1449 {
1450 return (pair1.second.compare(pair2.second) < 0);
1451 }
1452
1453 int dummy;
1454 };
1455
CreatePlanetarySystemMenu(string parentName,const PlanetarySystem * psys)1456 static HMENU CreatePlanetarySystemMenu(string parentName, const PlanetarySystem* psys)
1457 {
1458 // Use some vectors to categorize and sort the bodies within this PlanetarySystem.
1459 // In order to generate sorted menus, we must carry the name and menu index as a
1460 // single unit in the sort. One obvous way is to create a vector<pair<int,string>>
1461 // and then use a comparison predicate to sort.the vector based on the string in
1462 // each pair.
1463
1464 // Declare vector<pair<int,string>> objects for each classification of body
1465 vector<IntStrPair> asteroids;
1466 vector<IntStrPair> comets;
1467 vector<IntStrPair> invisibles;
1468 vector<IntStrPair> moons;
1469 vector<IntStrPair> planets;
1470 vector<IntStrPair> spacecraft;
1471
1472 // We will use these objects to iterate over all the above vectors
1473 vector<IntStrPairVec> objects;
1474 vector<string> menuNames;
1475
1476 // Place each body in the correct vector based on classification
1477 HMENU menu = CreatePopupMenu();
1478 for (int i = 0; i < psys->getSystemSize(); i++)
1479 {
1480 Body* body = psys->getBody(i);
1481 if (!body->getName().empty())
1482 {
1483 switch(body->getClassification())
1484 {
1485 case Body::Asteroid:
1486 asteroids.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
1487 break;
1488 case Body::Comet:
1489 comets.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
1490 break;
1491 case Body::Invisible:
1492 invisibles.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
1493 break;
1494 case Body::Moon:
1495 moons.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
1496 break;
1497 case Body::Planet:
1498 planets.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
1499 break;
1500 case Body::Spacecraft:
1501 spacecraft.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
1502 break;
1503 }
1504 }
1505 }
1506
1507 // Add each vector of PlanetarySystem bodies to a vector to iterate over
1508 objects.push_back(asteroids);
1509 menuNames.push_back(UTF8ToCurrentCP(_("Asteroids")));
1510 objects.push_back(comets);
1511 menuNames.push_back(UTF8ToCurrentCP(_("Comets")));
1512 objects.push_back(invisibles);
1513 menuNames.push_back(UTF8ToCurrentCP(_("Invisibles")));
1514 objects.push_back(moons);
1515 menuNames.push_back(UTF8ToCurrentCP(_("Moons")));
1516 objects.push_back(planets);
1517 menuNames.push_back(UTF8ToCurrentCP(_("Planets")));
1518 objects.push_back(spacecraft);
1519 menuNames.push_back(UTF8ToCurrentCP(_("Spacecraft")));
1520
1521 // Now sort each vector and generate submenus
1522 IntStrPairComparePredicate pred;
1523 vector<IntStrPairVec>::iterator obj;
1524 vector<IntStrPair>::iterator it;
1525 vector<string>::iterator menuName;
1526 HMENU hSubMenu;
1527 int numSubMenus;
1528
1529 // Count how many submenus we need to create
1530 numSubMenus = 0;
1531 for (obj=objects.begin(); obj != objects.end(); obj++)
1532 {
1533 if (obj->size() > 0)
1534 numSubMenus++;
1535 }
1536
1537 menuName = menuNames.begin();
1538 for (obj=objects.begin(); obj != objects.end(); obj++)
1539 {
1540 // Only generate a submenu if the vector is not empty
1541 if (obj->size() > 0)
1542 {
1543 // Don't create a submenu for a single item
1544 if (obj->size() == 1)
1545 {
1546 it=obj->begin();
1547 AppendMenu(menu, MF_STRING, MENU_CHOOSE_PLANET + it->first, it->second.c_str());
1548 }
1549 else
1550 {
1551 // Skip sorting if we are dealing with the planets in our own Solar System.
1552 if (parentName != "Sol" || *menuName != UTF8ToCurrentCP(_("Planets")))
1553 sort(obj->begin(), obj->end(), pred);
1554
1555 if (numSubMenus > 1)
1556 {
1557 // Add items to submenu
1558 hSubMenu = CreatePopupMenu();
1559 for(it=obj->begin(); it != obj->end(); it++)
1560 AppendMenu(hSubMenu, MF_STRING, MENU_CHOOSE_PLANET + it->first, it->second.c_str());
1561
1562 AppendMenu(menu, MF_POPUP | MF_STRING, (DWORD)hSubMenu, menuName->c_str());
1563 }
1564 else
1565 {
1566 // Just add items to the popup
1567 for(it=obj->begin(); it != obj->end(); it++)
1568 AppendMenu(menu, MF_STRING, MENU_CHOOSE_PLANET + it->first, it->second.c_str());
1569 }
1570 }
1571 }
1572 menuName++;
1573 }
1574
1575 return menu;
1576 }
1577
1578
CreateAlternateSurfaceMenu(const vector<string> & surfaces)1579 static HMENU CreateAlternateSurfaceMenu(const vector<string>& surfaces)
1580 {
1581 HMENU menu = CreatePopupMenu();
1582
1583 AppendMenu(menu, MF_STRING, MENU_CHOOSE_SURFACE, "Normal");
1584 for (unsigned int i = 0; i < surfaces.size(); i++)
1585 {
1586 AppendMenu(menu, MF_STRING, MENU_CHOOSE_SURFACE + i + 1,
1587 surfaces[i].c_str());
1588 }
1589
1590 return menu;
1591 }
1592
1593
handlePopupMenu(HWND hwnd,float x,float y,const Selection & sel)1594 VOID APIENTRY handlePopupMenu(HWND hwnd,
1595 float x, float y,
1596 const Selection& sel)
1597 {
1598 HMENU hMenu;
1599 string name;
1600
1601 hMenu = CreatePopupMenu();
1602 switch (sel.getType())
1603 {
1604 case Selection::Type_Body:
1605 {
1606 name = sel.body()->getName(true);
1607 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_CENTER, UTF8ToCurrentCP(name).c_str());
1608 AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
1609 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_GOTO, UTF8ToCurrentCP(_("&Goto")).c_str());
1610 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_FOLLOW, UTF8ToCurrentCP(_("&Follow")).c_str());
1611 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_SYNCORBIT, UTF8ToCurrentCP(_("S&ync Orbit")).c_str());
1612 AppendMenu(hMenu, MF_STRING, ID_INFO, UTF8ToCurrentCP(_("&Info")).c_str());
1613 HMENU refVectorMenu = CreatePopupMenu();
1614 AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) refVectorMenu, UTF8ToCurrentCP(_("&Reference Marks")).c_str());
1615 AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_BODY_AXES, UTF8ToCurrentCP(_("Show Body Axes")).c_str());
1616 AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_FRAME_AXES, UTF8ToCurrentCP(_("Show Frame Axes")).c_str());
1617 AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_SUN_DIRECTION, UTF8ToCurrentCP(_("Show Sun Direction")).c_str());
1618 AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_VELOCITY_VECTOR, UTF8ToCurrentCP(_("Show Velocity Vector")).c_str());
1619 AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_PLANETOGRAPHIC_GRID, UTF8ToCurrentCP(_("Show Planetographic Grid")).c_str());
1620 AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_TERMINATOR, UTF8ToCurrentCP(_("Show Terminator")).c_str());
1621
1622 CheckMenuItem(refVectorMenu, ID_RENDER_BODY_AXES, sel.body()->findReferenceMark("body axes") ? MF_CHECKED : MF_UNCHECKED);
1623 CheckMenuItem(refVectorMenu, ID_RENDER_FRAME_AXES, sel.body()->findReferenceMark("frame axes") ? MF_CHECKED : MF_UNCHECKED);
1624 CheckMenuItem(refVectorMenu, ID_RENDER_SUN_DIRECTION, sel.body()->findReferenceMark("sun direction") ? MF_CHECKED : MF_UNCHECKED);
1625 CheckMenuItem(refVectorMenu, ID_RENDER_VELOCITY_VECTOR, sel.body()->findReferenceMark("velocity vector") ? MF_CHECKED : MF_UNCHECKED);
1626 CheckMenuItem(refVectorMenu, ID_RENDER_PLANETOGRAPHIC_GRID, sel.body()->findReferenceMark("planetographic grid") ? MF_CHECKED : MF_UNCHECKED);
1627 CheckMenuItem(refVectorMenu, ID_RENDER_TERMINATOR, sel.body()->findReferenceMark("terminator") ? MF_CHECKED : MF_UNCHECKED);
1628
1629 const PlanetarySystem* satellites = sel.body()->getSatellites();
1630 if (satellites != NULL && satellites->getSystemSize() != 0)
1631 {
1632 HMENU satMenu = CreatePlanetarySystemMenu(name, satellites);
1633 AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) satMenu,
1634 UTF8ToCurrentCP(_("&Satellites")).c_str());
1635 }
1636
1637 vector<string>* altSurfaces = sel.body()->getAlternateSurfaceNames();
1638 if (altSurfaces != NULL)
1639 {
1640 if (altSurfaces->size() != NULL)
1641 {
1642 HMENU surfMenu = CreateAlternateSurfaceMenu(*altSurfaces);
1643 AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) surfMenu,
1644 UTF8ToCurrentCP(_("&Alternate Surfaces")).c_str());
1645 }
1646 delete altSurfaces;
1647 }
1648 }
1649 break;
1650
1651 case Selection::Type_Star:
1652 {
1653 Simulation* sim = appCore->getSimulation();
1654 name = sim->getUniverse()->getStarCatalog()->getStarName(*(sel.star()));
1655 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_CENTER, UTF8ToCurrentCP(name).c_str());
1656 AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
1657 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_GOTO, UTF8ToCurrentCP(_("&Goto")).c_str());
1658 AppendMenu(hMenu, MF_STRING, ID_INFO, UTF8ToCurrentCP(_("&Info")).c_str());
1659
1660 SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog();
1661 SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel.star()->getCatalogNumber());
1662 if (iter != solarSystemCatalog->end())
1663 {
1664 SolarSystem* solarSys = iter->second;
1665 HMENU planetsMenu = CreatePlanetarySystemMenu(name, solarSys->getPlanets());
1666 if (name == "Sol")
1667 AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) planetsMenu, UTF8ToCurrentCP(_("Orbiting Bodies")).c_str());
1668 else
1669 AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) planetsMenu, UTF8ToCurrentCP(_("Planets")).c_str());
1670 }
1671 }
1672 break;
1673
1674 case Selection::Type_DeepSky:
1675 {
1676 Simulation* sim = appCore->getSimulation();
1677 name = sim->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky());
1678 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_CENTER, UTF8ToCurrentCP(name).c_str());
1679 AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
1680 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_GOTO, UTF8ToCurrentCP(_("&Goto")).c_str());
1681 AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_FOLLOW, UTF8ToCurrentCP(_("&Follow")).c_str());
1682 AppendMenu(hMenu, MF_STRING, ID_INFO, UTF8ToCurrentCP(_("&Info")).c_str());
1683 }
1684 break;
1685
1686 case Selection::Type_Location:
1687 break;
1688
1689 default:
1690 break;
1691 }
1692
1693 if (appCore->getSimulation()->getUniverse()->isMarked(sel, 1))
1694 AppendMenu(hMenu, MF_STRING, ID_TOOLS_UNMARK, UTF8ToCurrentCP(_("&Unmark")).c_str());
1695 else
1696 AppendMenu(hMenu, MF_STRING, ID_TOOLS_MARK, UTF8ToCurrentCP(_("&Mark")).c_str());
1697
1698 POINT point;
1699 point.x = (int) x;
1700 point.y = (int) y;
1701
1702
1703 if (currentScreenMode == 0)
1704 ClientToScreen(hwnd, (LPPOINT) &point);
1705
1706 appCore->getSimulation()->setSelection(sel);
1707 TrackPopupMenu(hMenu, 0, point.x, point.y, 0, hwnd, NULL);
1708
1709 // TODO: Do we need to explicitly destroy submenus or does DestroyMenu
1710 // work recursively?
1711 // According to the MSDN documentation, DestroyMenu() IS recursive. Clint 11/01.
1712 DestroyMenu(hMenu);
1713
1714 #ifdef INFINITE_MOUSE
1715 ignoreNextMoveEvent = true;
1716 #endif // INFINITE_MOUSE
1717 }
1718
1719
1720 // TODO: get rid of fixed urls
ShowWWWInfo(const Selection & sel)1721 void ShowWWWInfo(const Selection& sel)
1722 {
1723 string url;
1724 switch (sel.getType())
1725 {
1726 case Selection::Type_Body:
1727 {
1728 url = sel.body()->getInfoURL();
1729 if (url.empty())
1730 {
1731 string name = sel.body()->getName();
1732 for (unsigned int i = 0; i < name.size(); i++)
1733 name[i] = tolower(name[i]);
1734
1735 url = string("http://www.nineplanets.org/") + name + ".html";
1736 }
1737 }
1738 break;
1739
1740 case Selection::Type_Star:
1741 {
1742 url = sel.star()->getInfoURL();
1743 if (url.empty())
1744 {
1745 char name[32];
1746 sprintf(name, "HIP%d", sel.star()->getCatalogNumber() & ~0xf0000000);
1747 url = string("http://simbad.u-strasbg.fr/sim-id.pl?protocol=html&Ident=") + name;
1748 }
1749 }
1750 break;
1751
1752 case Selection::Type_DeepSky:
1753 url = sel.deepsky()->getInfoURL();
1754 break;
1755
1756 case Selection::Type_Location:
1757 break;
1758
1759 default:
1760 break;
1761 }
1762
1763 ShellExecute(mainWindow,
1764 "open",
1765 url.c_str(),
1766 NULL,
1767 NULL,
1768 0);
1769 }
1770
1771
ContextMenu(float x,float y,Selection sel)1772 void ContextMenu(float x, float y, Selection sel)
1773 {
1774 handlePopupMenu(mainWindow, x, y, sel);
1775 }
1776
1777
EnableFullScreen(const DEVMODE & dm)1778 bool EnableFullScreen(const DEVMODE& dm)
1779 {
1780 DEVMODE devMode;
1781
1782 ZeroMemory(&devMode, sizeof devMode);
1783 devMode.dmSize = sizeof devMode;
1784 devMode.dmPelsWidth = dm.dmPelsWidth;
1785 devMode.dmPelsHeight = dm.dmPelsHeight;
1786 devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
1787
1788 if (ChangeDisplaySettings(&devMode, CDS_FULLSCREEN) !=
1789 DISP_CHANGE_SUCCESSFUL)
1790 {
1791 MessageBox(NULL,
1792 "Unable to switch to full screen mode; running in window mode",
1793 "Error",
1794 MB_OK | MB_ICONERROR);
1795 return false;
1796 }
1797
1798 return true;
1799 }
1800
1801
DisableFullScreen()1802 void DisableFullScreen()
1803 {
1804 ChangeDisplaySettings(0, 0);
1805 }
1806
1807
1808 unsigned int
ChooseBestMSAAPixelFormat(HDC hdc,int * formats,unsigned int numFormats,int samplesRequested)1809 ChooseBestMSAAPixelFormat(HDC hdc, int *formats, unsigned int numFormats,
1810 int samplesRequested)
1811 {
1812 int idealFormat = 0;
1813 int bestFormat = 0;
1814 int bestSamples = 0;
1815
1816 for (unsigned int i = 0; i < numFormats; i++)
1817 {
1818 int query = WGL_SAMPLES_ARB;
1819 int result = 0;
1820 bool isFloatFormat = false;
1821
1822 query = WGL_SAMPLES_ARB;
1823 wglGetPixelFormatAttribivARB(hdc, formats[i], 0, 1, &query, &result);
1824
1825 if (result <= samplesRequested && result >= bestSamples)
1826 {
1827 bestSamples = result;
1828 bestFormat = formats[i];
1829 }
1830
1831 if (result == samplesRequested)
1832 idealFormat = formats[i];
1833 }
1834
1835 if (idealFormat != 0)
1836 return idealFormat;
1837
1838 return bestFormat;
1839 }
1840
1841
1842 // Select the pixel format for a given device context
SetDCPixelFormat(HDC hDC)1843 bool SetDCPixelFormat(HDC hDC)
1844 {
1845 bool msaa = false;
1846 if (appCore->getConfig()->aaSamples > 1 &&
1847 WGLExtensionSupported("WGL_ARB_pixel_format") &&
1848 WGLExtensionSupported("WGL_ARB_multisample"))
1849 {
1850 msaa = true;
1851 }
1852
1853 if (!msaa)
1854 {
1855 static PIXELFORMATDESCRIPTOR pfd = {
1856 sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
1857 1, // Version of this structure
1858 PFD_DRAW_TO_WINDOW | // Draw to Window (not to bitmap)
1859 PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
1860 PFD_DOUBLEBUFFER, // Double buffered mode
1861 PFD_TYPE_RGBA, // RGBA Color mode
1862 GetDeviceCaps(hDC, BITSPIXEL),// Want the display bit depth
1863 0,0,0,0,0,0, // Not used to select mode
1864 0,0, // Not used to select mode
1865 0,0,0,0,0, // Not used to select mode
1866 24, // Size of depth buffer
1867 0, // Not used to select mode
1868 0, // Not used to select mode
1869 PFD_MAIN_PLANE, // Draw in main plane
1870 0, // Not used to select mode
1871 0,0,0 // Not used to select mode
1872 };
1873
1874 // Choose a pixel format that best matches that described in pfd
1875 int nPixelFormat = ChoosePixelFormat(hDC, &pfd);
1876 if (nPixelFormat == 0)
1877 {
1878 // Uh oh . . . looks like we can't handle OpenGL on this device.
1879 return false;
1880 }
1881 else
1882 {
1883 // Set the pixel format for the device context
1884 SetPixelFormat(hDC, nPixelFormat, &pfd);
1885 return true;
1886 }
1887 }
1888 else
1889 {
1890 PIXELFORMATDESCRIPTOR pfd;
1891
1892 int ifmtList[] = {
1893 WGL_DRAW_TO_WINDOW_ARB, TRUE,
1894 WGL_SUPPORT_OPENGL_ARB, TRUE,
1895 WGL_DOUBLE_BUFFER_ARB, TRUE,
1896 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
1897 WGL_DEPTH_BITS_ARB, 24,
1898 WGL_COLOR_BITS_ARB, 24,
1899 WGL_RED_BITS_ARB, 8,
1900 WGL_GREEN_BITS_ARB, 8,
1901 WGL_BLUE_BITS_ARB, 8,
1902 WGL_ALPHA_BITS_ARB, 0,
1903 WGL_ACCUM_BITS_ARB, 0,
1904 WGL_STENCIL_BITS_ARB, 0,
1905 WGL_SAMPLE_BUFFERS_ARB, appCore->getConfig()->aaSamples > 1,
1906 0
1907 };
1908
1909 int pixelFormatIndex;
1910 int pixFormats[256];
1911 unsigned int numFormats;
1912
1913 wglChoosePixelFormatARB(hDC, ifmtList, NULL, 256, pixFormats, &numFormats);
1914
1915 pixelFormatIndex = ChooseBestMSAAPixelFormat(hDC, pixFormats,
1916 numFormats,
1917 appCore->getConfig()->aaSamples);
1918
1919 DescribePixelFormat(hDC, pixelFormatIndex,
1920 sizeof(PIXELFORMATDESCRIPTOR), &pfd);
1921 if (!SetPixelFormat(hDC, pixelFormatIndex, &pfd))
1922 return false;
1923
1924 return true;
1925 }
1926 }
1927
1928
CreateOpenGLWindow(int x,int y,int width,int height,int mode,int & newMode)1929 HWND CreateOpenGLWindow(int x, int y, int width, int height,
1930 int mode, int& newMode)
1931 {
1932 assert(mode >= 0 && mode <= displayModes->size());
1933 if (mode != 0)
1934 {
1935 x = 0;
1936 y = 0;
1937 width = displayModes->at(mode - 1).dmPelsWidth;
1938 height = displayModes->at(mode - 1).dmPelsHeight;
1939 }
1940
1941 // Set up and register the window class
1942 WNDCLASS wc;
1943 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1944 wc.lpfnWndProc = (WNDPROC) MainWindowProc;
1945 wc.cbClsExtra = 0;
1946 wc.cbWndExtra = 0;
1947 wc.hInstance = appInstance;
1948 wc.hIcon = LoadIcon(hRes, MAKEINTRESOURCE(IDI_CELESTIA_ICON));
1949 wc.hCursor = hDefaultCursor;
1950 wc.hbrBackground = NULL;
1951 wc.lpszMenuName = NULL;
1952 wc.lpszClassName = AppName;
1953 if (RegisterClass(&wc) == 0)
1954 {
1955 MessageBox(NULL,
1956 "Failed to register the window class.", "Fatal Error",
1957 MB_OK | MB_ICONERROR);
1958 return NULL;
1959 }
1960
1961 newMode = currentScreenMode;
1962 if (mode != 0)
1963 {
1964 if (EnableFullScreen(displayModes->at(mode - 1)))
1965 newMode = mode;
1966 }
1967 else
1968 {
1969 DisableFullScreen();
1970 newMode = 0;
1971 }
1972
1973 // Determine the proper window style to use
1974 DWORD dwStyle;
1975 if (newMode != 0)
1976 {
1977 dwStyle = WS_POPUPWINDOW | WS_MAXIMIZE;
1978 }
1979 else
1980 {
1981 dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
1982 }
1983
1984 // Create the window
1985 HWND hwnd = CreateWindow(AppName,
1986 AppName,
1987 dwStyle,
1988 x, y,
1989 width, height,
1990 NULL,
1991 NULL,
1992 appInstance,
1993 NULL);
1994
1995 if (hwnd == NULL)
1996 return NULL;
1997
1998 ShowWindow(hwnd, SW_SHOW);
1999 SetForegroundWindow(hwnd);
2000 SetFocus(hwnd);
2001
2002 deviceContext = GetDC(hwnd);
2003 if (!SetDCPixelFormat(deviceContext))
2004 {
2005 MessageBox(NULL,
2006 "Could not get appropriate pixel format for OpenGL rendering.", "Fatal Error",
2007 MB_OK | MB_ICONERROR);
2008 return NULL;
2009 }
2010
2011 if (newMode == 0)
2012 SetMenu(hwnd, menuBar);
2013 else
2014 hideMenuBar = true;
2015
2016 if (glContext == NULL)
2017 {
2018 glContext = wglCreateContext(deviceContext);
2019 }
2020 wglMakeCurrent(deviceContext, glContext);
2021
2022 return hwnd;
2023 }
2024
DestroyOpenGLWindow()2025 void DestroyOpenGLWindow()
2026 {
2027 #if 0
2028 if (glContext != NULL)
2029 {
2030 wglMakeCurrent(NULL, NULL);
2031 if (!wglDeleteContext(glContext))
2032 {
2033 MessageBox(NULL,
2034 "Releasing GL context failed.", "Error",
2035 MB_OK | MB_ICONERROR);
2036 }
2037 glContext = NULL;
2038 }
2039 #endif
2040
2041 if (deviceContext != NULL)
2042 {
2043 if (!ReleaseDC(mainWindow, deviceContext))
2044 {
2045 MessageBox(NULL,
2046 "Releasing device context failed.", "Error",
2047 MB_OK | MB_ICONERROR);
2048 }
2049 deviceContext = NULL;
2050 }
2051
2052 if (mainWindow != NULL)
2053 {
2054 SetMenu(mainWindow, NULL);
2055 DestroyWindow(mainWindow);
2056 mainWindow = NULL;
2057 }
2058
2059 UnregisterClass(AppName, appInstance);
2060 }
2061
2062
handleKey(WPARAM key,bool down)2063 void handleKey(WPARAM key, bool down)
2064 {
2065 int k = -1;
2066 int modifiers = 0;
2067
2068 if (GetKeyState(VK_SHIFT) & 0x8000)
2069 modifiers |= CelestiaCore::ShiftKey;
2070 if (GetKeyState(VK_CONTROL) & 0x8000)
2071 modifiers |= CelestiaCore::ControlKey;
2072
2073 switch (key)
2074 {
2075 case VK_UP:
2076 k = CelestiaCore::Key_Up;
2077 break;
2078 case VK_DOWN:
2079 k = CelestiaCore::Key_Down;
2080 break;
2081 case VK_LEFT:
2082 k = CelestiaCore::Key_Left;
2083 break;
2084 case VK_RIGHT:
2085 k = CelestiaCore::Key_Right;
2086 break;
2087 case VK_HOME:
2088 k = CelestiaCore::Key_Home;
2089 break;
2090 case VK_END:
2091 k = CelestiaCore::Key_End;
2092 break;
2093 case VK_PRIOR:
2094 k = CelestiaCore::Key_PageUp;
2095 break;
2096 case VK_NEXT:
2097 k = CelestiaCore::Key_PageDown;
2098 break;
2099 case VK_F1:
2100 k = CelestiaCore::Key_F1;
2101 break;
2102 case VK_F2:
2103 k = CelestiaCore::Key_F2;
2104 break;
2105 case VK_F3:
2106 k = CelestiaCore::Key_F3;
2107 break;
2108 case VK_F4:
2109 k = CelestiaCore::Key_F4;
2110 break;
2111 case VK_F5:
2112 k = CelestiaCore::Key_F5;
2113 break;
2114 case VK_F6:
2115 k = CelestiaCore::Key_F6;
2116 break;
2117 case VK_F7:
2118 k = CelestiaCore::Key_F7;
2119 break;
2120 case VK_F8:
2121 if (joystickAvailable && down)
2122 {
2123 appCore->joystickAxis(CelestiaCore::Joy_XAxis, 0);
2124 appCore->joystickAxis(CelestiaCore::Joy_YAxis, 0);
2125 appCore->joystickAxis(CelestiaCore::Joy_ZAxis, 0);
2126 useJoystick = !useJoystick;
2127 }
2128 break;
2129 case VK_F11:
2130 k = CelestiaCore::Key_F11;
2131 break;
2132 case VK_F12:
2133 k = CelestiaCore::Key_F12;
2134 break;
2135
2136 case VK_NUMPAD2:
2137 k = CelestiaCore::Key_NumPad2;
2138 break;
2139 case VK_NUMPAD4:
2140 k = CelestiaCore::Key_NumPad4;
2141 break;
2142 case VK_NUMPAD5:
2143 k = CelestiaCore::Key_NumPad5;
2144 break;
2145 case VK_NUMPAD6:
2146 k = CelestiaCore::Key_NumPad6;
2147 break;
2148 case VK_NUMPAD7:
2149 k = CelestiaCore::Key_NumPad7;
2150 break;
2151 case VK_NUMPAD8:
2152 k = CelestiaCore::Key_NumPad8;
2153 break;
2154 case VK_NUMPAD9:
2155 k = CelestiaCore::Key_NumPad9;
2156 break;
2157 case VK_DELETE:
2158 if (!down)
2159 appCore->charEntered('\177');
2160 break;
2161
2162 case '0':
2163 case '1':
2164 case '2':
2165 case '3':
2166 case '4':
2167 case '5':
2168 case '6':
2169 case '7':
2170 case '8':
2171 case '9':
2172 // Special handling required to send Ctrl+number keys to
2173 // Celestia keyboard handler.
2174 if (!down && (modifiers & CelestiaCore::ControlKey))
2175 appCore->charEntered((char) key, modifiers);
2176 break;
2177
2178 case 'A':
2179 case 'Z':
2180 if ((GetKeyState(VK_CONTROL) & 0x8000) == 0)
2181 k = key;
2182 break;
2183 }
2184
2185 if (k >= 0)
2186 {
2187 if (down)
2188 appCore->keyDown(k, modifiers);
2189 else
2190 appCore->keyUp(k, modifiers);
2191 }
2192 }
2193
2194
BuildScriptsMenu(HMENU menuBar,const string & scriptsDir)2195 static void BuildScriptsMenu(HMENU menuBar, const string& scriptsDir)
2196 {
2197 HMENU fileMenu = GetSubMenu(menuBar, 0);
2198
2199 if (ScriptMenuItems != NULL)
2200 delete ScriptMenuItems;
2201
2202 ScriptMenuItems = ScanScriptsDirectory(scriptsDir, false);
2203 if (ScriptMenuItems == NULL || ScriptMenuItems->size() == 0)
2204 {
2205 EnableMenuItem(fileMenu, ID_FILE_SCRIPTS, MF_GRAYED);
2206 return;
2207 }
2208
2209 MENUITEMINFO info;
2210 memset(&info, sizeof(info), 0);
2211 info.cbSize = sizeof(info);
2212 info.fMask = MIIM_SUBMENU;
2213
2214 BOOL result = GetMenuItemInfo(fileMenu, 1, TRUE, &info);
2215
2216 if (result)
2217 {
2218 HMENU scriptMenu = info.hSubMenu;
2219
2220 // Remove the old menu items
2221 int count = GetMenuItemCount(scriptMenu);
2222 while (count-- > 0)
2223 DeleteMenu(scriptMenu, 0, MF_BYPOSITION);
2224
2225 for (unsigned int i = 0; i < ScriptMenuItems->size(); i++)
2226 {
2227 AppendMenu(scriptMenu, MF_STRING, ID_FIRST_SCRIPT + i, UTF8ToCurrentCP((*ScriptMenuItems)[i].title).c_str());
2228 }
2229 }
2230 }
2231
2232
syncMenusWithRendererState()2233 static void syncMenusWithRendererState()
2234 {
2235 int renderFlags = appCore->getRenderer()->getRenderFlags();
2236 int labelMode = appCore->getRenderer()->getLabelMode();
2237 float ambientLight = appCore->getRenderer()->getAmbientLightLevel();
2238 unsigned int textureRes = appCore->getRenderer()->getResolution();
2239
2240 setMenuItemCheck(ID_VIEW_SHOW_FRAMES,
2241 appCore->getFramesVisible());
2242 setMenuItemCheck(ID_VIEW_SYNC_TIME,
2243 appCore->getSimulation()->getSyncTime());
2244
2245 if(abs(0.0 - (double)ambientLight) < 1.0e-3)
2246 {
2247 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_CHECKED);
2248 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_UNCHECKED);
2249 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
2250 }
2251 else if(abs(0.1 - (double)ambientLight) < 1.0e-3)
2252 {
2253 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_UNCHECKED);
2254 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_CHECKED);
2255 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
2256 }
2257 else if(abs(0.25 - (double)ambientLight) < 1.0e-3)
2258 {
2259 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_UNCHECKED);
2260 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_UNCHECKED);
2261 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_CHECKED);
2262 }
2263
2264 Renderer::StarStyle style = appCore->getRenderer()->getStarStyle();
2265 CheckMenuItem(menuBar, ID_RENDER_STARSTYLE_FUZZY,
2266 style == Renderer::FuzzyPointStars ? MF_CHECKED : MF_UNCHECKED);
2267 CheckMenuItem(menuBar, ID_RENDER_STARSTYLE_POINTS,
2268 style == Renderer::PointStars ? MF_CHECKED : MF_UNCHECKED);
2269 CheckMenuItem(menuBar, ID_RENDER_STARSTYLE_DISCS,
2270 style == Renderer::ScaledDiscStars ? MF_CHECKED : MF_UNCHECKED);
2271
2272 CheckMenuItem(menuBar, ID_RENDER_TEXTURERES_LOW,
2273 textureRes == 0 ? MF_CHECKED : MF_UNCHECKED);
2274 CheckMenuItem(menuBar, ID_RENDER_TEXTURERES_MEDIUM,
2275 textureRes == 1 ? MF_CHECKED : MF_UNCHECKED);
2276 CheckMenuItem(menuBar, ID_RENDER_TEXTURERES_HIGH,
2277 textureRes == 2 ? MF_CHECKED : MF_UNCHECKED);
2278
2279 MENUITEMINFO menuInfo;
2280 menuInfo.cbSize = sizeof(MENUITEMINFO);
2281 menuInfo.fMask = MIIM_STATE;
2282 if (GetMenuItemInfo(menuBar, ID_TIME_SHOWLOCAL, FALSE, &menuInfo))
2283 {
2284 if (appCore->getTimeZoneBias() == 0)
2285 CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_UNCHECKED);
2286 else
2287 CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_CHECKED);
2288 }
2289
2290 CheckMenuItem(menuBar, ID_RENDER_ANTIALIASING,
2291 (renderFlags & Renderer::ShowSmoothLines)?MF_CHECKED:MF_UNCHECKED);
2292 CheckMenuItem(menuBar, ID_RENDER_AUTOMAG,
2293 (renderFlags & Renderer::ShowAutoMag)?MF_CHECKED:MF_UNCHECKED);
2294 }
2295
2296
2297 class WinAlerter : public CelestiaCore::Alerter
2298 {
2299 private:
2300 HWND hwnd;
2301
2302 public:
WinAlerter()2303 WinAlerter() : hwnd(NULL) {};
~WinAlerter()2304 ~WinAlerter() {};
2305
fatalError(const std::string & msg)2306 void fatalError(const std::string& msg)
2307 {
2308 if (s_splash != NULL)
2309 s_splash->close();
2310
2311 MessageBox(NULL,
2312 msg.c_str(),
2313 "Fatal Error",
2314 MB_OK | MB_ICONERROR | MB_SETFOREGROUND);
2315 }
2316 };
2317
2318
InitJoystick(JOYCAPS & caps)2319 static bool InitJoystick(JOYCAPS& caps)
2320 {
2321 int nJoysticks = joyGetNumDevs();
2322 if (nJoysticks <= 0)
2323 return false;
2324
2325 if (joyGetDevCaps(JOYSTICKID1, &caps, sizeof caps) != JOYERR_NOERROR)
2326 {
2327 cout << "Error getting joystick caps.\n";
2328 return false;
2329 }
2330
2331 cout << "Using joystick: " << caps.szPname << '\n';
2332
2333 return true;
2334 }
2335
2336
HandleJoystick()2337 static void HandleJoystick()
2338 {
2339 JOYINFOEX info;
2340
2341 info.dwSize = sizeof info;
2342 info.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNBUTTONS;
2343 MMRESULT err = joyGetPosEx(JOYSTICKID1, &info);
2344
2345 if (err == JOYERR_NOERROR)
2346 {
2347 float x = (float) info.dwXpos / 32768.0f - 1.0f;
2348 float y = (float) info.dwYpos / 32768.0f - 1.0f;
2349
2350 appCore->joystickAxis(CelestiaCore::Joy_XAxis, x);
2351 appCore->joystickAxis(CelestiaCore::Joy_YAxis, y);
2352 appCore->joystickButton(CelestiaCore::JoyButton1,
2353 (info.dwButtons & 0x1) != 0);
2354 appCore->joystickButton(CelestiaCore::JoyButton2,
2355 (info.dwButtons & 0x2) != 0);
2356 appCore->joystickButton(CelestiaCore::JoyButton7,
2357 (info.dwButtons & 0x40) != 0);
2358 appCore->joystickButton(CelestiaCore::JoyButton8,
2359 (info.dwButtons & 0x80) != 0);
2360 }
2361 }
2362
GetRegistryValue(HKEY hKey,LPSTR cpValueName,LPVOID lpBuf,DWORD iBufSize)2363 static bool GetRegistryValue(HKEY hKey, LPSTR cpValueName, LPVOID lpBuf, DWORD iBufSize)
2364 {
2365 /*
2366 Function retrieves a value from the registry.
2367 Key specified by open handle.
2368
2369 hKey - Handle of open key for which a key value is requested.
2370 cpValueName - Name of Key Value to obtain value for.
2371 lpBuf - Buffer to receive value of Key Value.
2372 iBufSize - Size of buffer pointed to by lpBuf.
2373
2374 RETURN - true if value was successfully retrieved, false otherwise.
2375
2376 Remarks: If requesting a string value, pass the character buffer to lpBuf.
2377 If requesting a DWORD value, pass the DWORD variable's address to lpBuf.
2378 If requesting binary data, pass the address of the binary buffer.
2379
2380 This function assumes you have an open registry key. Be sure to call
2381 CloseKey() when finished.
2382 */
2383 DWORD dwValueType, dwDataSize=0;
2384 bool bRC=false;
2385
2386 dwDataSize = iBufSize;
2387 if(RegQueryValueEx(hKey, cpValueName, NULL, &dwValueType,
2388 (LPBYTE)lpBuf, &dwDataSize) == ERROR_SUCCESS)
2389 bRC = true;
2390
2391 return bRC;
2392 }
2393
SetRegistryInt(HKEY key,LPCTSTR value,int intVal)2394 static bool SetRegistryInt(HKEY key, LPCTSTR value, int intVal)
2395 {
2396 LONG err = RegSetValueEx(key,
2397 value,
2398 0,
2399 REG_DWORD,
2400 reinterpret_cast<CONST BYTE*>(&intVal),
2401 sizeof(DWORD));
2402 return err == ERROR_SUCCESS;
2403 }
2404
SetRegistry(HKEY key,LPCTSTR value,const string & strVal)2405 static bool SetRegistry(HKEY key, LPCTSTR value, const string& strVal)
2406 {
2407 LONG err = RegSetValueEx(key,
2408 value,
2409 0,
2410 REG_SZ,
2411 reinterpret_cast<CONST BYTE*> (strVal.c_str()),
2412 strVal.length() + 1);
2413
2414 return err == ERROR_SUCCESS;
2415 }
2416
SetRegistryBin(HKEY hKey,LPSTR cpValueName,LPVOID lpData,int iDataSize)2417 static bool SetRegistryBin(HKEY hKey, LPSTR cpValueName, LPVOID lpData, int iDataSize)
2418 {
2419 /*
2420 Function sets BINARY data in the registry.
2421 Key specified by open handle.
2422
2423 hKey - Handle of Key for which a value is to be set.
2424
2425 cpValueName - Name of Key Value to obtain value for.
2426
2427 lpData - Address of binary data to store.
2428
2429 iDataSize - Size of binary data contained in lpData.
2430
2431 RETURN - Boolean true if value is successfully stored, false otherwise.
2432
2433 Remarks: This function assumes you have an open registry key. Be sure to call
2434 CloseKey() when finished.
2435 */
2436
2437 bool bRC = false;
2438
2439 if (RegSetValueEx(hKey, cpValueName, 0, REG_BINARY,
2440 (LPBYTE) lpData, (DWORD) iDataSize) == ERROR_SUCCESS)
2441 {
2442 bRC = true;
2443 }
2444
2445 return bRC;
2446 }
2447
2448
LoadPreferencesFromRegistry(LPTSTR regkey,AppPreferences & prefs)2449 static bool LoadPreferencesFromRegistry(LPTSTR regkey, AppPreferences& prefs)
2450 {
2451 LONG err;
2452 HKEY key;
2453
2454 DWORD disposition;
2455 err = RegCreateKeyEx(HKEY_CURRENT_USER,
2456 regkey,
2457 0,
2458 NULL,
2459 REG_OPTION_NON_VOLATILE,
2460 KEY_ALL_ACCESS,
2461 NULL,
2462 &key,
2463 &disposition);
2464 if (err != ERROR_SUCCESS)
2465 {
2466 cout << "Error opening registry key: " << err << '\n';
2467 return false;
2468 }
2469
2470 GetRegistryValue(key, "Width", &prefs.winWidth, sizeof(prefs.winWidth));
2471 GetRegistryValue(key, "Height", &prefs.winHeight, sizeof(prefs.winHeight));
2472 GetRegistryValue(key, "XPos", &prefs.winX, sizeof(prefs.winX));
2473 GetRegistryValue(key, "YPos", &prefs.winY, sizeof(prefs.winY));
2474 GetRegistryValue(key, "RenderFlags", &prefs.renderFlags, sizeof(prefs.renderFlags));
2475 GetRegistryValue(key, "LabelMode", &prefs.labelMode, sizeof(prefs.labelMode));
2476 GetRegistryValue(key, "LocationFilter", &prefs.locationFilter, sizeof(prefs.locationFilter));
2477 GetRegistryValue(key, "OrbitMask", &prefs.orbitMask, sizeof(prefs.orbitMask));
2478 GetRegistryValue(key, "VisualMagnitude", &prefs.visualMagnitude, sizeof(prefs.visualMagnitude));
2479 GetRegistryValue(key, "AmbientLight", &prefs.ambientLight, sizeof(prefs.ambientLight));
2480 GetRegistryValue(key, "GalaxyLightGain", &prefs.galaxyLightGain, sizeof(prefs.galaxyLightGain));
2481 GetRegistryValue(key, "ShowLocalTime", &prefs.showLocalTime, sizeof(prefs.showLocalTime));
2482 GetRegistryValue(key, "DateFormat", &prefs.dateFormat, sizeof(prefs.dateFormat));
2483 GetRegistryValue(key, "HudDetail", &prefs.hudDetail, sizeof(prefs.hudDetail));
2484 GetRegistryValue(key, "FullScreenMode", &prefs.fullScreenMode, sizeof(prefs.fullScreenMode));
2485
2486 prefs.starStyle = Renderer::FuzzyPointStars;
2487 GetRegistryValue(key, "StarStyle", &prefs.starStyle, sizeof(prefs.starStyle));
2488 prefs.renderPath = GLContext::GLPath_Basic;
2489 prefs.renderPathSet = GetRegistryValue(key, "RenderPath", &prefs.renderPath, sizeof(prefs.renderPath));
2490
2491 GetRegistryValue(key, "LastVersion", &prefs.lastVersion, sizeof(prefs.lastVersion));
2492 GetRegistryValue(key, "TextureResolution", &prefs.textureResolution, sizeof(prefs.textureResolution));
2493
2494 char surfaceName[512];
2495 surfaceName[0] = '\0';
2496 if (GetRegistryValue(key, "AltSurface", surfaceName, sizeof(surfaceName)))
2497 prefs.altSurfaceName = string(surfaceName);
2498
2499 if (prefs.lastVersion < 0x01020500)
2500 {
2501 prefs.renderFlags |= Renderer::ShowCometTails;
2502 prefs.renderFlags |= Renderer::ShowRingShadows;
2503 }
2504
2505 RegCloseKey(key);
2506
2507 return true;
2508 }
2509
2510
SavePreferencesToRegistry(LPTSTR regkey,AppPreferences & prefs)2511 static bool SavePreferencesToRegistry(LPTSTR regkey, AppPreferences& prefs)
2512 {
2513 LONG err;
2514 HKEY key;
2515
2516 cout << "Saving preferences . . .\n";
2517 err = RegOpenKeyEx(HKEY_CURRENT_USER,
2518 regkey,
2519 0,
2520 KEY_ALL_ACCESS,
2521 &key);
2522 if (err != ERROR_SUCCESS)
2523 {
2524 cout << "Error opening registry key: " << err << '\n';
2525 return false;
2526 }
2527 cout << "Opened registry key\n";
2528
2529 SetRegistryInt(key, "Width", prefs.winWidth);
2530 SetRegistryInt(key, "Height", prefs.winHeight);
2531 SetRegistryInt(key, "XPos", prefs.winX);
2532 SetRegistryInt(key, "YPos", prefs.winY);
2533 SetRegistryInt(key, "RenderFlags", prefs.renderFlags);
2534 SetRegistryInt(key, "LabelMode", prefs.labelMode);
2535 SetRegistryInt(key, "LocationFilter", prefs.locationFilter);
2536 SetRegistryInt(key, "OrbitMask", prefs.orbitMask);
2537 SetRegistryBin(key, "VisualMagnitude", &prefs.visualMagnitude, sizeof(prefs.visualMagnitude));
2538 SetRegistryBin(key, "AmbientLight", &prefs.ambientLight, sizeof(prefs.ambientLight));
2539 SetRegistryBin(key, "GalaxyLightGain", &prefs.galaxyLightGain, sizeof(prefs.galaxyLightGain));
2540 SetRegistryInt(key, "ShowLocalTime", prefs.showLocalTime);
2541 SetRegistryInt(key, "DateFormat", prefs.dateFormat);
2542 SetRegistryInt(key, "HudDetail", prefs.hudDetail);
2543 SetRegistryInt(key, "FullScreenMode", prefs.fullScreenMode);
2544 SetRegistryInt(key, "LastVersion", prefs.lastVersion);
2545 SetRegistryInt(key, "StarStyle", prefs.starStyle);
2546 SetRegistryInt(key, "RenderPath", prefs.renderPath);
2547 SetRegistry(key, "AltSurface", prefs.altSurfaceName);
2548 SetRegistryInt(key, "TextureResolution", prefs.textureResolution);
2549
2550 RegCloseKey(key);
2551
2552 return true;
2553 }
2554
2555
GetCurrentPreferences(AppPreferences & prefs)2556 static bool GetCurrentPreferences(AppPreferences& prefs)
2557 {
2558 WINDOWPLACEMENT placement;
2559
2560 placement.length = sizeof(placement);
2561 if (!GetWindowPlacement(mainWindow, &placement))
2562 return false;
2563
2564 RECT rect = placement.rcNormalPosition;
2565 prefs.winX = rect.left;
2566 prefs.winY = rect.top;
2567 prefs.winWidth = rect.right - rect.left;
2568 prefs.winHeight = rect.bottom - rect.top;
2569 prefs.renderFlags = appCore->getRenderer()->getRenderFlags();
2570 prefs.labelMode = appCore->getRenderer()->getLabelMode();
2571 prefs.locationFilter = appCore->getSimulation()->getActiveObserver()->getLocationFilter();
2572 prefs.orbitMask = appCore->getRenderer()->getOrbitMask();
2573 prefs.visualMagnitude = appCore->getSimulation()->getFaintestVisible();
2574 prefs.ambientLight = appCore->getRenderer()->getAmbientLightLevel();
2575 prefs.galaxyLightGain = Galaxy::getLightGain();
2576 prefs.showLocalTime = (appCore->getTimeZoneBias() != 0);
2577 prefs.dateFormat = appCore->getDateFormat();
2578 prefs.hudDetail = appCore->getHudDetail();
2579 prefs.fullScreenMode = lastFullScreenMode;
2580 prefs.lastVersion = 0x01040100;
2581 prefs.altSurfaceName = appCore->getSimulation()->getActiveObserver()->getDisplayedSurface();
2582 prefs.starStyle = appCore->getRenderer()->getStarStyle();
2583 prefs.renderPath = appCore->getRenderer()->getGLContext()->getRenderPath();
2584 prefs.textureResolution = appCore->getRenderer()->getResolution();
2585
2586 return true;
2587 }
2588
2589
HandleCaptureImage(HWND hWnd)2590 static void HandleCaptureImage(HWND hWnd)
2591 {
2592 // Display File SaveAs dialog to allow user to specify name and location of
2593 // of captured screen image.
2594 OPENFILENAME Ofn;
2595 char szFile[_MAX_PATH+1], szFileTitle[_MAX_PATH+1];
2596
2597 szFile[0] = '\0';
2598 szFileTitle[0] = '\0';
2599
2600 // Initialize OPENFILENAME
2601 ZeroMemory(&Ofn, sizeof(OPENFILENAME));
2602 Ofn.lStructSize = sizeof(OPENFILENAME);
2603 Ofn.hwndOwner = hWnd;
2604 Ofn.lpstrFilter = "JPEG - JFIF Compliant\0*.jpg;*.jif;*.jpeg\0Portable Network Graphics\0*.png\0";
2605 Ofn.lpstrFile= szFile;
2606 Ofn.nMaxFile = sizeof(szFile);
2607 Ofn.lpstrFileTitle = szFileTitle;
2608 Ofn.nMaxFileTitle = sizeof(szFileTitle);
2609 Ofn.lpstrInitialDir = (LPSTR)NULL;
2610
2611 // Comment this out if you just want the standard "Save As" caption.
2612 //Ofn.lpstrTitle = "Save As - Specify File to Capture Image";
2613
2614 // OFN_HIDEREADONLY - Do not display read-only JPEG or PNG files
2615 // OFN_OVERWRITEPROMPT - If user selected a file, prompt for overwrite confirmation.
2616 Ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
2617
2618 // Display the Save dialog box.
2619 if (GetSaveFileName(&Ofn))
2620 {
2621 // If you got here, a path and file has been specified.
2622 // Ofn.lpstrFile contains full path to specified file
2623 // Ofn.lpstrFileTitle contains just the filename with extension
2624
2625 // Get the dimensions of the current viewport
2626 int viewport[4];
2627 glGetIntegerv(GL_VIEWPORT, viewport);
2628
2629 bool success = false;
2630
2631 DWORD nFileType=0;
2632 char defaultExtensions[][4] = {"jpg", "png"};
2633 if (Ofn.nFileExtension == 0)
2634 {
2635 // If no extension was specified, use the selection of filter to
2636 // determine which type of file should be created, instead of
2637 // just defaulting to JPEG.
2638 nFileType = Ofn.nFilterIndex;
2639 strcat(Ofn.lpstrFile, ".");
2640 strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
2641 }
2642 else if (*(Ofn.lpstrFile + Ofn.nFileExtension) == '\0')
2643 {
2644 // If just a period was specified for the extension, use the
2645 // selection of filter to determine which type of file should be
2646 // created instead of just defaulting to JPEG.
2647 nFileType = Ofn.nFilterIndex;
2648 strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
2649 }
2650 else
2651 {
2652 switch (DetermineFileType(Ofn.lpstrFile))
2653 {
2654 case Content_JPEG:
2655 nFileType = 1;
2656 break;
2657 case Content_PNG:
2658 nFileType = 2;
2659 break;
2660 default:
2661 nFileType = 0;
2662 break;
2663 }
2664 }
2665
2666 // Redraw to make sure that the back buffer is up to date
2667 appCore->draw();
2668
2669 if (nFileType == 1)
2670 {
2671 success = CaptureGLBufferToJPEG(string(Ofn.lpstrFile),
2672 viewport[0], viewport[1],
2673 viewport[2], viewport[3]);
2674 }
2675 else if (nFileType == 2)
2676 {
2677 success = CaptureGLBufferToPNG(string(Ofn.lpstrFile),
2678 viewport[0], viewport[1],
2679 viewport[2], viewport[3]);
2680 }
2681 else
2682 {
2683 // Invalid file extension specified.
2684 DPRINTF(0, "WTF? Unknown file extension specified for screen capture.\n");
2685 }
2686
2687 if (!success)
2688 {
2689 char errorMsg[64];
2690
2691 if(nFileType == 0)
2692 sprintf(errorMsg, "Specified file extension is not recognized.");
2693 else
2694 sprintf(errorMsg, "Could not save image file.");
2695
2696 MessageBox(hWnd, errorMsg, "Error", MB_OK | MB_ICONERROR);
2697 }
2698 }
2699 }
2700
2701
HandleCaptureMovie(HWND hWnd)2702 static void HandleCaptureMovie(HWND hWnd)
2703 {
2704 // TODO: The menu item should be disable so that the user doesn't even
2705 // have the opportunity to record two movies simultaneously; the only
2706 // thing missing to make this happen is notification when recording
2707 // is complete.
2708 if (appCore->isCaptureActive())
2709 {
2710 MessageBox(hWnd, "Stop current movie capture before starting another one.", "Error", MB_OK | MB_ICONERROR);
2711 return;
2712 }
2713
2714 // Display File SaveAs dialog to allow user to specify name and location of
2715 // of captured movie
2716 OPENFILENAME Ofn;
2717 char szFile[_MAX_PATH+1], szFileTitle[_MAX_PATH+1];
2718
2719 szFile[0] = '\0';
2720 szFileTitle[0] = '\0';
2721
2722 // Initialize OPENFILENAME
2723 ZeroMemory(&Ofn, sizeof(OPENFILENAME));
2724 Ofn.lStructSize = sizeof(OPENFILENAME);
2725 Ofn.hwndOwner = hWnd;
2726 Ofn.lpstrFilter = "Microsoft AVI\0*.avi\0";
2727 Ofn.lpstrFile= szFile;
2728 Ofn.nMaxFile = sizeof(szFile);
2729 Ofn.lpstrFileTitle = szFileTitle;
2730 Ofn.nMaxFileTitle = sizeof(szFileTitle);
2731 Ofn.lpstrInitialDir = (LPSTR)NULL;
2732
2733 // Comment this out if you just want the standard "Save As" caption.
2734 //Ofn.lpstrTitle = "Save As - Specify Output File for Capture Movie";
2735
2736 // OFN_HIDEREADONLY - Do not display read-only video files
2737 // OFN_OVERWRITEPROMPT - If user selected a file, prompt for overwrite confirmation.
2738 Ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_NOCHANGEDIR;
2739
2740 Ofn.hInstance = appInstance;
2741 Ofn.lpTemplateName = MAKEINTRESOURCE(IDD_MOVIE_PARAMS_CHOOSER);
2742 Ofn.lpfnHook = ChooseMovieParamsProc;
2743
2744 // Display the Save dialog box.
2745 if (GetSaveFileName(&Ofn))
2746 {
2747 // If you got here, a path and file has been specified.
2748 // Ofn.lpstrFile contains full path to specified file
2749 // Ofn.lpstrFileTitle contains just the filename with extension
2750
2751 bool success = false;
2752
2753 DWORD nFileType=0;
2754 char defaultExtensions[][4] = { "avi" };
2755 if (Ofn.nFileExtension == 0)
2756 {
2757 // If no extension was specified, use the selection of filter to
2758 // determine which type of file should be created, instead of
2759 // just defaulting to AVI.
2760 nFileType = Ofn.nFilterIndex;
2761 strcat(Ofn.lpstrFile, ".");
2762 strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
2763 }
2764 else if (*(Ofn.lpstrFile + Ofn.nFileExtension) == '\0')
2765 {
2766 // If just a period was specified for the extension, use
2767 // the selection of filter to determine which type of file
2768 // should be created, instead of just defaulting to AVI.
2769 nFileType = Ofn.nFilterIndex;
2770 strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
2771 }
2772 else
2773 {
2774 switch (DetermineFileType(Ofn.lpstrFile))
2775 {
2776 case Content_AVI:
2777 nFileType = 1;
2778 break;
2779 default:
2780 nFileType = 0;
2781 break;
2782 }
2783 }
2784
2785 if (nFileType != 1)
2786 {
2787 // Invalid file extension specified.
2788 DPRINTF(0, "Unknown file extension specified for movie capture.\n");
2789 }
2790 else
2791 {
2792 success = BeginMovieCapture(string(Ofn.lpstrFile),
2793 MovieSizes[movieSize][0],
2794 MovieSizes[movieSize][1],
2795 MovieFramerates[movieFramerate]);
2796 }
2797
2798 if (!success)
2799 {
2800 char errorMsg[64];
2801
2802 if (nFileType == 0)
2803 sprintf(errorMsg, "Specified file extension is not recognized.");
2804 else
2805 sprintf(errorMsg, "Could not capture movie.");
2806
2807 MessageBox(hWnd, errorMsg, "Error", MB_OK | MB_ICONERROR);
2808 }
2809 }
2810 }
2811
2812
HandleOpenScript(HWND hWnd,CelestiaCore * appCore)2813 static void HandleOpenScript(HWND hWnd, CelestiaCore* appCore)
2814 {
2815 // Display File Open dialog to allow user to specify name and location of
2816 // of captured screen image.
2817 OPENFILENAME Ofn;
2818 char szFile[_MAX_PATH + 1];
2819 char szFileTitle[_MAX_PATH + 1];
2820
2821 // Save the current directory
2822 char currentDir[_MAX_PATH + 1];
2823 currentDir[0] = '\0';
2824 GetCurrentDirectory(sizeof(currentDir), currentDir);
2825
2826 szFile[0] = '\0';
2827 szFileTitle[0] = '\0';
2828
2829 // Initialize OPENFILENAME
2830 ZeroMemory(&Ofn, sizeof(OPENFILENAME));
2831 Ofn.lStructSize = sizeof(OPENFILENAME);
2832 Ofn.hwndOwner = hWnd;
2833 Ofn.lpstrFilter = "Celestia Script\0*.celx;*.clx;*.cel\0";
2834 Ofn.lpstrFile= szFile;
2835 Ofn.nMaxFile = sizeof(szFile);
2836 Ofn.lpstrFileTitle = szFileTitle;
2837 Ofn.nMaxFileTitle = sizeof(szFileTitle);
2838 Ofn.lpstrInitialDir = (LPSTR)NULL;
2839
2840 // Comment this out if you just want the standard "Save As" caption.
2841 // Ofn.lpstrTitle = "Save As - Specify File to Capture Image";
2842
2843 // Display the Open dialog box.
2844 if (GetOpenFileName(&Ofn))
2845 {
2846 // If you got here, a path and file has been specified.
2847 // Ofn.lpstrFile contains full path to specified file
2848 // Ofn.lpstrFileTitle contains just the filename with extension
2849 ContentType type = DetermineFileType(Ofn.lpstrFile);
2850
2851 if (type == Content_CelestiaScript)
2852 {
2853 appCore->runScript(Ofn.lpstrFile);
2854 }
2855 else if (type == Content_CelestiaLegacyScript)
2856 {
2857 ifstream scriptfile(Ofn.lpstrFile);
2858 if (!scriptfile.good())
2859 {
2860 MessageBox(hWnd, "Error opening script file.", "Error",
2861 MB_OK | MB_ICONERROR);
2862 }
2863 else
2864 {
2865 CommandParser parser(scriptfile);
2866 CommandSequence* script = parser.parse();
2867 if (script == NULL)
2868 {
2869 const vector<string>* errors = parser.getErrors();
2870 const char* errorMsg = "";
2871 if (errors->size() > 0)
2872 errorMsg = (*errors)[0].c_str();
2873 MessageBox(hWnd, errorMsg, "Error in script file.",
2874 MB_OK | MB_ICONERROR);
2875 }
2876 else
2877 {
2878 appCore->cancelScript(); // cancel any running script
2879 appCore->runScript(script);
2880 }
2881 }
2882 }
2883 }
2884
2885 if (strlen(currentDir) != 0)
2886 SetCurrentDirectory(currentDir);
2887 }
2888
2889
operator <(const DEVMODE & a,const DEVMODE & b)2890 bool operator<(const DEVMODE& a, const DEVMODE& b)
2891 {
2892 if (a.dmBitsPerPel != b.dmBitsPerPel)
2893 return a.dmBitsPerPel < b.dmBitsPerPel;
2894 if (a.dmPelsWidth != b.dmPelsWidth)
2895 return a.dmPelsWidth < b.dmPelsWidth;
2896 if (a.dmPelsHeight != b.dmPelsHeight)
2897 return a.dmPelsHeight < b.dmPelsHeight;
2898 return a.dmDisplayFrequency < b.dmDisplayFrequency;
2899 }
2900
EnumerateDisplayModes(unsigned int minBPP)2901 vector<DEVMODE>* EnumerateDisplayModes(unsigned int minBPP)
2902 {
2903 vector<DEVMODE>* modes = new vector<DEVMODE>();
2904 if (modes == NULL)
2905 return NULL;
2906
2907 DEVMODE dm;
2908 int i = 0;
2909 while (EnumDisplaySettings(NULL, i, &dm))
2910 {
2911 if (dm.dmBitsPerPel >= minBPP)
2912 modes->insert(modes->end(), dm);
2913 i++;
2914 }
2915 sort(modes->begin(), modes->end());
2916
2917 // Bail out early if EnumDisplaySettings fails for some messed up reason
2918 if (i == 0)
2919 return modes;
2920
2921 // Go through the sorted list and eliminate modes that differ only
2922 // by refresh rates.
2923 vector<DEVMODE>::iterator keepIter = modes->begin();
2924 vector<DEVMODE>::const_iterator iter = modes->begin();
2925 iter++;
2926 for (; iter != modes->end(); iter++)
2927 {
2928 if (iter->dmPelsWidth != keepIter->dmPelsWidth ||
2929 iter->dmPelsHeight != keepIter->dmPelsHeight ||
2930 iter->dmBitsPerPel != keepIter->dmBitsPerPel)
2931 {
2932 *++keepIter = *iter;
2933 }
2934 }
2935
2936 modes->resize(keepIter - modes->begin() + 1);
2937
2938 // Select the fallback display mode--choose 640x480 at the current
2939 // pixel depth. If for some bizarre reason that's not available,
2940 // fall back to the first mode in the list.
2941 fallbackFullScreenMode = 0;
2942 for (iter = modes->begin(); iter != modes->end(); iter++)
2943 {
2944 if (iter->dmPelsWidth == 640 && iter->dmPelsHeight == 480)
2945 {
2946 // Add one to the mode index, since mode 0 means windowed mode
2947 fallbackFullScreenMode = (iter - modes->begin()) + 1;
2948 break;
2949 }
2950 }
2951 if (fallbackFullScreenMode == 0 && modes->size() > 0)
2952 fallbackFullScreenMode = 1;
2953 lastFullScreenMode = fallbackFullScreenMode;
2954
2955 return modes;
2956 }
2957
2958
skipSpace(char * s)2959 static char* skipSpace(char* s)
2960 {
2961 while (*s == ' ')
2962 s++;
2963 return s;
2964 }
2965
skipUntilQuote(char * s)2966 static char* skipUntilQuote(char* s)
2967 {
2968 while (*s != '"' && s != '\0')
2969 s++;
2970 return s;
2971 }
2972
nextWord(char * s)2973 static char* nextWord(char* s)
2974 {
2975 while (*s != ' ' && *s != '\0')
2976 s++;
2977 return s;
2978 }
2979
splitCommandLine(LPSTR cmdLine,int & argc)2980 static char** splitCommandLine(LPSTR cmdLine,
2981 int& argc)
2982 {
2983 int nArgs = 0;
2984 int maxArgs = 50;
2985 char** argv = new char*[maxArgs];
2986
2987 cmdLine = skipSpace(cmdLine);
2988 while (*cmdLine != '\0')
2989 {
2990 char* startOfWord = cmdLine;
2991 char* endOfWord = cmdLine;
2992 int wordLength = 0;
2993
2994 if (*cmdLine == '"')
2995 {
2996 // Handle quoted strings
2997 startOfWord = cmdLine + 1;
2998 endOfWord = skipUntilQuote(startOfWord);
2999 wordLength = endOfWord - startOfWord;
3000 if (*endOfWord != '\0')
3001 endOfWord++;
3002 }
3003 else
3004 {
3005 endOfWord = nextWord(cmdLine);
3006 wordLength = endOfWord - startOfWord;
3007 assert(wordLength != 0);
3008 }
3009
3010 char* newWord = new char[wordLength + 1];
3011 strncpy(newWord, startOfWord, wordLength);
3012 newWord[wordLength] = '\0';
3013
3014 if (nArgs == maxArgs)
3015 {
3016 char** newArgv = new char*[maxArgs * 2];
3017 copy(argv, argv + nArgs, newArgv);
3018 delete argv;
3019 argv = newArgv;
3020 maxArgs *= 2;
3021 }
3022
3023 argv[nArgs] = newWord;
3024 nArgs++;
3025
3026 cmdLine = endOfWord;
3027 cmdLine = skipSpace(cmdLine);
3028 }
3029
3030 argc = nArgs;
3031
3032 return argv;
3033 }
3034
3035
3036 static bool startFullscreen = false;
3037 static bool runOnce = false;
3038 static string startURL;
3039 static string startDirectory;
3040 static string startScript;
3041 static vector<string> extrasDirectories;
3042 static string configFileName;
3043 static bool useAlternateConfigFile = false;
3044 static bool skipSplashScreen = false;
3045
parseCommandLine(int argc,char * argv[])3046 static bool parseCommandLine(int argc, char* argv[])
3047 {
3048 int i = 0;
3049
3050 while (i < argc)
3051 {
3052 bool isLastArg = (i == argc - 1);
3053 if (strcmp(argv[i], "--verbose") == 0)
3054 {
3055 SetDebugVerbosity(1);
3056 }
3057 else if (strcmp(argv[i], "--fullscreen") == 0)
3058 {
3059 startFullscreen = true;
3060 }
3061 else if (strcmp(argv[i], "--once") == 0)
3062 {
3063 runOnce = true;
3064 }
3065 else if (strcmp(argv[i], "--dir") == 0)
3066 {
3067 if (isLastArg)
3068 {
3069 MessageBox(NULL,
3070 "Directory expected after --dir", "Celestia Command Line Error",
3071 MB_OK | MB_ICONERROR);
3072 return false;
3073 }
3074 i++;
3075 startDirectory = string(argv[i]);
3076 }
3077 else if (strcmp(argv[i], "--conf") == 0)
3078 {
3079 if (isLastArg)
3080 {
3081 MessageBox(NULL,
3082 "Configuration file name expected after --conf",
3083 "Celestia Command Line Error",
3084 MB_OK | MB_ICONERROR);
3085 return false;
3086 }
3087 i++;
3088 configFileName = string(argv[i]);
3089 useAlternateConfigFile = true;
3090 }
3091 else if (strcmp(argv[i], "--extrasdir") == 0)
3092 {
3093 if (isLastArg)
3094 {
3095 MessageBox(NULL,
3096 "Directory expected after --extrasdir", "Celestia Command Line Error",
3097 MB_OK | MB_ICONERROR);
3098 return false;
3099 }
3100 i++;
3101 extrasDirectories.push_back(string(argv[i]));
3102 }
3103 else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--url") == 0)
3104 {
3105 if (isLastArg)
3106 {
3107 MessageBox(NULL,
3108 "URL expected after --url", "Celestia Command Line Error",
3109 MB_OK | MB_ICONERROR);
3110 return false;
3111 }
3112 i++;
3113 startURL = string(argv[i]);
3114 }
3115 else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--nosplash") == 0)
3116 {
3117 skipSplashScreen = true;
3118 }
3119 else
3120 {
3121 char* buf = new char[strlen(argv[i]) + 256];
3122 sprintf(buf, "Invalid command line option '%s'", argv[i]);
3123 MessageBox(NULL,
3124 buf, "Celestia Command Line Error",
3125 MB_OK | MB_ICONERROR);
3126 delete[] buf;
3127 return false;
3128 }
3129
3130 i++;
3131 }
3132
3133 return true;
3134 }
3135
3136
3137 class WinSplashProgressNotifier : public ProgressNotifier
3138 {
3139 public:
WinSplashProgressNotifier(SplashWindow * _splash)3140 WinSplashProgressNotifier(SplashWindow* _splash) : splash(_splash) {};
~WinSplashProgressNotifier()3141 virtual ~WinSplashProgressNotifier() {};
3142
update(const string & filename)3143 virtual void update(const string& filename)
3144 {
3145 splash->setMessage(UTF8ToCurrentCP(_("Loading: ")) + filename);
3146 }
3147
3148 private:
3149 SplashWindow* splash;
3150 };
3151
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)3152 int APIENTRY WinMain(HINSTANCE hInstance,
3153 HINSTANCE hPrevInstance,
3154 LPSTR lpCmdLine,
3155 int nCmdShow)
3156 {
3157
3158 // Say we're not ready to render yet.
3159 bReady = false;
3160
3161 appInstance = hInstance;
3162
3163 int argc;
3164 char** argv;
3165 argv = splitCommandLine(lpCmdLine, argc);
3166 bool cmdLineOK = parseCommandLine(argc, argv);
3167 if (!cmdLineOK)
3168 return 1;
3169
3170 // If Celestia was invoked with the --once command line parameter,
3171 // check to see if there's already an instance of Celestia running.
3172 if (runOnce)
3173 {
3174 // The FindWindow method of checking for another running instance
3175 // is broken, and we should be using CreateMutex instead. But we'll
3176 // sort that out later . . .
3177 HWND existingWnd = FindWindow(AppName, AppName);
3178 if (existingWnd)
3179 {
3180 // If there's an existing instance and we've been given a
3181 // URL on the command line, send the URL to the running instance
3182 // of Celestia before terminating.
3183 if (startURL != "")
3184 {
3185 COPYDATASTRUCT cd;
3186 cd.dwData = 0;
3187 cd.cbData = startURL.length();
3188 cd.lpData = reinterpret_cast<void*>(const_cast<char*>(startURL.c_str()));
3189 SendMessage(existingWnd, WM_COPYDATA, 0, reinterpret_cast<LPARAM>(&cd));
3190 }
3191 SetForegroundWindow(existingWnd);
3192 Sleep(1000);
3193 exit(0);
3194 }
3195 }
3196
3197 // If a start directory was given on the command line, switch to it
3198 // now.
3199 if (startDirectory != "")
3200 SetCurrentDirectory(startDirectory.c_str());
3201
3202 s_splash = new SplashWindow("splash.png");
3203 s_splash->setMessage("Loading data files...");
3204 if (!skipSplashScreen)
3205 s_splash->showSplash();
3206
3207 OleInitialize(NULL);
3208 dropTarget = new CelestiaDropTarget();
3209 if (dropTarget)
3210 {
3211 if (CoLockObjectExternal(dropTarget, TRUE, TRUE) != S_OK)
3212 {
3213 cout << "Error locking drop target\n";
3214 delete dropTarget;
3215 dropTarget = NULL;
3216 }
3217 }
3218
3219 // Specify some default values in case registry keys are not found. Ideally, these
3220 // defaults should never be used--they should be overridden by settings in
3221 // celestia.cfg.
3222 AppPreferences prefs;
3223 prefs.winWidth = 800;
3224 prefs.winHeight = 600;
3225 prefs.winX = CW_USEDEFAULT;
3226 prefs.winY = CW_USEDEFAULT;
3227 prefs.ambientLight = 0.1f; // Low
3228 prefs.galaxyLightGain = 0.0f;
3229 prefs.labelMode = 0;
3230 prefs.locationFilter = 0;
3231 prefs.orbitMask = Body::Planet | Body::Moon;
3232 prefs.renderFlags = Renderer::DefaultRenderFlags;
3233
3234 prefs.visualMagnitude = 6.0f; //Default specified in Simulation::Simulation()
3235 prefs.showLocalTime = 0;
3236 prefs.dateFormat = 0;
3237 prefs.hudDetail = 1;
3238 prefs.fullScreenMode = -1;
3239 prefs.lastVersion = 0x00000000;
3240 prefs.textureResolution = 1;
3241 LoadPreferencesFromRegistry(CelestiaRegKey, prefs);
3242
3243 // Adjust window dimensions for screen dimensions
3244 int screenWidth = GetSystemMetrics(SM_CXSCREEN);
3245 int screenHeight = GetSystemMetrics(SM_CYSCREEN);
3246 if (prefs.winWidth > screenWidth)
3247 prefs.winWidth = screenWidth;
3248 if (prefs.winHeight > screenHeight)
3249 prefs.winHeight = screenHeight;
3250 if (prefs.winX != CW_USEDEFAULT && prefs.winY != CW_USEDEFAULT)
3251 {
3252 if (prefs.winX + prefs.winWidth > screenWidth)
3253 prefs.winX = screenWidth - prefs.winWidth;
3254 if (prefs.winY + prefs.winHeight > screenHeight)
3255 prefs.winY = screenHeight - prefs.winHeight;
3256 }
3257
3258 // Make sure windowRect contains default window size in case Celestia is
3259 // launched in fullscreen mode. Without this code, CreateOpenGLWindow()
3260 // will be called with windowRect = {0, 0, 0, 0} when the user switches to
3261 // windowed mode.
3262 windowRect.left = prefs.winX;
3263 windowRect.top = prefs.winY;
3264 windowRect.right = windowRect.left + prefs.winWidth;
3265 windowRect.bottom = windowRect.top + prefs.winHeight;
3266
3267 joystickAvailable = InitJoystick(joystickCaps);
3268
3269 displayModes = EnumerateDisplayModes(16);
3270
3271 // If full screen mode was found in registry, override default with it.
3272 if (prefs.fullScreenMode != -1)
3273 lastFullScreenMode = prefs.fullScreenMode;
3274
3275 // If the user changes the computer's graphics card or driver, the
3276 // number of display modes may change. Since we're storing a display
3277 // index this can cause troubles. Avoid out of range errors by
3278 // checking against the size of the mode list, falling back to a
3279 // safe mode if the last used mode index is now out of range.
3280 // TODO: A MUCH better fix for the problem of changing GPU/driver
3281 // is to store the mode parameters instead of just the mode index.
3282 if (lastFullScreenMode > displayModes->size())
3283 {
3284 lastFullScreenMode = fallbackFullScreenMode;
3285 }
3286
3287 appCore = new CelestiaCore();
3288 if (appCore == NULL)
3289 {
3290 if (s_splash != NULL)
3291 {
3292 s_splash->close();
3293 delete s_splash;
3294 s_splash = NULL;
3295 }
3296
3297 MessageBox(NULL,
3298 "Out of memory.", "Fatal Error",
3299 MB_OK | MB_ICONERROR | MB_TOPMOST);
3300 return false;
3301 }
3302
3303 // Get Application Path
3304 char appPath[_MAX_PATH];
3305 GetCurrentDirectory(_MAX_PATH - 1, appPath);
3306
3307 // Gettext integration
3308 setlocale(LC_ALL, "");
3309 setlocale(LC_NUMERIC, "C");
3310 string localeDir = (string)appPath + "/locale";
3311 bindtextdomain("celestia", localeDir.c_str());
3312 bind_textdomain_codeset("celestia", "UTF-8");
3313 bindtextdomain("celestia_constellations", localeDir.c_str());
3314 bind_textdomain_codeset("celestia_constellations", "UTF-8");
3315 textdomain("celestia");
3316
3317 // Loading localized resources
3318 char res[255];
3319 sprintf(res, "locale\\res_%s.dll", _("LANGUAGE"));
3320 int langID = 0;
3321 if (sscanf(_("WinLangID"), "%x", &langID) == 1)
3322 SetThreadLocale(langID);
3323 if ((hRes = LoadLibrary(res)) == NULL) {
3324 cout << "Couldn't load localized resources: "<< res<< "\n";
3325 hRes = hInstance;
3326 }
3327
3328 appCore->setAlerter(new WinAlerter());
3329
3330 WinSplashProgressNotifier* progressNotifier = NULL;
3331 if (!skipSplashScreen)
3332 progressNotifier = new WinSplashProgressNotifier(s_splash);
3333
3334 string* altConfig = useAlternateConfigFile ? &configFileName : NULL;
3335 bool initSucceeded = appCore->initSimulation(altConfig, &extrasDirectories, progressNotifier);
3336
3337 delete progressNotifier;
3338
3339 // Close the splash screen after all data has been loaded
3340 if (s_splash != NULL)
3341 {
3342 s_splash->close();
3343 delete s_splash;
3344 s_splash = NULL;
3345 }
3346
3347 // Give up now if we failed initialization
3348 if (!initSucceeded)
3349 return 1;
3350
3351 if (startURL != "")
3352 appCore->setStartURL(startURL);
3353
3354 menuBar = CreateMenuBar();
3355 acceleratorTable = LoadAccelerators(hRes,
3356 MAKEINTRESOURCE(IDR_ACCELERATORS));
3357
3358 if (appCore->getConfig() != NULL)
3359 {
3360 if (!compareIgnoringCase(appCore->getConfig()->cursor, "arrow"))
3361 hDefaultCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
3362 else if (!compareIgnoringCase(appCore->getConfig()->cursor, "inverting crosshair"))
3363 hDefaultCursor = LoadCursor(hRes, MAKEINTRESOURCE(IDC_CROSSHAIR));
3364 else
3365 hDefaultCursor = LoadCursor(hRes, MAKEINTRESOURCE(IDC_CROSSHAIR_OPAQUE));
3366 }
3367
3368 cursorHandler = new WinCursorHandler(hDefaultCursor);
3369 appCore->setCursorHandler(cursorHandler);
3370
3371 InitWGLExtensions(appInstance);
3372
3373 HWND hWnd;
3374 if (startFullscreen)
3375 {
3376 hWnd = CreateOpenGLWindow(0, 0, 800, 600,
3377 lastFullScreenMode, currentScreenMode);
3378
3379 // Prevent unnecessary destruction and recreation of OpenGLWindow in
3380 // while() loop below.
3381 newScreenMode = currentScreenMode;
3382 }
3383 else
3384 {
3385 hWnd = CreateOpenGLWindow(prefs.winX, prefs.winY,
3386 prefs.winWidth, prefs.winHeight,
3387 0, currentScreenMode);
3388 }
3389
3390 if (hWnd == NULL)
3391 {
3392 MessageBox(NULL,
3393 "Failed to create the application window.",
3394 "Fatal Error",
3395 MB_OK | MB_ICONERROR);
3396 return FALSE;
3397 }
3398
3399 if (dropTarget != NULL)
3400 {
3401 HRESULT hr = RegisterDragDrop(hWnd, dropTarget);
3402 if (hr != S_OK)
3403 {
3404 cout << "Failed to register drop target as OLE object.\n";
3405 }
3406 }
3407
3408 mainWindow = hWnd;
3409
3410 UpdateWindow(mainWindow);
3411
3412 // Initialize common controls
3413 INITCOMMONCONTROLSEX icex;
3414 icex.dwSize = sizeof(icex);
3415 icex.dwICC = ICC_DATE_CLASSES;
3416 InitCommonControlsEx(&icex);
3417
3418 extern void RegisterDatePicker();
3419 RegisterDatePicker();
3420
3421 if (!appCore->initRenderer())
3422 {
3423 return 1;
3424 }
3425
3426 // Set values saved in registry: renderFlags, visualMagnitude, labelMode and timezone bias.
3427 if (prefs.lastVersion != 0)
3428 {
3429 appCore->getSimulation()->setFaintestVisible(prefs.visualMagnitude);
3430 appCore->getRenderer()->setRenderFlags(prefs.renderFlags);
3431 appCore->getRenderer()->setLabelMode(prefs.labelMode);
3432 appCore->getSimulation()->getActiveObserver()->setLocationFilter(prefs.locationFilter);
3433 appCore->getRenderer()->setOrbitMask(prefs.orbitMask);
3434 appCore->getRenderer()->setAmbientLightLevel(prefs.ambientLight);
3435 Galaxy::setLightGain(prefs.galaxyLightGain);
3436 appCore->getRenderer()->setStarStyle(prefs.starStyle);
3437 appCore->setHudDetail(prefs.hudDetail);
3438 if (prefs.showLocalTime == 1)
3439 ShowLocalTime(appCore);
3440 else
3441 ShowUniversalTime(appCore);
3442 appCore->setDateFormat((astro::Date::Format) prefs.dateFormat);
3443 appCore->getSimulation()->getActiveObserver()->setDisplayedSurface(prefs.altSurfaceName);
3444 appCore->getRenderer()->setResolution(prefs.textureResolution);
3445 if (prefs.renderPathSet)
3446 {
3447 GLContext* glContext = appCore->getRenderer()->getGLContext();
3448 if (glContext->renderPathSupported(prefs.renderPath))
3449 glContext->setRenderPath(prefs.renderPath);
3450 }
3451 }
3452 else
3453 {
3454 // Set default render flags for a new installation
3455 appCore->getRenderer()->setRenderFlags(Renderer::DefaultRenderFlags);
3456 }
3457
3458 BuildFavoritesMenu(menuBar, appCore, appInstance, &odAppMenu);
3459 BuildScriptsMenu(menuBar, ScriptsDirectory);
3460 syncMenusWithRendererState();
3461
3462 appCore->setContextMenuCallback(ContextMenu);
3463
3464 bReady = true;
3465
3466 // Get the current time
3467 time_t systime = time(NULL);
3468 struct tm *gmt = gmtime(&systime);
3469 double timeTDB = astro::J2000;
3470 if (gmt != NULL)
3471 {
3472 astro::Date d;
3473 d.year = gmt->tm_year + 1900;
3474 d.month = gmt->tm_mon + 1;
3475 d.day = gmt->tm_mday;
3476 d.hour = gmt->tm_hour;
3477 d.minute = gmt->tm_min;
3478 d.seconds = (int) gmt->tm_sec;
3479 timeTDB = astro::UTCtoTDB(d);
3480 }
3481 appCore->start(timeTDB);
3482
3483 if (startURL != "")
3484 {
3485 COPYDATASTRUCT cd;
3486 cd.dwData = 0;
3487 cd.cbData = startURL.length();
3488 cd.lpData = reinterpret_cast<void*>(const_cast<char*>(startURL.c_str()));
3489 SendMessage(mainWindow, WM_COPYDATA, 0, reinterpret_cast<LPARAM>(&cd));
3490 }
3491
3492 // Initial tick required before first frame is rendered; this gives
3493 // start up scripts a chance to run.
3494 appCore->tick();
3495
3496 MSG msg;
3497 PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
3498 while (msg.message != WM_QUIT)
3499 {
3500 bool isVisible = !IsIconic(mainWindow);
3501
3502 // If Celestia is in an inactive state, use GetMessage to avoid
3503 // sucking CPU cycles. If time is paused and the camera isn't
3504 // moving in any view, we can probably also avoid constantly
3505 // rendering.
3506 BOOL haveMessage;
3507 if (isVisible)
3508 haveMessage = PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE);
3509 else
3510 haveMessage = GetMessage(&msg, NULL, 0U, 0U);
3511
3512 if (!haveMessage)
3513 {
3514 // Tick the simulation
3515 appCore->tick();
3516 }
3517
3518 if (haveMessage)
3519 {
3520 bool dialogMessage = false;
3521
3522 if (starBrowser != NULL &&
3523 IsDialogMessage(starBrowser->hwnd, &msg))
3524 dialogMessage = true;
3525 if (solarSystemBrowser != NULL &&
3526 IsDialogMessage(solarSystemBrowser->hwnd, &msg))
3527 dialogMessage = true;
3528 if (tourGuide != NULL &&
3529 IsDialogMessage(tourGuide->hwnd, &msg))
3530 dialogMessage = true;
3531 if (gotoObjectDlg != NULL &&
3532 IsDialogMessage(gotoObjectDlg->hwnd, &msg))
3533 dialogMessage = true;
3534 if (viewOptionsDlg != NULL &&
3535 IsDialogMessage(viewOptionsDlg->hwnd, &msg))
3536 dialogMessage = true;
3537 if (eclipseFinder != NULL &&
3538 IsDialogMessage(eclipseFinder->hwnd, &msg))
3539 dialogMessage = true;
3540 if (locationsDlg != NULL &&
3541 IsDialogMessage(locationsDlg->hwnd, &msg))
3542 dialogMessage = true;
3543
3544 // Translate and dispatch the message
3545 if (!dialogMessage)
3546 {
3547 if (!TranslateAccelerator(mainWindow, acceleratorTable, &msg))
3548 TranslateMessage(&msg);
3549 DispatchMessage(&msg);
3550 }
3551 }
3552 else
3553 {
3554 // And force a redraw
3555 InvalidateRect(mainWindow, NULL, FALSE);
3556 }
3557
3558 if (useJoystick)
3559 HandleJoystick();
3560
3561 if (currentScreenMode != newScreenMode)
3562 {
3563 if (currentScreenMode == 0)
3564 GetWindowRect(mainWindow, &windowRect);
3565 else
3566 lastFullScreenMode = currentScreenMode;
3567 DestroyOpenGLWindow();
3568 mainWindow = CreateOpenGLWindow(windowRect.left,
3569 windowRect.top,
3570 windowRect.right-windowRect.left,
3571 windowRect.bottom-windowRect.top,
3572 newScreenMode,
3573 currentScreenMode);
3574 UpdateWindow(mainWindow);
3575 }
3576 }
3577
3578 // Save application preferences
3579 {
3580 AppPreferences prefs;
3581 if (GetCurrentPreferences(prefs))
3582 SavePreferencesToRegistry(CelestiaRegKey, prefs);
3583 }
3584
3585 // Not ready to render anymore.
3586 bReady = false;
3587
3588 // Clean up the window
3589 if (currentScreenMode != 0)
3590 RestoreDisplayMode();
3591 DestroyOpenGLWindow();
3592
3593 if (appCore != NULL)
3594 delete appCore;
3595
3596 OleUninitialize();
3597
3598 return msg.wParam;
3599 }
3600
3601
modifiers(WPARAM wParam,WPARAM mods)3602 bool modifiers(WPARAM wParam, WPARAM mods)
3603 {
3604 return (wParam & mods) == mods;
3605 }
3606
3607
RestoreCursor()3608 static void RestoreCursor()
3609 {
3610 ShowCursor(TRUE);
3611 cursorVisible = true;
3612 SetCursorPos(saveCursorPos.x, saveCursorPos.y);
3613 }
3614
3615
MainWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)3616 LRESULT CALLBACK MainWindowProc(HWND hWnd,
3617 UINT uMsg,
3618 WPARAM wParam, LPARAM lParam)
3619 {
3620 switch(uMsg)
3621 {
3622 case WM_CREATE:
3623 //Instruct menu class to enumerate menu structure
3624 odAppMenu.Init(hWnd, menuBar);
3625
3626 //Associate some menu items with bitmap resources
3627 odAppMenu.SetItemImage(appInstance, ID_FILE_OPENSCRIPT, IDB_SCRIPT);
3628 odAppMenu.SetItemImage(appInstance, ID_FILE_CAPTUREIMAGE, IDB_CAMERA);
3629 odAppMenu.SetItemImage(appInstance, ID_FILE_CAPTUREMOVIE, IDB_CAMCORDER);
3630 odAppMenu.SetItemImage(appInstance, ID_FILE_EXIT, IDB_EXIT);
3631 odAppMenu.SetItemImage(appInstance, ID_TIME_SETTIME, IDB_CLOCK);
3632 odAppMenu.SetItemImage(appInstance, ID_TIME_FREEZE, IDB_STOP);
3633 odAppMenu.SetItemImage(appInstance, ID_RENDER_VIEWOPTIONS, IDB_SUNGLASSES);
3634 odAppMenu.SetItemImage(appInstance, ID_RENDER_LOCATIONS, IDB_GLOBE);
3635 odAppMenu.SetItemImage(appInstance, ID_HELP_RUNDEMO, IDB_SCRIPT);
3636 odAppMenu.SetItemImage(appInstance, ID_HELP_CONTROLS, IDB_CONFIG);
3637 odAppMenu.SetItemImage(appInstance, ID_HELP_ABOUT, IDB_ABOUT);
3638
3639 DragAcceptFiles(hWnd, TRUE);
3640 break;
3641
3642 case WM_DROPFILES:
3643 break;
3644
3645 case WM_MEASUREITEM:
3646 odAppMenu.MeasureItem(hWnd, lParam);
3647 return TRUE;
3648
3649 case WM_DRAWITEM:
3650 odAppMenu.DrawItem(hWnd, lParam);
3651 return TRUE;
3652
3653 case WM_MOUSEMOVE:
3654 {
3655 int x, y;
3656 x = LOWORD(lParam);
3657 y = HIWORD(lParam);
3658
3659 bool reallyMoved = x != lastMouseMove.x || y != lastMouseMove.y;
3660 lastMouseMove.x = x;
3661 lastMouseMove.y = y;
3662
3663 if (reallyMoved)
3664 {
3665 appCore->mouseMove((float) x, (float) y);
3666
3667 if ((wParam & (MK_LBUTTON | MK_RBUTTON)) != 0)
3668 {
3669 #ifdef INFINITE_MOUSE
3670 // A bit of mouse tweaking here . . . we want to allow the
3671 // user to rotate and zoom continuously, without having to
3672 // pick up the mouse every time it leaves the window. So,
3673 // once we start dragging, we'll hide the mouse and reset
3674 // its position every time it's moved.
3675 POINT pt;
3676 pt.x = lastX;
3677 pt.y = lastY;
3678 ClientToScreen(hWnd, &pt);
3679
3680 // If the cursor is still visible, this is the first mouse
3681 // move message of this drag. Hide the cursor and set the
3682 // cursor position to the center of the window. Once the
3683 // drag is complete, we'll restore the cursor position and
3684 // make it visible again.
3685 if (ignoreNextMoveEvent)
3686 {
3687 // This hack is required because there's a move event
3688 // right after canceling a context menu by clicking
3689 // outside of it. Because it was canceled by clicking,
3690 // the mouse button down bits are set, and the infinite
3691 // mouse code gets confused.
3692 ignoreNextMoveEvent = false;
3693 }
3694 else if (cursorVisible)
3695 {
3696 // Hide the cursor
3697 ShowCursor(FALSE);
3698 cursorVisible = false;
3699
3700 // Save the cursor position
3701 saveCursorPos = pt;
3702
3703 // Compute the center point of the client area
3704 RECT rect;
3705 GetClientRect(hWnd, &rect);
3706 POINT center;
3707 center.x = (rect.right - rect.left) / 2;
3708 center.y = (rect.bottom - rect.top) / 2;
3709
3710 // Set the cursor position to the center of the window
3711 x = center.x + (x - lastX);
3712 y = center.y + (y - lastY);
3713 lastX = center.x;
3714 lastY = center.y;
3715
3716 ClientToScreen(hWnd, ¢er);
3717 SetCursorPos(center.x, center.y);
3718 }
3719 else
3720 {
3721 if (x - lastX != 0 || y - lastY != 0)
3722 SetCursorPos(pt.x, pt.y);
3723 }
3724 #else
3725 lastX = x;
3726 lastY = y;
3727 #endif // INFINITE_MOUSE
3728 }
3729
3730 int buttons = 0;
3731 if ((wParam & MK_LBUTTON) != 0)
3732 buttons |= CelestiaCore::LeftButton;
3733 if ((wParam & MK_RBUTTON) != 0)
3734 buttons |= CelestiaCore::RightButton;
3735 if ((wParam & MK_MBUTTON) != 0)
3736 buttons |= CelestiaCore::MiddleButton;
3737 if ((wParam & MK_SHIFT) != 0)
3738 buttons |= CelestiaCore::ShiftKey;
3739 if ((wParam & MK_CONTROL) != 0)
3740 buttons |= CelestiaCore::ControlKey;
3741 appCore->mouseMove((float) (x - lastX),
3742 (float) (y - lastY),
3743 buttons);
3744
3745 if (currentScreenMode != 0)
3746 {
3747 if (hideMenuBar && y < 10)
3748 {
3749 SetMenu(mainWindow, menuBar);
3750 hideMenuBar = false;
3751 }
3752 else if (!hideMenuBar && y >= 10)
3753 {
3754 SetMenu(mainWindow, NULL);
3755 hideMenuBar = true;
3756 }
3757 }
3758 }
3759 }
3760
3761 break;
3762
3763 case WM_LBUTTONDOWN:
3764 lastX = LOWORD(lParam);
3765 lastY = HIWORD(lParam);
3766 appCore->mouseButtonDown(LOWORD(lParam), HIWORD(lParam),
3767 CelestiaCore::LeftButton);
3768 break;
3769 case WM_RBUTTONDOWN:
3770 lastX = LOWORD(lParam);
3771 lastY = HIWORD(lParam);
3772 appCore->mouseButtonDown(LOWORD(lParam), HIWORD(lParam),
3773 CelestiaCore::RightButton);
3774 break;
3775 case WM_MBUTTONDOWN:
3776 lastX = LOWORD(lParam);
3777 lastY = HIWORD(lParam);
3778 appCore->mouseButtonDown(LOWORD(lParam), HIWORD(lParam),
3779 CelestiaCore::MiddleButton);
3780 break;
3781
3782 case WM_LBUTTONUP:
3783 if (!cursorVisible)
3784 RestoreCursor();
3785 appCore->mouseButtonUp(LOWORD(lParam), HIWORD(lParam),
3786 CelestiaCore::LeftButton);
3787 break;
3788
3789 case WM_RBUTTONUP:
3790 if (!cursorVisible)
3791 RestoreCursor();
3792 appCore->mouseButtonUp(LOWORD(lParam), HIWORD(lParam),
3793 CelestiaCore::RightButton);
3794 break;
3795
3796 case WM_MBUTTONUP:
3797 lastX = LOWORD(lParam);
3798 lastY = HIWORD(lParam);
3799 appCore->mouseButtonUp(LOWORD(lParam), HIWORD(lParam),
3800 CelestiaCore::MiddleButton);
3801 break;
3802
3803 case WM_MOUSEWHEEL:
3804 {
3805 int modifiers = 0;
3806 if ((wParam & MK_SHIFT) != 0)
3807 modifiers |= CelestiaCore::ShiftKey;
3808 appCore->mouseWheel((short) HIWORD(wParam) > 0 ? -1.0f : 1.0f,
3809 modifiers);
3810 }
3811 break;
3812
3813 case WM_KEYDOWN:
3814 switch (wParam)
3815 {
3816 case VK_ESCAPE:
3817 appCore->charEntered('\033');
3818 break;
3819 case VK_INSERT:
3820 case 'C':
3821 if ((GetKeyState(VK_LCONTROL) | GetKeyState(VK_RCONTROL)) & 0x8000)
3822 {
3823 CopyStateURLToClipboard();
3824 appCore->flash(_("Copied URL"));
3825 }
3826 break;
3827 default:
3828 handleKey(wParam, true);
3829 break;
3830 }
3831 break;
3832
3833 case WM_KEYUP:
3834 handleKey(wParam, false);
3835 break;
3836
3837 case WM_CHAR:
3838 {
3839 // Bits 16-23 of lParam specify the scan code of the key pressed.
3840
3841 // Ignore all keypad input, this will be handled by WM_KEYDOWN
3842 // messages.
3843 char cScanCode = (char) (HIWORD(lParam) & 0xFF);
3844 if((cScanCode >= 71 && cScanCode <= 73) ||
3845 (cScanCode >= 75 && cScanCode <= 77) ||
3846 (cScanCode >= 79 && cScanCode <= 83))
3847 {
3848 break;
3849 }
3850
3851 int charCode = (char) wParam;
3852 int modifiers = 0;
3853 if (GetKeyState(VK_SHIFT) & 0x8000)
3854 modifiers |= CelestiaCore::ShiftKey;
3855
3856 // Catch backtab (shift+Tab)
3857 if (charCode == '\011' && (modifiers & CelestiaCore::ShiftKey))
3858 charCode = CelestiaCore::Key_BackTab;
3859
3860 Renderer* r = appCore->getRenderer();
3861 int oldRenderFlags = r->getRenderFlags();
3862 int oldLabelMode = r->getLabelMode();
3863
3864 // Convert charCode from current locale to UTF-8
3865 char utf8CharCode[7];
3866 memset(utf8CharCode, 0, sizeof(utf8CharCode));
3867 WCHAR wCharCode;
3868 MultiByteToWideChar(CP_ACP, 0, (char*)&charCode, 1, &wCharCode, 1);
3869 WideCharToMultiByte(CP_UTF8, 0, &wCharCode, 1, utf8CharCode, 7, 0, 0);
3870
3871 /*cerr << "Char input: (ANSI) " << (int)(unsigned char)charCode << " - UTF8 -> ";
3872 for(int i=0; utf8CharCode[i] != '\0'; i++) cerr << (int)(unsigned char)(utf8CharCode[i]) << " ";
3873 cerr << "[" << utf8CharCode << "]" << endl;*/
3874
3875 Renderer::StarStyle oldStarStyle = r->getStarStyle();
3876 appCore->charEntered(utf8CharCode, modifiers);
3877 if (r->getRenderFlags() != oldRenderFlags ||
3878 r->getLabelMode() != oldLabelMode ||
3879 r->getStarStyle() != oldStarStyle)
3880 {
3881 syncMenusWithRendererState();
3882 }
3883 }
3884 break;
3885
3886 case WM_IME_CHAR:
3887 {
3888 char ch[2];
3889 char utf8CharCode[7];
3890 memset(utf8CharCode, 0, sizeof(utf8CharCode));
3891 WCHAR wCharCode;
3892 ch[0] = (wParam >> 8);
3893 ch[1] = (wParam & 0xff);
3894 if (ch[0]) MultiByteToWideChar(CP_ACP, 0, ch, 2, &wCharCode, 1);
3895 else MultiByteToWideChar(CP_ACP, 0, &ch[1], 1, &wCharCode, 1);
3896 WideCharToMultiByte(CP_UTF8, 0, &wCharCode, 1, utf8CharCode, 7, 0, 0);
3897 appCore->charEntered(utf8CharCode);
3898 /*cerr << "IME input: (ANSI) " << (int)(unsigned char)ch[0] << " " << (int)(unsigned char)ch[1] << " - UTF8 -> ";
3899 for(int i=0; utf8CharCode[i] != '\0'; i++) cerr << (int)(unsigned char)(utf8CharCode[i]) << " ";
3900 cerr << "[" << utf8CharCode << "]" << endl;*/
3901 }
3902 break;
3903
3904 case WM_COPYDATA:
3905 // The copy data message is used to send URL strings between
3906 // processes.
3907 {
3908 COPYDATASTRUCT* cd = reinterpret_cast<COPYDATASTRUCT*>(lParam);
3909 if (cd != NULL && cd->lpData != NULL)
3910 {
3911 char* urlChars = reinterpret_cast<char*>(cd->lpData);
3912 if (cd->cbData > 3) // minimum of "cel:" or ".cel"
3913 {
3914 string urlString(urlChars, cd->cbData);
3915
3916 if (!urlString.substr(0,4).compare("cel:"))
3917 {
3918 appCore->flash(_("Loading URL"));
3919 appCore->goToUrl(urlString);
3920 }
3921 else if (DetermineFileType(urlString) == Content_CelestiaScript)
3922 {
3923 appCore->runScript(urlString);
3924 }
3925 else
3926 {
3927 ifstream scriptfile(urlString.c_str());
3928 if (!scriptfile.good())
3929 {
3930 appCore->flash(_("Error opening script"));
3931 }
3932 else
3933 {
3934 // TODO: Need to fix memory leak with scripts;
3935 // a refcount is probably required.
3936 CommandParser parser(scriptfile);
3937 CommandSequence* script = parser.parse();
3938 if (script == NULL)
3939 {
3940 const vector<string>* errors = parser.getErrors();
3941 const char* errorMsg = "";
3942 if (errors->size() > 0)
3943 {
3944 errorMsg = (*errors)[0].c_str();
3945 appCore->flash(errorMsg);
3946 }
3947 else
3948 {
3949 appCore->flash(_("Error loading script"));
3950 }
3951 }
3952 else
3953 {
3954 appCore->flash(_("Running script"));
3955 appCore->runScript(script);
3956 }
3957 }
3958 }
3959 }
3960 }
3961 }
3962 break;
3963
3964 case WM_COMMAND:
3965 switch (LOWORD(wParam))
3966 {
3967 case ID_NAVIGATION_CENTER:
3968 appCore->charEntered('c');
3969 break;
3970 case ID_NAVIGATION_GOTO:
3971 appCore->charEntered('G');
3972 break;
3973 case ID_NAVIGATION_FOLLOW:
3974 appCore->charEntered('F');
3975 break;
3976 case ID_NAVIGATION_SYNCORBIT:
3977 appCore->charEntered('Y');
3978 break;
3979 case ID_NAVIGATION_TRACK:
3980 appCore->charEntered('T');
3981 break;
3982 case ID_NAVIGATION_HOME:
3983 appCore->charEntered('H');
3984 break;
3985 case ID_NAVIGATION_SELECT:
3986 DialogBox(hRes, MAKEINTRESOURCE(IDD_FINDOBJECT), hWnd, FindObjectProc);
3987 break;
3988 case ID_NAVIGATION_GOTO_OBJECT:
3989 if (gotoObjectDlg == NULL)
3990 gotoObjectDlg = new GotoObjectDialog(hRes, hWnd, appCore);
3991 break;
3992 case IDCLOSE:
3993 if (reinterpret_cast<LPARAM>(gotoObjectDlg) == lParam &&
3994 gotoObjectDlg != NULL)
3995 {
3996 delete gotoObjectDlg;
3997 gotoObjectDlg = NULL;
3998 }
3999 else if (reinterpret_cast<LPARAM>(tourGuide) == lParam &&
4000 tourGuide != NULL)
4001 {
4002 delete tourGuide;
4003 tourGuide = NULL;
4004 }
4005 else if (reinterpret_cast<LPARAM>(starBrowser) == lParam &&
4006 starBrowser != NULL)
4007 {
4008 delete starBrowser;
4009 starBrowser = NULL;
4010 }
4011 else if (reinterpret_cast<LPARAM>(solarSystemBrowser) == lParam &&
4012 solarSystemBrowser != NULL)
4013 {
4014 delete solarSystemBrowser;
4015 solarSystemBrowser = NULL;
4016 }
4017 else if (reinterpret_cast<LPARAM>(viewOptionsDlg) == lParam &&
4018 viewOptionsDlg != NULL)
4019 {
4020 delete viewOptionsDlg;
4021 viewOptionsDlg = NULL;
4022 }
4023 else if (reinterpret_cast<LPARAM>(eclipseFinder) == lParam &&
4024 eclipseFinder != NULL)
4025 {
4026 delete eclipseFinder;
4027 eclipseFinder = NULL;
4028 }
4029 else if (reinterpret_cast<LPARAM>(locationsDlg) == lParam &&
4030 locationsDlg != NULL)
4031 {
4032 delete locationsDlg;
4033 locationsDlg = NULL;
4034 }
4035 break;
4036
4037 case ID_NAVIGATION_TOURGUIDE:
4038 if (tourGuide == NULL)
4039 tourGuide = new TourGuide(hRes, hWnd, appCore);
4040 break;
4041
4042 case ID_NAVIGATION_SSBROWSER:
4043 if (solarSystemBrowser == NULL)
4044 solarSystemBrowser = new SolarSystemBrowser(hRes, hWnd, appCore);
4045 break;
4046
4047 case ID_NAVIGATION_STARBROWSER:
4048 if (starBrowser == NULL)
4049 starBrowser = new StarBrowser(hRes, hWnd, appCore);
4050 break;
4051
4052 case ID_NAVIGATION_ECLIPSEFINDER:
4053 if (eclipseFinder == NULL)
4054 eclipseFinder = new EclipseFinderDialog(hRes, hWnd, appCore);
4055 break;
4056
4057 case ID_RENDER_DISPLAYMODE:
4058 newScreenMode = currentScreenMode;
4059 CreateDialogParam(hRes,
4060 MAKEINTRESOURCE(IDD_DISPLAYMODE),
4061 hWnd,
4062 SelectDisplayModeProc,
4063 NULL);
4064 break;
4065
4066 case ID_RENDER_FULLSCREEN:
4067 if (currentScreenMode == 0)
4068 newScreenMode = lastFullScreenMode;
4069 else
4070 newScreenMode = 0;
4071 break;
4072
4073 case ID_RENDER_VIEWOPTIONS:
4074 if (viewOptionsDlg == NULL)
4075 viewOptionsDlg = new ViewOptionsDialog(hRes, hWnd, appCore);
4076 break;
4077
4078 case ID_RENDER_LOCATIONS:
4079 if (locationsDlg == NULL)
4080 locationsDlg = new LocationsDialog(hRes, hWnd, appCore);
4081 break;
4082
4083 case ID_RENDER_MORESTARS:
4084 appCore->charEntered(']');
4085 break;
4086
4087 case ID_RENDER_FEWERSTARS:
4088 appCore->charEntered('[');
4089 break;
4090
4091 case ID_RENDER_AUTOMAG:
4092 appCore->charEntered('\031');
4093 syncMenusWithRendererState();
4094 break;
4095
4096 case ID_RENDER_AMBIENTLIGHT_NONE:
4097 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_CHECKED);
4098 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_UNCHECKED);
4099 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
4100 appCore->getRenderer()->setAmbientLightLevel(0.0f);
4101 break;
4102 case ID_RENDER_AMBIENTLIGHT_LOW:
4103 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_UNCHECKED);
4104 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_CHECKED);
4105 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
4106 appCore->getRenderer()->setAmbientLightLevel(0.1f);
4107 break;
4108 case ID_RENDER_AMBIENTLIGHT_MEDIUM:
4109 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE, MF_UNCHECKED);
4110 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW, MF_UNCHECKED);
4111 CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_CHECKED);
4112 appCore->getRenderer()->setAmbientLightLevel(0.25f);
4113 break;
4114
4115 case ID_RENDER_STARSTYLE_FUZZY:
4116 appCore->getRenderer()->setStarStyle(Renderer::FuzzyPointStars);
4117 syncMenusWithRendererState();
4118 break;
4119
4120 case ID_RENDER_STARSTYLE_POINTS:
4121 appCore->getRenderer()->setStarStyle(Renderer::PointStars);
4122 syncMenusWithRendererState();
4123 break;
4124
4125 case ID_RENDER_STARSTYLE_DISCS:
4126 appCore->getRenderer()->setStarStyle(Renderer::ScaledDiscStars);
4127 syncMenusWithRendererState();
4128 break;
4129
4130 case ID_RENDER_TEXTURERES_LOW:
4131 appCore->getRenderer()->setResolution(0);
4132 syncMenusWithRendererState();
4133 break;
4134 case ID_RENDER_TEXTURERES_MEDIUM:
4135 appCore->getRenderer()->setResolution(1);
4136 syncMenusWithRendererState();
4137 break;
4138 case ID_RENDER_TEXTURERES_HIGH:
4139 appCore->getRenderer()->setResolution(2);
4140 syncMenusWithRendererState();
4141 break;
4142
4143 case ID_RENDER_ANTIALIASING:
4144 appCore->charEntered('\030');
4145 syncMenusWithRendererState();
4146 break;
4147
4148 case ID_RENDER_BODY_AXES:
4149 appCore->toggleReferenceMark("body axes");
4150 break;
4151
4152 case ID_RENDER_FRAME_AXES:
4153 appCore->toggleReferenceMark("frame axes");
4154 break;
4155
4156 case ID_RENDER_SUN_DIRECTION:
4157 appCore->toggleReferenceMark("sun direction");
4158 break;
4159
4160 case ID_RENDER_VELOCITY_VECTOR:
4161 appCore->toggleReferenceMark("velocity vector");
4162 break;
4163
4164 case ID_RENDER_PLANETOGRAPHIC_GRID:
4165 appCore->toggleReferenceMark("planetographic grid");
4166 break;
4167
4168 case ID_RENDER_TERMINATOR:
4169 appCore->toggleReferenceMark("terminator");
4170 break;
4171
4172 case ID_TIME_FASTER:
4173 appCore->charEntered('l');
4174 break;
4175 case ID_TIME_SLOWER:
4176 appCore->charEntered('k');
4177 break;
4178 case ID_TIME_REALTIME:
4179 appCore->charEntered('\\');
4180 break;
4181
4182 case ID_TIME_FREEZE:
4183 appCore->charEntered(' ');
4184 break;
4185 case ID_TIME_REVERSE:
4186 appCore->charEntered('J');
4187 break;
4188 case ID_TIME_SETTIME:
4189 ShowSetTimeDialog(hRes, hWnd, appCore);
4190
4191 // Update the local time menu item--since the set time dialog handles setting the time zone,
4192 // should we just get rid of the menu item?
4193 if (appCore->getTimeZoneBias() == 0)
4194 CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_UNCHECKED);
4195 else
4196 CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_CHECKED);
4197 break;
4198 case ID_TIME_SHOWLOCAL:
4199 if (ToggleMenuItem(menuBar, ID_TIME_SHOWLOCAL))
4200 ShowLocalTime(appCore);
4201 else
4202 ShowUniversalTime(appCore);
4203 break;
4204
4205 case ID_VIEW_HSPLIT:
4206 appCore->splitView(View::HorizontalSplit);
4207 break;
4208
4209 case ID_VIEW_VSPLIT:
4210 appCore->splitView(View::VerticalSplit);
4211 break;
4212
4213 case ID_VIEW_SINGLE:
4214 appCore->singleView();
4215 break;
4216
4217 case ID_VIEW_DELETE_ACTIVE:
4218 appCore->deleteView();
4219 break;
4220
4221 case ID_VIEW_SHOW_FRAMES:
4222 appCore->setFramesVisible(!appCore->getFramesVisible());
4223 syncMenusWithRendererState();
4224 break;
4225
4226 case ID_VIEW_SYNC_TIME:
4227 {
4228 Simulation* sim = appCore->getSimulation();
4229 sim->setSyncTime(!sim->getSyncTime());
4230 if (sim->getSyncTime())
4231 sim->synchronizeTime();
4232 syncMenusWithRendererState();
4233 }
4234 break;
4235
4236 case ID_BOOKMARKS_ADDBOOKMARK:
4237 DialogBox(hRes, MAKEINTRESOURCE(IDD_ADDBOOKMARK), hWnd, AddBookmarkProc);
4238 break;
4239
4240 case ID_BOOKMARKS_ORGANIZE:
4241 DialogBox(hRes, MAKEINTRESOURCE(IDD_ORGANIZE_BOOKMARKS), hWnd, OrganizeBookmarksProc);
4242 break;
4243
4244 case ID_HELP_RUNDEMO:
4245 appCore->charEntered('D');
4246 break;
4247
4248 case ID_HELP_GUIDE:
4249 ShellExecute(hWnd, "open", "help\\CelestiaGuide.html", NULL, NULL, SW_NORMAL);
4250
4251 break;
4252
4253 case ID_HELP_CONTROLS:
4254 CreateDialogParam(hRes,
4255 MAKEINTRESOURCE(IDD_CONTROLSHELP),
4256 hWnd,
4257 ControlsHelpProc,
4258 NULL);
4259 break;
4260
4261 case ID_HELP_ABOUT:
4262 DialogBox(hRes, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutProc);
4263 break;
4264
4265 case ID_HELP_GLINFO:
4266 DialogBox(hRes, MAKEINTRESOURCE(IDD_GLINFO), hWnd, GLInfoProc);
4267 break;
4268
4269 case ID_HELP_LICENSE:
4270 DialogBox(hRes, MAKEINTRESOURCE(IDD_LICENSE), hWnd, LicenseProc);
4271 break;
4272
4273 case ID_INFO:
4274 ShowWWWInfo(appCore->getSimulation()->getSelection());
4275 break;
4276
4277 case ID_FILE_OPENSCRIPT:
4278 HandleOpenScript(hWnd, appCore);
4279 break;
4280
4281 case ID_FILE_CAPTUREIMAGE:
4282 HandleCaptureImage(hWnd);
4283 break;
4284
4285 case ID_FILE_CAPTUREMOVIE:
4286 HandleCaptureMovie(hWnd);
4287 break;
4288
4289 case ID_FILE_EXIT:
4290 SendMessage(hWnd, WM_CLOSE, 0, 0);
4291 break;
4292
4293 case ID_GOTO_URL:
4294 {
4295 // Relies on a pointer in lparam, do this does not
4296 // work cross-process.
4297 char* urlString = reinterpret_cast<char*>(lParam);
4298 if (urlString != NULL)
4299 {
4300 appCore->flash(string("URL: ") + string(urlString));
4301 appCore->goToUrl(urlString);
4302 }
4303 }
4304 break;
4305
4306 case ID_TOOLS_MARK:
4307 {
4308 Simulation* sim = appCore->getSimulation();
4309 if (sim->getUniverse() != NULL)
4310 {
4311 MarkerRepresentation markerRep(MarkerRepresentation::Diamond,
4312 10.0f,
4313 Color(0.0f, 1.0f, 0.0f, 0.9f));
4314
4315 sim->getUniverse()->markObject(sim->getSelection(),
4316 markerRep,
4317 1);
4318
4319 appCore->getRenderer()->setRenderFlags(appCore->getRenderer()->getRenderFlags() | Renderer::ShowMarkers);
4320 }
4321 }
4322 break;
4323
4324 case ID_TOOLS_UNMARK:
4325 {
4326 Simulation* sim = appCore->getSimulation();
4327 if (sim->getUniverse() != NULL)
4328 sim->getUniverse()->unmarkObject(sim->getSelection(), 1);
4329 }
4330 break;
4331
4332 default:
4333 {
4334 const FavoritesList* favorites = appCore->getFavorites();
4335 if (favorites != NULL &&
4336 LOWORD(wParam) >= ID_BOOKMARKS_FIRSTBOOKMARK &&
4337 LOWORD(wParam) - ID_BOOKMARKS_FIRSTBOOKMARK < (int) favorites->size())
4338 {
4339 int whichFavorite = LOWORD(wParam) - ID_BOOKMARKS_FIRSTBOOKMARK;
4340 appCore->activateFavorite(*(*favorites)[whichFavorite]);
4341 }
4342 else if (LOWORD(wParam) >= MENU_CHOOSE_PLANET &&
4343 LOWORD(wParam) < MENU_CHOOSE_PLANET + 1000)
4344 {
4345 // Handle the satellite/child object submenu
4346 Selection sel = appCore->getSimulation()->getSelection();
4347 switch (sel.getType())
4348 {
4349 case Selection::Type_Star:
4350 appCore->getSimulation()->selectPlanet(LOWORD(wParam) - MENU_CHOOSE_PLANET);
4351 break;
4352
4353 case Selection::Type_Body:
4354 {
4355 PlanetarySystem* satellites = (PlanetarySystem*) sel.body()->getSatellites();
4356 appCore->getSimulation()->setSelection(Selection(satellites->getBody(LOWORD(wParam) - MENU_CHOOSE_PLANET)));
4357 break;
4358 }
4359
4360 case Selection::Type_DeepSky:
4361 // Current deep sky object/galaxy implementation does
4362 // not have children to select.
4363 break;
4364
4365 case Selection::Type_Location:
4366 break;
4367
4368 default:
4369 break;
4370 }
4371 }
4372 else if (LOWORD(wParam) >= MENU_CHOOSE_SURFACE &&
4373 LOWORD(wParam) < MENU_CHOOSE_SURFACE + 1000)
4374 {
4375 // Handle the alternate surface submenu
4376 Selection sel = appCore->getSimulation()->getSelection();
4377 if (sel.body() != NULL)
4378 {
4379 int index = (int) LOWORD(wParam) - MENU_CHOOSE_SURFACE - 1;
4380 vector<string>* surfNames = sel.body()->getAlternateSurfaceNames();
4381 if (surfNames != NULL)
4382 {
4383 string surfName;
4384 if (index >= 0 && index < (int) surfNames->size())
4385 surfName = surfNames->at(index);
4386 appCore->getSimulation()->getActiveObserver()->setDisplayedSurface(surfName);
4387 delete surfNames;
4388 }
4389 }
4390 }
4391 else if (LOWORD(wParam) >= ID_FIRST_SCRIPT &&
4392 LOWORD(wParam) < ID_FIRST_SCRIPT + ScriptMenuItems->size())
4393 {
4394 // Handle the script menu
4395 unsigned int scriptIndex = LOWORD(wParam) - ID_FIRST_SCRIPT;
4396 appCore->runScript((*ScriptMenuItems)[scriptIndex].filename);
4397 }
4398 }
4399 break;
4400 }
4401 break;
4402
4403 case WM_CLOSE:
4404 PostQuitMessage(0);
4405 break;
4406
4407 case WM_SIZE:
4408 appCore->resize(LOWORD(lParam), HIWORD(lParam));
4409 break;
4410
4411 case WM_PAINT:
4412 if (bReady)
4413 {
4414 appCore->draw();
4415 SwapBuffers(deviceContext);
4416 ValidateRect(hWnd, NULL);
4417 }
4418 break;
4419
4420 default:
4421 return DefWindowProc( hWnd, uMsg, wParam, lParam );
4422 }
4423
4424 return 0;
4425 }
4426