winmain.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:138k
源码类别:

OpenGL

开发平台:

Visual C++

  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. #include <iostream>
  12. #include <fstream>
  13. #include <sstream>
  14. #include <algorithm>
  15. #include <set>
  16. #include <cstdlib>
  17. #include <cctype>
  18. #include <cstring>
  19. #include <cassert>
  20. #include <process.h>
  21. #include <time.h>
  22. #include <windows.h>
  23. #include <commctrl.h>
  24. #include <mmsystem.h>
  25. #include <celmath/vecmath.h>
  26. #include <celmath/quaternion.h>
  27. #include <celmath/mathlib.h>
  28. #include <celutil/debug.h>
  29. #include <celutil/util.h>
  30. #include <celutil/winutil.h>
  31. #include <celutil/filetype.h>
  32. #include <celengine/celestia.h>
  33. #include <celengine/astro.h>
  34. #include <celengine/cmdparser.h>
  35. #include <celengine/axisarrow.h>
  36. #include <celengine/planetgrid.h>
  37. #include "../celengine/gl.h"
  38. #include "../celengine/glext.h"
  39. #include "celestiacore.h"
  40. #include "imagecapture.h"
  41. #include "avicapture.h"
  42. #include "url.h"
  43. #include "winstarbrowser.h"
  44. #include "winssbrowser.h"
  45. #include "wintourguide.h"
  46. #include "wingotodlg.h"
  47. #include "winviewoptsdlg.h"
  48. #include "winlocations.h"
  49. #include "winbookmarks.h"
  50. #include "wineclipses.h"
  51. #include "winhyperlinks.h"
  52. #include "wintime.h"
  53. #include "winsplash.h"
  54. #include "odmenu.h"
  55. #include "scriptmenu.h"
  56. #include "res/resource.h"
  57. #include "wglext.h"
  58. #include <locale.h>
  59. using namespace std;
  60. typedef pair<int,string> IntStrPair;
  61. typedef vector<IntStrPair> IntStrPairVec;
  62. char AppName[] = "Celestia";
  63. static CelestiaCore* appCore = NULL;
  64. // Display modes for full screen operation
  65. static vector<DEVMODE>* displayModes = NULL;
  66. // Display mode indices
  67. static int currentScreenMode = 0;
  68. static int newScreenMode = 0;
  69. // The last fullscreen mode set; saved and restored from the registry
  70. static int lastFullScreenMode = 0;
  71. // A fullscreen mode guaranteed to work
  72. static int fallbackFullScreenMode = 0;
  73. static RECT windowRect;
  74. static HGLRC glContext;
  75. static HDC deviceContext;
  76. static bool bReady = false;
  77. static LPTSTR CelestiaRegKey = "Software\Shatters.net\Celestia";
  78. HINSTANCE appInstance;
  79. HMODULE hRes;
  80. HWND mainWindow = 0;
  81. static SolarSystemBrowser* solarSystemBrowser = NULL;
  82. static StarBrowser* starBrowser = NULL;
  83. static TourGuide* tourGuide = NULL;
  84. static GotoObjectDialog* gotoObjectDlg = NULL;
  85. static ViewOptionsDialog* viewOptionsDlg = NULL;
  86. static EclipseFinderDialog* eclipseFinder = NULL;
  87. static LocationsDialog* locationsDlg = NULL;
  88. static SplashWindow* s_splash = NULL;
  89. static HMENU menuBar = 0;
  90. ODMenu odAppMenu;
  91. static HACCEL acceleratorTable = 0;
  92. static bool hideMenuBar = false;
  93. // Joystick info
  94. static bool useJoystick = false;
  95. static bool joystickAvailable = false;
  96. static JOYCAPS joystickCaps;
  97. static HCURSOR hDefaultCursor = 0;
  98. bool cursorVisible = true;
  99. static POINT saveCursorPos;
  100. static POINT lastMouseMove;
  101. class WinCursorHandler;
  102. WinCursorHandler* cursorHandler = NULL;
  103. static int MovieSizes[8][2] = {
  104.                                 { 160, 120 },
  105.                                 { 320, 240 },
  106.                                 { 640, 480 },
  107.                                 { 720, 480 },
  108.                                 { 720, 576 },
  109.                                 { 1024, 768 },
  110.                                 { 1280, 720 },
  111.                                 { 1920, 1080 }
  112.                               };
  113. static float MovieFramerates[5] = { 15.0f, 24.0f, 25.0f, 29.97f, 30.0f };
  114. static int movieSize = 1;
  115. static int movieFramerate = 1;
  116. astro::Date newTime(0.0);
  117. #define REFMARKS 1
  118. #define INFINITE_MOUSE
  119. static int lastX = 0;
  120. static int lastY = 0;
  121. static bool ignoreNextMoveEvent = false;
  122. static const WPARAM ID_GOTO_URL = 62000;
  123. HWND hBookmarkTree;
  124. char bookmarkName[33];
  125. static const string ScriptsDirectory = "scripts";
  126. static vector<ScriptMenuItem>* ScriptMenuItems = NULL;
  127. static LRESULT CALLBACK MainWindowProc(HWND hWnd,
  128.                                        UINT uMsg,
  129.                                        WPARAM wParam, LPARAM lParam);
  130. #define MENU_CHOOSE_PLANET   32000
  131. #define MENU_CHOOSE_SURFACE  31000
  132. struct AppPreferences
  133. {
  134.     int winWidth;
  135.     int winHeight;
  136.     int winX;
  137.     int winY;
  138.     int renderFlags;
  139.     int labelMode;
  140.     int orbitMask;
  141.     float visualMagnitude;
  142.     float ambientLight;
  143.     float galaxyLightGain;
  144.     int showLocalTime;
  145.     int dateFormat;
  146.     int hudDetail;
  147.     int fullScreenMode;
  148.     uint32 lastVersion;
  149.     string altSurfaceName;
  150.     uint32 textureResolution;
  151.     Renderer::StarStyle starStyle;
  152.     GLContext::GLRenderPath renderPath;
  153.     bool renderPathSet;
  154. };
  155. void ChangeDisplayMode()
  156. {
  157.     DEVMODE device_mode;
  158.     memset(&device_mode, 0, sizeof(DEVMODE));
  159.     device_mode.dmSize = sizeof(DEVMODE);
  160.     device_mode.dmPelsWidth  = 800;
  161.     device_mode.dmPelsHeight = 600;
  162.     device_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
  163.     ChangeDisplaySettings(&device_mode, CDS_FULLSCREEN);
  164. }
  165. void RestoreDisplayMode()
  166. {
  167.     ChangeDisplaySettings(0, 0);
  168. }
  169. //
  170. // A very minimal IDropTarget interface implementation
  171. //
  172. class CelestiaDropTarget : public IDropTarget
  173. {
  174. public:
  175.     CelestiaDropTarget();
  176.     ~CelestiaDropTarget();
  177.     STDMETHOD  (QueryInterface)(REFIID idd, void** ppvObject);
  178.     STDMETHOD_ (ULONG, AddRef) (void);
  179.     STDMETHOD_ (ULONG, Release) (void);
  180.     // IDropTarget methods
  181.     STDMETHOD (DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
  182.     STDMETHOD (DragOver) (DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
  183.     STDMETHOD (DragLeave)(void);
  184.     STDMETHOD (Drop)     (LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
  185. private:
  186.     ULONG refCount;
  187. };
  188. static CelestiaDropTarget* dropTarget = NULL;
  189. CelestiaDropTarget::CelestiaDropTarget() :
  190.     refCount(0)
  191. {
  192. }
  193. CelestiaDropTarget::~CelestiaDropTarget()
  194. {
  195. }
  196. HRESULT CelestiaDropTarget::QueryInterface(REFIID iid, void** ppvObject)
  197. {
  198.     if (iid == IID_IUnknown || iid == IID_IDropTarget)
  199.     {
  200.         *ppvObject = this;
  201.         AddRef();
  202.         return ResultFromScode(S_OK);
  203.     }
  204.     else
  205.     {
  206.         *ppvObject = NULL;
  207.         return ResultFromScode(E_NOINTERFACE);
  208.     }
  209. }
  210. ULONG CelestiaDropTarget::AddRef(void)
  211. {
  212.     return ++refCount;
  213. }
  214. ULONG CelestiaDropTarget::Release(void)
  215. {
  216.     if (--refCount == 0)
  217.     {
  218.         delete this;
  219.         return 0;
  220.     }
  221.     return refCount;
  222. }
  223. STDMETHODIMP
  224. CelestiaDropTarget::DragEnter(IDataObject* pDataObject,
  225.                               DWORD grfKeyState,
  226.                               POINTL pt,
  227.                               DWORD* pdwEffect)
  228. {
  229.     return S_OK;
  230. }
  231. STDMETHODIMP
  232. CelestiaDropTarget::DragOver(DWORD grfKeyState,
  233.                              POINTL pt,
  234.                              DWORD* pdwEffect)
  235. {
  236.     return S_OK;
  237. }
  238. STDMETHODIMP
  239. CelestiaDropTarget::DragLeave(void)
  240. {
  241.     return S_OK;
  242. }
  243. STDMETHODIMP
  244. CelestiaDropTarget::Drop(IDataObject* pDataObject,
  245.                          DWORD grfKeyState,
  246.                          POINTL pt,
  247.                          DWORD* pdwEffect)
  248. {
  249.     IEnumFORMATETC* enumFormat = NULL;
  250.     HRESULT hr = pDataObject->EnumFormatEtc(DATADIR_GET, &enumFormat);
  251.     if (FAILED(hr) || enumFormat == NULL)
  252.         return E_FAIL;
  253.     FORMATETC format;
  254.     ULONG nFetched;
  255.     while (enumFormat->Next(1, &format, &nFetched) == S_OK)
  256.     {
  257.         char buf[512];
  258.         if (GetClipboardFormatName(format.cfFormat, buf, 511) != 0 &&
  259.             !strcmp(buf, "UniformResourceLocator"))
  260.         {
  261.             STGMEDIUM medium;
  262.             if (pDataObject->GetData(&format, &medium) == S_OK)
  263.             {
  264.                 if (medium.tymed == TYMED_HGLOBAL && medium.hGlobal != 0)
  265.                 {
  266.                     char* s = (char*) GlobalLock(medium.hGlobal);
  267. appCore->goToUrl(s);
  268.                     GlobalUnlock(medium.hGlobal);
  269.                     break;
  270.                 }
  271.             }
  272.         }
  273.     }
  274.     enumFormat->Release();
  275.     return E_FAIL;
  276. }
  277. // Cursor handler callback class for Windows.  We pass an instance to
  278. // the app core which the calls the setCursorShape method to change
  279. // the cursor icon.
  280. class WinCursorHandler : public CelestiaCore::CursorHandler
  281. {
  282. public:
  283.     WinCursorHandler(HCURSOR _defaultCursor);
  284.     virtual ~WinCursorHandler();
  285.     virtual void setCursorShape(CelestiaCore::CursorShape);
  286.     virtual CelestiaCore::CursorShape getCursorShape() const;
  287. private:
  288.     CelestiaCore::CursorShape shape;
  289.     HCURSOR defaultCursor;
  290.     HCURSOR sizeVertical;
  291.     HCURSOR sizeHorizontal;
  292. };
  293. WinCursorHandler::WinCursorHandler(HCURSOR _defaultCursor) :
  294.     shape(CelestiaCore::ArrowCursor),
  295.     defaultCursor(_defaultCursor)
  296. {
  297.     sizeVertical   = LoadCursor(NULL, IDC_SIZENS);
  298.     sizeHorizontal = LoadCursor(NULL, IDC_SIZEWE);
  299. }
  300. WinCursorHandler::~WinCursorHandler()
  301. {
  302. }
  303. void WinCursorHandler::setCursorShape(CelestiaCore::CursorShape _shape)
  304. {
  305.     shape = _shape;
  306.     switch (shape)
  307.     {
  308.     case CelestiaCore::SizeVerCursor:
  309.         SetCursor(sizeVertical);
  310.         break;
  311.     case CelestiaCore::SizeHorCursor:
  312.         SetCursor(sizeHorizontal);
  313.         break;
  314.     default:
  315.         SetCursor(defaultCursor);
  316.         break;
  317.     }
  318. }
  319. CelestiaCore::CursorShape WinCursorHandler::getCursorShape() const
  320. {
  321.     return shape;
  322. }
  323. // end WinCursorHandler methods
  324. static void ShowUniversalTime(CelestiaCore* appCore)
  325. {
  326.     appCore->setTimeZoneBias(0);
  327.     appCore->setTimeZoneName("UTC");
  328. }
  329. static void ShowLocalTime(CelestiaCore* appCore)
  330. {
  331.     TIME_ZONE_INFORMATION tzi;
  332.     DWORD dst = GetTimeZoneInformation(&tzi);
  333.     if (dst != TIME_ZONE_ID_INVALID)
  334.     {
  335.         LONG dstBias = 0;
  336.         WCHAR* tzName = NULL;
  337.         if (dst == TIME_ZONE_ID_STANDARD)
  338.         {
  339.             dstBias = tzi.StandardBias;
  340.             tzName = tzi.StandardName;
  341.         }
  342.         else if (dst == TIME_ZONE_ID_DAYLIGHT)
  343.         {
  344.             dstBias = tzi.DaylightBias;
  345.             tzName = tzi.DaylightName;
  346.         }
  347.         appCore->setTimeZoneName("   ");
  348.         appCore->setTimeZoneBias((tzi.Bias + dstBias) * -60);
  349.     }
  350. }
  351. static bool BeginMovieCapture(const std::string& filename,
  352.                               int width, int height,
  353.                               float framerate)
  354. {
  355.     MovieCapture* movieCapture = new AVICapture();
  356.     bool success = movieCapture->start(filename, width, height, framerate);
  357.     if (success)
  358.         appCore->initMovieCapture(movieCapture);
  359.     else
  360.         delete movieCapture;
  361.     return success;
  362. }
  363. static bool CopyStateURLToClipboard()
  364. {
  365.     BOOL b;
  366.     b = OpenClipboard(mainWindow);
  367.     if (!b)
  368.         return false;
  369.     CelestiaState appState;
  370.     appState.captureState(appCore);
  371.     
  372.     Url url(appState, Url::CurrentVersion);
  373.     string urlString = url.getAsString();
  374.     char* s = const_cast<char*>(urlString.c_str());
  375.     HGLOBAL clipboardDataHandle = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
  376.                                               strlen(s) + 1);
  377.     char* clipboardData = (char*) GlobalLock(clipboardDataHandle);
  378.     if (clipboardData != NULL)
  379.     {
  380.         strcpy(clipboardData, s);
  381.         GlobalUnlock(clipboardDataHandle);
  382.         EmptyClipboard();
  383.         HANDLE h = SetClipboardData(CF_TEXT, clipboardDataHandle);
  384.         CloseClipboard();
  385.         return h != NULL;
  386.     }
  387.     else
  388.     {
  389.         CloseClipboard();
  390.         return false;
  391.     }
  392. }
  393. static bool ToggleMenuItem(HMENU menu, int id)
  394. {
  395.     MENUITEMINFO menuInfo;
  396.     menuInfo.cbSize = sizeof(MENUITEMINFO);
  397.     menuInfo.fMask = MIIM_STATE;
  398.     if (GetMenuItemInfo(menu, id, FALSE, &menuInfo))
  399.     {
  400.         bool isChecked = ((menuInfo.fState & MFS_CHECKED) != 0);
  401.         CheckMenuItem(menu, id, isChecked ? MF_UNCHECKED : MF_CHECKED);
  402.         return !isChecked;
  403.     }
  404.     return false;
  405. }
  406. bool LoadItemTextFromFile(HWND hWnd,
  407.                           int item,
  408.                           char* filename)
  409. {
  410.     // ifstream textFile(filename, ios::in | ios::binary);
  411.     ifstream textFile(filename, ios::in);
  412.     string s;
  413.     if (!textFile.good())
  414.     {
  415.         SetDlgItemText(hWnd, item, "License file missing!rrnSee http://www.gnu.org/copyleft/gpl.html");
  416.         return true;
  417.     }
  418.     char c;
  419.     while (textFile.get(c))
  420.     {
  421.         if (c == 'n')
  422.             s += "rrn";
  423.         else
  424.             s += c;
  425.     }
  426.     SetDlgItemText(hWnd, item, UTF8ToCurrentCP(s).c_str());
  427.     return true;
  428. }
  429. BOOL APIENTRY AboutProc(HWND hDlg,
  430.                         UINT message,
  431.                         UINT wParam,
  432.                         LONG lParam)
  433. {
  434.     switch (message)
  435.     {
  436.     case WM_INITDIALOG:
  437.         MakeHyperlinkFromStaticCtrl(hDlg, IDC_CELESTIALINK);
  438.         return(TRUE);
  439.     case WM_COMMAND:
  440.         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  441.         {
  442.             EndDialog(hDlg, 0);
  443.             return TRUE;
  444.         }
  445.         else if (LOWORD(wParam) == IDC_CELESTIALINK)
  446.         {
  447.             char urlBuf[256];
  448.             HWND hCtrl = GetDlgItem(hDlg, IDC_CELESTIALINK);
  449.             if (hCtrl)
  450.             {
  451.                 if (GetWindowText(hCtrl, urlBuf, sizeof(urlBuf)) > 0)
  452.                 {
  453.                     ShellExecute(hDlg, "open", urlBuf, NULL, NULL, SW_SHOWNORMAL);
  454.                     return TRUE;
  455.                 }
  456.             }
  457.         }
  458.         break;
  459.     }
  460.     return FALSE;
  461. }
  462. BOOL APIENTRY ControlsHelpProc(HWND hDlg,
  463.                               UINT message,
  464.                               UINT wParam,
  465.                               LONG lParam)
  466. {
  467.     switch (message)
  468.     {
  469.     case WM_INITDIALOG:
  470.         LoadItemTextFromFile(hDlg, IDC_TEXT_CONTROLSHELP, const_cast<char*>(LocaleFilename("controls.txt").c_str()));
  471.         return(TRUE);
  472.     case WM_COMMAND:
  473.         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  474.         {
  475.             EndDialog(hDlg, 0);
  476.             return TRUE;
  477.         }
  478.         break;
  479.     }
  480.     return FALSE;
  481. }
  482. BOOL APIENTRY LicenseProc(HWND hDlg,
  483.                           UINT message,
  484.                           UINT wParam,
  485.                           LONG lParam)
  486. {
  487.     switch (message)
  488.     {
  489.     case WM_INITDIALOG:
  490.         LoadItemTextFromFile(hDlg, IDC_LICENSE_TEXT, const_cast<char*>(LocaleFilename("COPYING").c_str()));
  491.         return(TRUE);
  492.     case WM_COMMAND:
  493.         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  494.         {
  495.             EndDialog(hDlg, 0);
  496.             return TRUE;
  497.         }
  498.         break;
  499.     }
  500.     return FALSE;
  501. }
  502. BOOL APIENTRY GLInfoProc(HWND hDlg,
  503.                          UINT message,
  504.                          UINT wParam,
  505.                          LONG lParam)
  506. {
  507.     switch (message)
  508.     {
  509.     case WM_INITDIALOG:
  510.         {
  511.             const char* vendor = (char*) glGetString(GL_VENDOR);
  512.             const char* render = (char*) glGetString(GL_RENDERER);
  513.             const char* version = (char*) glGetString(GL_VERSION);
  514.             const char* ext = (char*) glGetString(GL_EXTENSIONS);
  515.             string s;
  516.             s += UTF8ToCurrentCP(_("Vendor: "));
  517.             if (vendor != NULL)
  518.                 s += vendor;
  519.             s += "rrn";
  520.             s += UTF8ToCurrentCP(_("Renderer: "));
  521.             if (render != NULL)
  522.                 s += render;
  523.             s += "rrn";
  524.             s += UTF8ToCurrentCP(_("Version: "));
  525.             if (version != NULL)
  526.                 s += version;
  527.             s += "rrn";
  528.             if (ExtensionSupported("GL_ARB_shading_language_100"))
  529.             {
  530.                 const char* versionString = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION_ARB);
  531.                 if (versionString != NULL)
  532.                 {
  533.                     s += UTF8ToCurrentCP(_("GLSL version: "));
  534.                     s += versionString;
  535.                     s += "rrn";
  536.                 }
  537.             }
  538.             char buf[1024];
  539.             GLint simTextures = 1;
  540.             if (ExtensionSupported("GL_ARB_multitexture"))
  541.                 glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &simTextures);
  542.             sprintf(buf, "%s%drrn",
  543.                     UTF8ToCurrentCP(_("Max simultaneous textures: ")).c_str(),
  544.                     simTextures);
  545.             s += buf;
  546.             GLint maxTextureSize = 0;
  547.             glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
  548.             sprintf(buf, "%s%drrn",
  549.                     UTF8ToCurrentCP(_("Max texture size: ")).c_str(),
  550.                     maxTextureSize);
  551.             s += buf;
  552.             if (ExtensionSupported("GL_EXT_texture_cube_map"))
  553.             {
  554.                 GLint maxCubeMapSize = 0;
  555.                 glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &maxCubeMapSize);
  556.                 sprintf(buf, "%s%drrn",
  557.                         UTF8ToCurrentCP(_("Max cube map size: ")).c_str(),
  558.                         maxTextureSize);
  559.                 s += buf;
  560.             }
  561.             GLfloat pointSizeRange[2];
  562.             glGetFloatv(GL_POINT_SIZE_RANGE, pointSizeRange);
  563.             sprintf(buf, "%s%f - %frrn",
  564.                     UTF8ToCurrentCP(_("Point size range: ")).c_str(),
  565.                     pointSizeRange[0], pointSizeRange[1]);
  566.             s += buf;
  567.             s += "rrn";
  568.             s += UTF8ToCurrentCP(_("Supported Extensions:")).c_str();
  569.             s += "rrn";
  570.             if (ext != NULL)
  571.             {
  572.                 string extString(ext);
  573.                 int pos = extString.find(' ', 0);
  574.                 while (pos != string::npos)
  575.                 {
  576.                     extString.replace(pos, 1, "rrn");
  577.                     pos = extString.find(' ', pos);
  578.                 }
  579.                 s += extString;
  580.             }
  581.             SetDlgItemText(hDlg, IDC_GLINFO_TEXT, s.c_str());
  582.         }
  583.         return(TRUE);
  584.     case WM_COMMAND:
  585.         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  586.         {
  587.             EndDialog(hDlg, 0);
  588.             return TRUE;
  589.         }
  590.         break;
  591.     }
  592.     return FALSE;
  593. }
  594. UINT CALLBACK ChooseMovieParamsProc(HWND hDlg, UINT message,
  595.                                     WPARAM wParam, LPARAM lParam)
  596. {
  597.     switch (message)
  598.     {
  599.     case WM_INITDIALOG:
  600.         {
  601.             char buf[100];
  602.             HWND hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_SIZE);
  603.             int nSizes = sizeof MovieSizes / sizeof MovieSizes[0];
  604.             int i;
  605.             for (i = 0; i < nSizes; i++)
  606.             {
  607.                 sprintf(buf, "%d x %d", MovieSizes[i][0], MovieSizes[i][1]);
  608.                 SendMessage(hwnd, CB_INSERTSTRING, -1,
  609.                             reinterpret_cast<LPARAM>(buf));
  610.             }
  611.             SendMessage(hwnd, CB_SETCURSEL, movieSize, 0);
  612.             hwnd = GetDlgItem(hDlg, IDC_COMBO_MOVIE_FRAMERATE);
  613.             int nFramerates = sizeof MovieFramerates / sizeof MovieFramerates[0];
  614.             for (i = 0; i < nFramerates; i++)
  615.             {
  616.                 sprintf(buf, "%.2f", MovieFramerates[i]);
  617.                 SendMessage(hwnd, CB_INSERTSTRING, -1,
  618.                             reinterpret_cast<LPARAM>(buf));
  619.             }
  620.             SendMessage(hwnd, CB_SETCURSEL, movieFramerate, 0);
  621.         }
  622.         return TRUE;
  623.     case WM_COMMAND:
  624.         if (LOWORD(wParam) == IDC_COMBO_MOVIE_SIZE)
  625.         {
  626.             if (HIWORD(wParam) == CBN_SELCHANGE)
  627.             {
  628.                 HWND hwnd = reinterpret_cast<HWND>(lParam);
  629.                 int item = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
  630.                 if (item != CB_ERR)
  631.                     movieSize = item;
  632.             }
  633.             return TRUE;
  634.         }
  635.         else if (LOWORD(wParam) == IDC_COMBO_MOVIE_FRAMERATE)
  636.         {
  637.             if (HIWORD(wParam) == CBN_SELCHANGE)
  638.             {
  639.                 HWND hwnd = reinterpret_cast<HWND>(lParam);
  640.                 int item = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
  641.                 if (item != CB_ERR)
  642.                     movieFramerate = item;
  643.             }
  644.             return TRUE;
  645.         }
  646.     }
  647.     return FALSE;
  648. }
  649. BOOL APIENTRY FindObjectProc(HWND hDlg,
  650.                              UINT message,
  651.                              UINT wParam,
  652.                              LONG lParam)
  653. {
  654.     switch (message)
  655.     {
  656.     case WM_INITDIALOG:
  657.         return(TRUE);
  658.     case WM_COMMAND:
  659.         if (LOWORD(wParam) == IDOK)
  660.         {
  661.             char buf[1024], out[1024];
  662.             wchar_t wbuff[1024];
  663.             int len = GetDlgItemText(hDlg, IDC_FINDOBJECT_EDIT, buf, sizeof(buf));
  664.             if (len > 0)
  665.             {
  666.                 int wlen = MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuff, sizeof(wbuff));
  667.                 WideCharToMultiByte(CP_UTF8, 0, wbuff, wlen, out, sizeof(out), NULL, NULL);
  668.                 Selection sel = appCore->getSimulation()->findObject(string(out), true);
  669.                 if (!sel.empty())
  670.                     appCore->getSimulation()->setSelection(sel);
  671.             }
  672.             EndDialog(hDlg, 0);
  673.             return TRUE;
  674.         }
  675.         else if (LOWORD(wParam) == IDCANCEL)
  676.         {
  677.             EndDialog(hDlg, 0);
  678.             return FALSE;
  679.         }
  680.         break;
  681.     }
  682.     return FALSE;
  683. }
  684. BOOL APIENTRY AddBookmarkFolderProc(HWND hDlg,
  685.                                     UINT message,
  686.                                     UINT wParam,
  687.                                     LONG lParam)
  688. {
  689.     switch (message)
  690.     {
  691.     case WM_INITDIALOG:
  692.         {
  693.             // Center dialog directly over parent
  694.             HWND hParent = GetParent(hDlg);
  695.             CenterWindow(hParent, hDlg);
  696.             // Limit text of folder name to 32 chars
  697.             HWND hEdit = GetDlgItem(hDlg, IDC_BOOKMARKFOLDER);
  698.             SendMessage(hEdit, EM_LIMITTEXT, 32, 0);
  699.             // Set initial button states
  700.             HWND hOK = GetDlgItem(hDlg, IDOK);
  701.             HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
  702.             EnableWindow(hOK, FALSE);
  703.             RemoveButtonDefaultStyle(hOK);
  704.             AddButtonDefaultStyle(hCancel);
  705.         }
  706.         return TRUE;
  707.     case WM_COMMAND:
  708.         {
  709.             if (HIWORD(wParam) == EN_CHANGE)
  710.             {
  711.                 HWND hOK = GetDlgItem(hDlg, IDOK);
  712.                 HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
  713.                 if (hOK && hCancel)
  714.                 {
  715.                     // If edit control contains text, enable OK button
  716.                     char name[33];
  717.                     GetWindowText((HWND)lParam, name, sizeof(name));
  718.                     if (name[0])
  719.                     {
  720.                         // Remove Cancel button default style
  721.                         RemoveButtonDefaultStyle(hCancel);
  722.                         // Enable OK button
  723.                         EnableWindow(hOK, TRUE);
  724.                         // Make OK button default
  725.                         AddButtonDefaultStyle(hOK);
  726.                     }
  727.                     else
  728.                     {
  729.                         // Disable OK button
  730.                         EnableWindow(hOK, FALSE);
  731.                         // Remove OK button default style
  732.                         RemoveButtonDefaultStyle(hOK);
  733.                         // Make Cancel button default
  734.                         AddButtonDefaultStyle(hCancel);
  735.                     }
  736.                 }
  737.             }
  738.         }
  739.         if (LOWORD(wParam) == IDOK)
  740.         {
  741.             // Get text entered in Folder Name Edit box
  742.             char name[33];
  743.             HWND hEdit = GetDlgItem(hDlg, IDC_BOOKMARKFOLDER);
  744.             if (hEdit)
  745.             {
  746.                 if (GetWindowText(hEdit, name, sizeof(name)))
  747.                 {
  748.                     // Create new folder in parent dialog tree control.
  749.                     AddNewBookmarkFolderInTree(hBookmarkTree, appCore, name);
  750.                 }
  751.             }
  752.             EndDialog(hDlg, 0);
  753.             return TRUE;
  754.         }
  755.         else if (LOWORD(wParam) == IDCANCEL)
  756.         {
  757.             EndDialog(hDlg, 0);
  758.             return FALSE;
  759.         }
  760.     }
  761.     return FALSE;
  762. }
  763. BOOL APIENTRY AddBookmarkProc(HWND hDlg,
  764.                               UINT message,
  765.                               UINT wParam,
  766.                               LONG lParam)
  767. {
  768.     switch (message)
  769.     {
  770.     case WM_INITDIALOG:
  771.         {
  772.         RECT dlgRect, treeRect;
  773.         HWND hCtrl;
  774.         if (GetWindowRect(hDlg, &dlgRect))
  775.         {
  776.             if (hCtrl = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
  777.             {
  778.                 if (GetWindowRect(hCtrl, &treeRect))
  779.                 {
  780.                     int width = dlgRect.right - dlgRect.left;
  781.                     int height = treeRect.top - dlgRect.top;
  782.                     SetWindowPos(hDlg, HWND_TOP, 0, 0, width, height,
  783.                                  SWP_NOMOVE | SWP_NOZORDER);
  784.                 }
  785.                 HTREEITEM hParent;
  786.                 if (hParent = PopulateBookmarkFolders(hCtrl, appCore, appInstance))
  787.                 {
  788.                     //Expand bookmarks item
  789.                     TreeView_Expand(hCtrl, hParent, TVE_EXPAND);
  790.                 }
  791.             }
  792.         }
  793.         //Set initial button states
  794.         HWND hOK = GetDlgItem(hDlg, IDOK);
  795.         HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
  796.         EnableWindow(hOK, FALSE);
  797.         RemoveButtonDefaultStyle(hOK);
  798.         AddButtonDefaultStyle(hCancel);
  799.         // Set bookmark text to selection text
  800.         if (hCtrl = GetDlgItem(hDlg, IDC_BOOKMARK_EDIT))
  801.         {
  802.             //If this is a body, set the text.
  803.             Selection sel = appCore->getSimulation()->getSelection();
  804.             switch (sel.getType())
  805.             {
  806.             case Selection::Type_Body:
  807.                 {
  808.                     string name = UTF8ToCurrentCP(sel.body()->getName(true));
  809.                     SetWindowText(hCtrl, (char*)name.c_str());
  810.                 }
  811.                 break;
  812.             default:
  813.                 break;
  814.             }
  815.         }
  816.         return(TRUE);
  817.         }
  818.     case WM_COMMAND:
  819.         {
  820.         if (HIWORD(wParam) == EN_CHANGE)
  821.         {
  822.             HWND hOK = GetDlgItem(hDlg, IDOK);
  823.             HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
  824.             if (hOK && hCancel)
  825.             {
  826.                 //If edit control contains text, enable OK button
  827.                 char name[33];
  828.                 GetWindowText((HWND)lParam, name, sizeof(name));
  829.                 if (name[0])
  830.                 {
  831.                     //Remove Cancel button default style
  832.                     RemoveButtonDefaultStyle(hCancel);
  833.                     //Enable OK button
  834.                     EnableWindow(hOK, TRUE);
  835.                     //Make OK button default
  836.                     AddButtonDefaultStyle(hOK);
  837.                 }
  838.                 else
  839.                 {
  840.                     //Disable OK button
  841.                     EnableWindow(hOK, FALSE);
  842.                     //Remove OK button default style
  843.                     RemoveButtonDefaultStyle(hOK);
  844.                     //Make Cancel button default
  845.                     AddButtonDefaultStyle(hCancel);
  846.                 }
  847.             }
  848.         }
  849.         if (LOWORD(wParam) == IDOK)
  850.         {
  851.             char name[33];
  852.             int len = GetDlgItemText(hDlg, IDC_BOOKMARK_EDIT, name, sizeof(name));
  853.             if (len > 0)
  854.             {
  855.                 HWND hTree;
  856.                 if(hTree = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
  857.                 {
  858.                     InsertBookmarkInFavorites(hTree, name, appCore);
  859.                     appCore->writeFavoritesFile();
  860.                     // Rebuild bookmarks menu.
  861.                     BuildFavoritesMenu(menuBar, appCore, appInstance, &odAppMenu);
  862.                 }
  863.             }
  864.             EndDialog(hDlg, 0);
  865.             return TRUE;
  866.         }
  867.         else if (LOWORD(wParam) == IDCANCEL)
  868.         {
  869.             EndDialog(hDlg, 0);
  870.             return FALSE;
  871.         }
  872.         else if (LOWORD(wParam) == IDC_BOOKMARK_CREATEIN)
  873.         {
  874.             HWND button;
  875.             RECT dlgRect, treeRect;
  876.             HWND hTree;
  877.             char text[16];
  878.             if (GetWindowRect(hDlg, &dlgRect))
  879.             {
  880.                 if (hTree = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
  881.                 {
  882.                     if (GetWindowRect(hTree, &treeRect))
  883.                     {
  884.                         if (button = GetDlgItem(hDlg, IDC_BOOKMARK_CREATEIN))
  885.                         {
  886.                             if (GetWindowText(button, text, sizeof(text)))
  887.                             {
  888.                                 int width = dlgRect.right - dlgRect.left;
  889.                                 if (strstr(text, ">>"))
  890.                                 {
  891.                                     //Increase size of dialog
  892.                                     int height = treeRect.bottom - dlgRect.top + 12;
  893.                                     SetWindowPos(hDlg, HWND_TOP, 0, 0, width, height,
  894.                                                  SWP_NOMOVE | SWP_NOZORDER);
  895.                                     //Change text in button
  896.                                     strcpy(text + strlen(text) - 2, "<<");
  897.                                     SetWindowText(button, text);
  898.                                 }
  899.                                 else
  900.                                 {
  901.                                     //Decrease size of dialog
  902.                                     int height = treeRect.top - dlgRect.top;
  903.                                     SetWindowPos(hDlg, HWND_TOP, 0, 0, width, height,
  904.                                                  SWP_NOMOVE | SWP_NOZORDER);
  905.                                     //Change text in button
  906.                                     strcpy(text + strlen(text) - 2, ">>");
  907.                                     SetWindowText(button, text);
  908.                                 }
  909.                             }
  910.                         }
  911.                     }
  912.                 }
  913.             }
  914.         }
  915.         else if (LOWORD(wParam) == IDC_BOOKMARK_NEWFOLDER)
  916.         {
  917.             if(hBookmarkTree = GetDlgItem(hDlg, IDC_BOOKMARK_FOLDERTREE))
  918.             {
  919.                 DialogBox(hRes, MAKEINTRESOURCE(IDD_ADDBOOKMARK_FOLDER),
  920.                           hDlg, AddBookmarkFolderProc);
  921.             }
  922.         }
  923.         break;
  924.         }
  925.     }
  926.     return FALSE;
  927. }
  928. BOOL APIENTRY RenameBookmarkProc(HWND hDlg,
  929.                                  UINT message,
  930.                                  UINT wParam,
  931.                                  LONG lParam)
  932. {
  933.     switch (message)
  934.     {
  935.     case WM_INITDIALOG:
  936.         {
  937.         //Center dialog directly over parent
  938.         HWND hParent = GetParent(hDlg);
  939.         CenterWindow(hParent, hDlg);
  940.         //Limit text of folder name to 32 chars
  941.         HWND hEdit = GetDlgItem(hDlg, IDC_NEWBOOKMARK);
  942.         SendMessage(hEdit, EM_LIMITTEXT, 32, 0);
  943.         //Set text in edit control to current bookmark name
  944.         SetWindowText(hEdit, bookmarkName);
  945.         return(TRUE);
  946.         }
  947.     case WM_COMMAND:
  948.         {
  949.         if (HIWORD(wParam) == EN_CHANGE)
  950.         {
  951.             HWND hOK = GetDlgItem(hDlg, IDOK);
  952.             HWND hCancel = GetDlgItem(hDlg, IDCANCEL);
  953.             if (hOK && hCancel)
  954.             {
  955.                 //If edit control contains text, enable OK button
  956.                 char name[33];
  957.                 GetWindowText((HWND)lParam, name, sizeof(name));
  958.                 if (name[0])
  959.                 {
  960.                     //Remove Cancel button default style
  961.                     RemoveButtonDefaultStyle(hCancel);
  962.                     //Enable OK button
  963.                     EnableWindow(hOK, TRUE);
  964.                     //Make OK button default
  965.                     AddButtonDefaultStyle(hOK);
  966.                 }
  967.                 else
  968.                 {
  969.                     //Disable OK button
  970.                     EnableWindow(hOK, FALSE);
  971.                     //Remove OK button default style
  972.                     RemoveButtonDefaultStyle(hOK);
  973.                     //Make Cancel button default
  974.                     AddButtonDefaultStyle(hCancel);
  975.                 }
  976.             }
  977.         }
  978.         if (LOWORD(wParam) == IDOK)
  979.         {
  980.             //Get text entered in Folder Name Edit box
  981.             char name[33];
  982.             HWND hEdit = GetDlgItem(hDlg, IDC_NEWBOOKMARK);
  983.             if (hEdit)
  984.             {
  985.                 if (GetWindowText(hEdit, name, sizeof(name)))
  986.                     RenameBookmarkInFavorites(hBookmarkTree, name, appCore);
  987.             }
  988.             EndDialog(hDlg, 0);
  989.             return TRUE;
  990.         }
  991.         else if (LOWORD(wParam) == IDCANCEL)
  992.         {
  993.             EndDialog(hDlg, 0);
  994.             return FALSE;
  995.         }
  996.         }
  997.     }
  998.     return FALSE;
  999. }
  1000. BOOL APIENTRY OrganizeBookmarksProc(HWND hDlg,
  1001.                                     UINT message,
  1002.                                     UINT wParam,
  1003.                                     LONG lParam)
  1004. {
  1005.     static UINT_PTR dragDropTimer;
  1006.     switch (message)
  1007.     {
  1008.     case WM_INITDIALOG:
  1009.         {
  1010.         HWND hCtrl;
  1011.         if (hCtrl = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1012.         {
  1013.             HTREEITEM hParent;
  1014.             if (hParent = PopulateBookmarksTree(hCtrl, appCore, hRes))
  1015.             {
  1016.                 // Expand bookmarks item
  1017.                 TreeView_Expand(hCtrl, hParent, TVE_EXPAND);
  1018.             }
  1019.         }
  1020.         if (hCtrl = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_DELETE))
  1021.             EnableWindow(hCtrl, FALSE);
  1022.         if (hCtrl = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_RENAME))
  1023.             EnableWindow(hCtrl, FALSE);
  1024.         return(TRUE);
  1025.         }
  1026.     case WM_COMMAND:
  1027.         {
  1028.         if (LOWORD(wParam) == IDOK)
  1029.         {
  1030. #if 0
  1031.             HWND hTree;
  1032.             if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1033.                 SyncTreeFoldersWithFavoriteFolders(hTree, appCore);
  1034. #endif
  1035.             // Write any change to bookmarks
  1036.             appCore->writeFavoritesFile();
  1037.             // Rebuild bookmarks menu
  1038.             BuildFavoritesMenu(menuBar, appCore, hRes, &odAppMenu);
  1039.             EndDialog(hDlg, 0);
  1040.             return TRUE;
  1041.         }
  1042.         else if (LOWORD(wParam) == IDCANCEL)
  1043.         {
  1044.             //Refresh from file
  1045.             appCore->readFavoritesFile();
  1046.             EndDialog(hDlg, 0);
  1047.             return FALSE;
  1048.         }
  1049.         else if (LOWORD(wParam) == IDC_ORGANIZE_BOOKMARKS_NEWFOLDER)
  1050.         {
  1051.             if (hBookmarkTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1052.             {
  1053.                 DialogBox(hRes, MAKEINTRESOURCE(IDD_ADDBOOKMARK_FOLDER), hDlg, AddBookmarkFolderProc);
  1054.             }
  1055.         }
  1056.         else if (LOWORD(wParam) == IDC_ORGANIZE_BOOKMARKS_RENAME)
  1057.         {
  1058.             if (hBookmarkTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1059.             {
  1060.                 HTREEITEM hItem;
  1061.                 TVITEM tvItem;
  1062.                 if (hItem = TreeView_GetSelection(hBookmarkTree))
  1063.                 {
  1064.                     tvItem.hItem = hItem;
  1065.                     tvItem.mask = TVIF_TEXT | TVIF_HANDLE;
  1066.                     tvItem.pszText = bookmarkName;
  1067.                     tvItem.cchTextMax = sizeof(bookmarkName);
  1068.                     if (TreeView_GetItem(hBookmarkTree, &tvItem))
  1069.                     {
  1070.                         DialogBox(hRes,
  1071.                                   MAKEINTRESOURCE(IDD_RENAME_BOOKMARK),
  1072.                                   hDlg, RenameBookmarkProc);
  1073.                     }
  1074.                 }
  1075.             }
  1076.         }
  1077.         else if (LOWORD(wParam) == IDC_ORGANIZE_BOOKMARKS_DELETE)
  1078.         {
  1079.             HWND hTree;
  1080.             if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1081.                 DeleteBookmarkFromFavorites(hTree, appCore);
  1082.         }
  1083.         break;
  1084.         }
  1085.     case WM_NOTIFY:
  1086.         {
  1087.             if (((LPNMHDR)lParam)->code == TVN_SELCHANGED)
  1088.             {
  1089.                 HWND hTree;
  1090.                 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1091.                 {
  1092.                     //Enable buttons as necessary
  1093.                     HTREEITEM hItem;
  1094.                     if (hItem = TreeView_GetSelection(hTree))
  1095.                     {
  1096.                         HWND hDelete, hRename;
  1097.                         hDelete = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_DELETE);
  1098.                         hRename = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARKS_RENAME);
  1099.                         if (hDelete && hRename)
  1100.                         {
  1101.                             if (TreeView_GetParent(hTree, hItem))
  1102.                             {
  1103.                                 EnableWindow(hDelete, TRUE);
  1104.                                 EnableWindow(hRename, TRUE);
  1105.                             }
  1106.                             else
  1107.                             {
  1108.                                 EnableWindow(hDelete, FALSE);
  1109.                                 EnableWindow(hRename, FALSE);
  1110.                             }
  1111.                         }
  1112.                     }
  1113.                 }
  1114.             }
  1115.             else if(((LPNMHDR)lParam)->code == TVN_BEGINDRAG)
  1116.             {
  1117.                 //Do not allow folders to be dragged
  1118.                 HWND hTree;
  1119.                 TVITEM tvItem;
  1120.                 LPNMTREEVIEW nm = (LPNMTREEVIEW)lParam;
  1121.                 HTREEITEM hItem = nm->itemNew.hItem;
  1122.                 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1123.                 {
  1124.                     tvItem.hItem = hItem;
  1125.                     tvItem.mask = TVIF_PARAM | TVIF_HANDLE;
  1126.                     if (TreeView_GetItem(hTree, &tvItem))
  1127.                     {
  1128.                         if(tvItem.lParam != 1)
  1129.                         {
  1130.                             //Start a timer to handle auto-scrolling
  1131.                             dragDropTimer = SetTimer(hDlg, 1, 100, NULL);
  1132.                             OrganizeBookmarksOnBeginDrag(hTree, (LPNMTREEVIEW)lParam);
  1133.                         }
  1134.                     }
  1135.                 }
  1136.             }
  1137.         }
  1138.         break;
  1139.     case WM_MOUSEMOVE:
  1140.         {
  1141.             if(isOrganizeBookmarksDragDropActive())
  1142.             {
  1143.                 HWND hTree;
  1144.                 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1145.                 {
  1146.                     OrganizeBookmarksOnMouseMove(hTree, GET_X_LPARAM(lParam),
  1147.                         GET_Y_LPARAM(lParam));
  1148.                 }
  1149.             }
  1150.         }
  1151.         break;
  1152.     case WM_LBUTTONUP:
  1153.         {
  1154.             if(isOrganizeBookmarksDragDropActive())
  1155.             {
  1156.                 HWND hTree;
  1157.                 if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1158.                 {
  1159.                     //Kill the auto-scroll timer
  1160.                     KillTimer(hDlg, dragDropTimer);
  1161.                     OrganizeBookmarksOnLButtonUp(hTree);
  1162.                     MoveBookmarkInFavorites(hTree, appCore);
  1163.                 }
  1164.             }
  1165.         }
  1166.         break;
  1167.     case WM_TIMER:
  1168.         {
  1169.             if(isOrganizeBookmarksDragDropActive())
  1170.             {
  1171.                 if(wParam == 1)
  1172.                 {
  1173.                     //Handle
  1174.                     HWND hTree;
  1175.                     if (hTree = GetDlgItem(hDlg, IDC_ORGANIZE_BOOKMARK_TREE))
  1176.                     {
  1177.                         DragDropAutoScroll(hTree);
  1178.                     }
  1179.                 }
  1180.             }
  1181.         }
  1182.         break;
  1183.     }
  1184.     return FALSE;
  1185. }
  1186. int selectedScreenMode = 0;
  1187. BOOL APIENTRY SelectDisplayModeProc(HWND hDlg,
  1188.                                     UINT message,
  1189.                                     UINT wParam,
  1190.                                     LONG lParam)
  1191. {
  1192.     switch (message)
  1193.     {
  1194.     case WM_INITDIALOG:
  1195.         {
  1196.             char buf[100];
  1197.             HWND hwnd = GetDlgItem(hDlg, IDC_COMBO_RESOLUTION);
  1198.             // Add windowed mode as the first item on the menu
  1199.             bind_textdomain_codeset("celestia", CurrentCP());
  1200.             SendMessage(hwnd, CB_INSERTSTRING, -1,
  1201.                         reinterpret_cast<LPARAM>(_("Windowed Mode")));
  1202.             bind_textdomain_codeset("celestia", "UTF8");
  1203.             for (vector<DEVMODE>::const_iterator iter= displayModes->begin();
  1204.                  iter != displayModes->end(); iter++)
  1205.             {
  1206.                 sprintf(buf, "%d x %d x %d",
  1207.                         iter->dmPelsWidth, iter->dmPelsHeight,
  1208.                         iter->dmBitsPerPel);
  1209.                 SendMessage(hwnd, CB_INSERTSTRING, -1,
  1210.                             reinterpret_cast<LPARAM>(buf));
  1211.             }
  1212.             SendMessage(hwnd, CB_SETCURSEL, currentScreenMode, 0);
  1213.         }
  1214.         return TRUE;
  1215.     case WM_COMMAND:
  1216.         if (LOWORD(wParam) == IDOK)
  1217.         {
  1218.             newScreenMode = selectedScreenMode;
  1219.             EndDialog(hDlg, 0);
  1220.             return TRUE;
  1221.         }
  1222.         else if (LOWORD(wParam) == IDCANCEL)
  1223.         {
  1224.             EndDialog(hDlg, 0);
  1225.             return TRUE;
  1226.         }
  1227.         else if (LOWORD(wParam) == IDC_COMBO_RESOLUTION)
  1228.         {
  1229.             if (HIWORD(wParam) == CBN_SELCHANGE)
  1230.             {
  1231.                 HWND hwnd = reinterpret_cast<HWND>(lParam);
  1232.                 int item = SendMessage(hwnd, CB_GETCURSEL, 0, 0);
  1233.                 if (item != CB_ERR)
  1234.                     selectedScreenMode = item;
  1235.             }
  1236.             return TRUE;
  1237.         }
  1238.     }
  1239.     return FALSE;
  1240. }
  1241. HMENU CreateMenuBar()
  1242. {
  1243.     return LoadMenu(hRes, MAKEINTRESOURCE(IDR_MAIN_MENU));
  1244. }
  1245. static void setMenuItemCheck(int menuItem, bool checked)
  1246. {
  1247.     CheckMenuItem(menuBar, menuItem, checked ? MF_CHECKED : MF_UNCHECKED);
  1248. }
  1249. struct IntStrPairComparePredicate
  1250. {
  1251.     IntStrPairComparePredicate() : dummy(0)
  1252.     {
  1253.     }
  1254.     bool operator()(const IntStrPair pair1, const IntStrPair pair2) const
  1255.     {
  1256.         return (pair1.second.compare(pair2.second) < 0);
  1257.     }
  1258.     int dummy;
  1259. };
  1260. static HMENU CreatePlanetarySystemMenu(string parentName, const PlanetarySystem* psys)
  1261. {
  1262.     // Use some vectors to categorize and sort the bodies within this PlanetarySystem.
  1263.     // In order to generate sorted menus, we must carry the name and menu index as a
  1264.     // single unit in the sort. One obvous way is to create a vector<pair<int,string>>
  1265.     // and then use a comparison predicate to sort.the vector based on the string in
  1266.     // each pair.
  1267.     // Declare vector<pair<int,string>> objects for each classification of body
  1268.     vector<IntStrPair> asteroids;
  1269.     vector<IntStrPair> comets;
  1270.     vector<IntStrPair> invisibles;
  1271.     vector<IntStrPair> moons;
  1272.     vector<IntStrPair> planets;
  1273.     vector<IntStrPair> spacecraft;
  1274.     // We will use these objects to iterate over all the above vectors
  1275.     vector<IntStrPairVec> objects;
  1276.     vector<string> menuNames;
  1277.     // Place each body in the correct vector based on classification
  1278.     HMENU menu = CreatePopupMenu();
  1279.     for (int i = 0; i < psys->getSystemSize(); i++)
  1280.     {
  1281.         Body* body = psys->getBody(i);
  1282.         if (!body->getName().empty())
  1283.         {
  1284.             switch(body->getClassification())
  1285.             {
  1286.             case Body::Asteroid:
  1287.                 asteroids.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
  1288.                 break;
  1289.             case Body::Comet:
  1290.                 comets.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
  1291.                 break;
  1292.             case Body::Invisible:
  1293.                 invisibles.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
  1294.                 break;
  1295.             case Body::Moon:
  1296.                 moons.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
  1297.                 break;
  1298.             case Body::Planet:
  1299.                 planets.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
  1300.                 break;
  1301.             case Body::Spacecraft:
  1302.                 spacecraft.push_back(make_pair(i, UTF8ToCurrentCP(body->getName(true))));
  1303.                 break;
  1304.             }
  1305.         }
  1306.     }
  1307.     // Add each vector of PlanetarySystem bodies to a vector to iterate over
  1308.     objects.push_back(asteroids);
  1309.     menuNames.push_back(UTF8ToCurrentCP(_("Asteroids")));
  1310.     objects.push_back(comets);
  1311.     menuNames.push_back(UTF8ToCurrentCP(_("Comets")));
  1312.     objects.push_back(invisibles);
  1313.     menuNames.push_back(UTF8ToCurrentCP(_("Invisibles")));
  1314.     objects.push_back(moons);
  1315.     menuNames.push_back(UTF8ToCurrentCP(_("Moons")));
  1316.     objects.push_back(planets);
  1317.     menuNames.push_back(UTF8ToCurrentCP(_("Planets")));
  1318.     objects.push_back(spacecraft);
  1319.     menuNames.push_back(UTF8ToCurrentCP(_("Spacecraft")));
  1320.     // Now sort each vector and generate submenus
  1321.     IntStrPairComparePredicate pred;
  1322.     vector<IntStrPairVec>::iterator obj;
  1323.     vector<IntStrPair>::iterator it;
  1324.     vector<string>::iterator menuName;
  1325.     HMENU hSubMenu;
  1326.     int numSubMenus;
  1327.     // Count how many submenus we need to create
  1328.     numSubMenus = 0;
  1329.     for (obj=objects.begin(); obj != objects.end(); obj++)
  1330.     {
  1331.         if (obj->size() > 0)
  1332.             numSubMenus++;
  1333.     }
  1334.     menuName = menuNames.begin();
  1335.     for (obj=objects.begin(); obj != objects.end(); obj++)
  1336.     {
  1337.         // Only generate a submenu if the vector is not empty
  1338.         if (obj->size() > 0)
  1339.         {
  1340.             // Don't create a submenu for a single item
  1341.             if (obj->size() == 1)
  1342.             {
  1343.                 it=obj->begin();
  1344.                 AppendMenu(menu, MF_STRING, MENU_CHOOSE_PLANET + it->first, it->second.c_str());
  1345.             }
  1346.             else
  1347.             {
  1348.                 // Skip sorting if we are dealing with the planets in our own Solar System.
  1349.                 if (parentName != "Sol" || *menuName != UTF8ToCurrentCP(_("Planets")))
  1350.                     sort(obj->begin(), obj->end(), pred);
  1351.                 if (numSubMenus > 1)
  1352.                 {
  1353.                     // Add items to submenu
  1354.                     hSubMenu = CreatePopupMenu();
  1355.                     for(it=obj->begin(); it != obj->end(); it++)
  1356.                         AppendMenu(hSubMenu, MF_STRING, MENU_CHOOSE_PLANET + it->first, it->second.c_str());
  1357.                     AppendMenu(menu, MF_POPUP | MF_STRING, (DWORD)hSubMenu, menuName->c_str());
  1358.                 }
  1359.                 else
  1360.                 {
  1361.                     // Just add items to the popup
  1362.                     for(it=obj->begin(); it != obj->end(); it++)
  1363.                         AppendMenu(menu, MF_STRING, MENU_CHOOSE_PLANET + it->first, it->second.c_str());
  1364.                 }
  1365.             }
  1366.         }
  1367.         menuName++;
  1368.     }
  1369.     return menu;
  1370. }
  1371. static HMENU CreateAlternateSurfaceMenu(const vector<string>& surfaces)
  1372. {
  1373.     HMENU menu = CreatePopupMenu();
  1374.     AppendMenu(menu, MF_STRING, MENU_CHOOSE_SURFACE, "Normal");
  1375.     for (unsigned int i = 0; i < surfaces.size(); i++)
  1376.     {
  1377.         AppendMenu(menu, MF_STRING, MENU_CHOOSE_SURFACE + i + 1,
  1378.                    surfaces[i].c_str());
  1379.     }
  1380.     return menu;
  1381. }
  1382. VOID APIENTRY handlePopupMenu(HWND hwnd,
  1383.                               float x, float y,
  1384.                               const Selection& sel)
  1385. {
  1386.     HMENU hMenu;
  1387.     string name;
  1388.     hMenu = CreatePopupMenu();
  1389.     switch (sel.getType())
  1390.     {
  1391.     case Selection::Type_Body:
  1392.         {
  1393.             name = sel.body()->getName(true);
  1394.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_CENTER, UTF8ToCurrentCP(name).c_str());
  1395.             AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
  1396.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_GOTO, UTF8ToCurrentCP(_("&Goto")).c_str());
  1397.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_FOLLOW, UTF8ToCurrentCP(_("&Follow")).c_str());
  1398.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_SYNCORBIT, UTF8ToCurrentCP(_("S&ync Orbit")).c_str());
  1399.             AppendMenu(hMenu, MF_STRING, ID_INFO, UTF8ToCurrentCP(_("&Info")).c_str());
  1400.             HMENU refVectorMenu = CreatePopupMenu();
  1401.             AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) refVectorMenu, UTF8ToCurrentCP(_("&Reference Vectors")).c_str());
  1402.             AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_BODY_AXES, UTF8ToCurrentCP(_("Show Body Axes")).c_str());
  1403.             AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_FRAME_AXES, UTF8ToCurrentCP(_("Show Frame Axes")).c_str());
  1404.             AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_SUN_DIRECTION, UTF8ToCurrentCP(_("Show Sun Direction")).c_str());
  1405.             AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_VELOCITY_VECTOR, UTF8ToCurrentCP(_("Show Velocity Vector")).c_str());
  1406.             AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_PLANETOGRAPHIC_GRID, UTF8ToCurrentCP(_("Show Planetographic Grid")).c_str());
  1407.             AppendMenu(refVectorMenu, MF_STRING, ID_RENDER_TERMINATOR, UTF8ToCurrentCP(_("Show Terminator")).c_str());
  1408.             CheckMenuItem(refVectorMenu, ID_RENDER_BODY_AXES,   sel.body()->findReferenceMark("body axes") ? MF_CHECKED : MF_UNCHECKED);
  1409.             CheckMenuItem(refVectorMenu, ID_RENDER_FRAME_AXES,  sel.body()->findReferenceMark("frame axes") ? MF_CHECKED : MF_UNCHECKED);
  1410.             CheckMenuItem(refVectorMenu, ID_RENDER_SUN_DIRECTION,  sel.body()->findReferenceMark("sun direction") ? MF_CHECKED : MF_UNCHECKED);
  1411.             CheckMenuItem(refVectorMenu, ID_RENDER_VELOCITY_VECTOR,  sel.body()->findReferenceMark("velocity vector") ? MF_CHECKED : MF_UNCHECKED);
  1412.             CheckMenuItem(refVectorMenu, ID_RENDER_PLANETOGRAPHIC_GRID, sel.body()->findReferenceMark("planetographic grid") ? MF_CHECKED : MF_UNCHECKED);
  1413.             CheckMenuItem(refVectorMenu, ID_RENDER_TERMINATOR, sel.body()->findReferenceMark("terminator") ? MF_CHECKED : MF_UNCHECKED);
  1414.             const PlanetarySystem* satellites = sel.body()->getSatellites();
  1415.             if (satellites != NULL && satellites->getSystemSize() != 0)
  1416.             {
  1417.                 HMENU satMenu = CreatePlanetarySystemMenu(name, satellites);
  1418.                 AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) satMenu,
  1419.                            UTF8ToCurrentCP(_("&Satellites")).c_str());
  1420.             }
  1421.             vector<string>* altSurfaces = sel.body()->getAlternateSurfaceNames();
  1422.             if (altSurfaces != NULL)
  1423.             {
  1424.                 if (altSurfaces->size() != NULL)
  1425.                 {
  1426.                     HMENU surfMenu = CreateAlternateSurfaceMenu(*altSurfaces);
  1427.                     AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) surfMenu,
  1428.                                UTF8ToCurrentCP(_("&Alternate Surfaces")).c_str());
  1429.                 }
  1430.                 delete altSurfaces;
  1431.             }
  1432.         }
  1433.         break;
  1434.     case Selection::Type_Star:
  1435.         {
  1436.             Simulation* sim = appCore->getSimulation();
  1437.             name = sim->getUniverse()->getStarCatalog()->getStarName(*(sel.star()));
  1438.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_CENTER, UTF8ToCurrentCP(name).c_str());
  1439.             AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
  1440.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_GOTO, UTF8ToCurrentCP(_("&Goto")).c_str());
  1441.             AppendMenu(hMenu, MF_STRING, ID_INFO, UTF8ToCurrentCP(_("&Info")).c_str());
  1442.             SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog();
  1443.             SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel.star()->getCatalogNumber());
  1444.             if (iter != solarSystemCatalog->end())
  1445.             {
  1446.                 SolarSystem* solarSys = iter->second;
  1447.                 HMENU planetsMenu = CreatePlanetarySystemMenu(name, solarSys->getPlanets());
  1448.                 if (name == "Sol")
  1449.                     AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) planetsMenu, UTF8ToCurrentCP(_("Orbiting Bodies")).c_str());
  1450.                 else
  1451.                     AppendMenu(hMenu, MF_POPUP | MF_STRING, (DWORD) planetsMenu, UTF8ToCurrentCP(_("Planets")).c_str());
  1452.             }
  1453.         }
  1454.         break;
  1455.     case Selection::Type_DeepSky:
  1456.         {
  1457.             Simulation* sim = appCore->getSimulation();
  1458.             name = sim->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky());
  1459.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_CENTER, UTF8ToCurrentCP(name).c_str());
  1460.             AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
  1461.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_GOTO, UTF8ToCurrentCP(_("&Goto")).c_str());
  1462.             AppendMenu(hMenu, MF_STRING, ID_NAVIGATION_FOLLOW, UTF8ToCurrentCP(_("&Follow")).c_str());
  1463.             AppendMenu(hMenu, MF_STRING, ID_INFO, UTF8ToCurrentCP(_("&Info")).c_str());
  1464.         }
  1465.         break;
  1466.     case Selection::Type_Location:
  1467.         break;
  1468.     default:
  1469.         break;
  1470.     }
  1471.     if (appCore->getSimulation()->getUniverse()->isMarked(sel, 1))
  1472.         AppendMenu(hMenu, MF_STRING, ID_TOOLS_UNMARK, UTF8ToCurrentCP(_("&Unmark")).c_str());
  1473.     else
  1474.         AppendMenu(hMenu, MF_STRING, ID_TOOLS_MARK, UTF8ToCurrentCP(_("&Mark")).c_str());
  1475.     POINT point;
  1476.     point.x = (int) x;
  1477.     point.y = (int) y;
  1478.     if (currentScreenMode == 0)
  1479.         ClientToScreen(hwnd, (LPPOINT) &point);
  1480.     appCore->getSimulation()->setSelection(sel);
  1481.     TrackPopupMenu(hMenu, 0, point.x, point.y, 0, hwnd, NULL);
  1482.     // TODO: Do we need to explicitly destroy submenus or does DestroyMenu
  1483.     // work recursively?
  1484.     // According to the MSDN documentation, DestroyMenu() IS recursive. Clint 11/01.
  1485.     DestroyMenu(hMenu);
  1486. #ifdef INFINITE_MOUSE
  1487.     ignoreNextMoveEvent = true;
  1488. #endif // INFINITE_MOUSE
  1489. }
  1490. // TODO: get rid of fixed urls
  1491. void ShowWWWInfo(const Selection& sel)
  1492. {
  1493.     string url;
  1494.     switch (sel.getType())
  1495.     {
  1496.     case Selection::Type_Body:
  1497.         {
  1498.             url = sel.body()->getInfoURL();
  1499.             if (url.empty())
  1500.             {
  1501.                 string name = sel.body()->getName();
  1502.                 for (unsigned int i = 0; i < name.size(); i++)
  1503.                     name[i] = tolower(name[i]);
  1504.                 url = string("http://www.nineplanets.org/") + name + ".html";
  1505.             }
  1506.         }
  1507.         break;
  1508.     case Selection::Type_Star:
  1509.         {
  1510.             url = sel.star()->getInfoURL();
  1511.             if (url.empty())
  1512.             {
  1513.                 char name[32];
  1514.                 sprintf(name, "HIP%d", sel.star()->getCatalogNumber() & ~0xf0000000);
  1515.                 url = string("http://simbad.u-strasbg.fr/sim-id.pl?protocol=html&Ident=") + name;
  1516.             }
  1517.         }
  1518.         break;
  1519.     case Selection::Type_DeepSky:
  1520.         url = sel.deepsky()->getInfoURL();
  1521.         break;
  1522.     case Selection::Type_Location:
  1523.         break;
  1524.     default:
  1525.         break;
  1526.     }
  1527.     ShellExecute(mainWindow,
  1528.                  "open",
  1529.                  url.c_str(),
  1530.                  NULL,
  1531.                  NULL,
  1532.                  0);
  1533. }
  1534. void ContextMenu(float x, float y, Selection sel)
  1535. {
  1536.     handlePopupMenu(mainWindow, x, y, sel);
  1537. }
  1538. bool EnableFullScreen(const DEVMODE& dm)
  1539. {
  1540.     DEVMODE devMode;
  1541.     ZeroMemory(&devMode, sizeof devMode);
  1542.     devMode.dmSize = sizeof devMode;
  1543.     devMode.dmPelsWidth  = dm.dmPelsWidth;
  1544.     devMode.dmPelsHeight = dm.dmPelsHeight;
  1545.     devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
  1546.     if (ChangeDisplaySettings(&devMode, CDS_FULLSCREEN) !=
  1547.         DISP_CHANGE_SUCCESSFUL)
  1548.     {
  1549. MessageBox(NULL,
  1550.                    "Unable to switch to full screen mode; running in window mode",
  1551.                    "Error",
  1552.                    MB_OK | MB_ICONERROR);
  1553.         return false;
  1554.     }
  1555.     return true;
  1556. }
  1557. void DisableFullScreen()
  1558. {
  1559.     ChangeDisplaySettings(0, 0);
  1560. }
  1561. unsigned int
  1562. ChooseBestMSAAPixelFormat(HDC hdc, int *formats, unsigned int numFormats,
  1563.                           int samplesRequested)
  1564. {
  1565.     int idealFormat = 0;
  1566.     int bestFormat  = 0;
  1567.     int bestSamples = 0;
  1568.     for (unsigned int i = 0; i < numFormats; i++)
  1569.     {
  1570.         int query = WGL_SAMPLES_ARB;
  1571.      int result = 0;
  1572.         bool isFloatFormat = false;
  1573.         query = WGL_SAMPLES_ARB;
  1574.         wglGetPixelFormatAttribivARB(hdc, formats[i], 0, 1, &query, &result);
  1575.         if (result <= samplesRequested && result >= bestSamples)
  1576.         {
  1577.             bestSamples = result;
  1578.             bestFormat = formats[i];
  1579.         }
  1580.         if (result == samplesRequested)
  1581.             idealFormat = formats[i];
  1582.     }
  1583.     if (idealFormat != 0)
  1584.         return idealFormat;
  1585.     return bestFormat;
  1586. }
  1587. // Select the pixel format for a given device context
  1588. bool SetDCPixelFormat(HDC hDC)
  1589. {
  1590.     bool msaa = false;
  1591.     if (appCore->getConfig()->aaSamples > 1 &&
  1592.         WGLExtensionSupported("WGL_ARB_pixel_format") &&
  1593.         WGLExtensionSupported("WGL_ARB_multisample"))
  1594.     {
  1595.         msaa = true;
  1596.     }
  1597.         
  1598.     if (!msaa)
  1599.     {
  1600.         static PIXELFORMATDESCRIPTOR pfd = {
  1601.             sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure
  1602.             1, // Version of this structure
  1603.             PFD_DRAW_TO_WINDOW | // Draw to Window (not to bitmap)
  1604.             PFD_SUPPORT_OPENGL | // Support OpenGL calls in window
  1605.             PFD_DOUBLEBUFFER, // Double buffered mode
  1606.             PFD_TYPE_RGBA, // RGBA Color mode
  1607.             GetDeviceCaps(hDC, BITSPIXEL),// Want the display bit depth
  1608.             0,0,0,0,0,0,   // Not used to select mode
  1609.             0,0, // Not used to select mode
  1610.             0,0,0,0,0, // Not used to select mode
  1611.             24, // Size of depth buffer
  1612.             0, // Not used to select mode
  1613.             0, // Not used to select mode
  1614.             PFD_MAIN_PLANE,             // Draw in main plane
  1615.             0,                          // Not used to select mode
  1616.             0,0,0                       // Not used to select mode
  1617.         };
  1618.         // Choose a pixel format that best matches that described in pfd
  1619.         int nPixelFormat = ChoosePixelFormat(hDC, &pfd);
  1620.         if (nPixelFormat == 0)
  1621.         {
  1622.             // Uh oh . . . looks like we can't handle OpenGL on this device.
  1623.             return false;
  1624.         }
  1625.         else
  1626.         {
  1627.             // Set the pixel format for the device context
  1628.             SetPixelFormat(hDC, nPixelFormat, &pfd);
  1629.             return true;
  1630.         }
  1631.     }
  1632.     else
  1633.     {
  1634.         PIXELFORMATDESCRIPTOR pfd;
  1635.         int ifmtList[] = {
  1636.             WGL_DRAW_TO_WINDOW_ARB,        TRUE,
  1637.             WGL_SUPPORT_OPENGL_ARB,        TRUE,
  1638.             WGL_DOUBLE_BUFFER_ARB,         TRUE,
  1639.             WGL_PIXEL_TYPE_ARB,            WGL_TYPE_RGBA_ARB,
  1640.             WGL_DEPTH_BITS_ARB,            24,
  1641.             WGL_COLOR_BITS_ARB,            24,
  1642.             WGL_RED_BITS_ARB,               8,
  1643.             WGL_GREEN_BITS_ARB,             8,
  1644.             WGL_BLUE_BITS_ARB,              8,
  1645.             WGL_ALPHA_BITS_ARB,             0,
  1646.             WGL_ACCUM_BITS_ARB,             0,
  1647.             WGL_STENCIL_BITS_ARB,           0,
  1648.             WGL_SAMPLE_BUFFERS_ARB,         appCore->getConfig()->aaSamples > 1,
  1649.             0
  1650.         };
  1651.         int             pixelFormatIndex;
  1652.         int             pixFormats[256];
  1653.         unsigned int    numFormats;
  1654.         wglChoosePixelFormatARB(hDC, ifmtList, NULL, 256, pixFormats, &numFormats);
  1655.         pixelFormatIndex = ChooseBestMSAAPixelFormat(hDC, pixFormats,
  1656.                                                      numFormats,
  1657.                                                      appCore->getConfig()->aaSamples);
  1658.         DescribePixelFormat(hDC, pixelFormatIndex,
  1659.                             sizeof(PIXELFORMATDESCRIPTOR), &pfd);
  1660.         if (!SetPixelFormat(hDC, pixelFormatIndex, &pfd))
  1661.             return false;
  1662.         return true;
  1663.     }
  1664. }
  1665. HWND CreateOpenGLWindow(int x, int y, int width, int height,
  1666.                         int mode, int& newMode)
  1667. {
  1668.     assert(mode >= 0 && mode <= displayModes->size());
  1669.     if (mode != 0)
  1670.     {
  1671.         x = 0;
  1672.         y = 0;
  1673.         width = displayModes->at(mode - 1).dmPelsWidth;
  1674.         height = displayModes->at(mode - 1).dmPelsHeight;
  1675.     }
  1676.     // Set up and register the window class
  1677.     WNDCLASS wc;
  1678.     wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  1679.     wc.lpfnWndProc = (WNDPROC) MainWindowProc;
  1680.     wc.cbClsExtra = 0;
  1681.     wc.cbWndExtra = 0;
  1682.     wc.hInstance = appInstance;
  1683.     wc.hIcon = LoadIcon(hRes, MAKEINTRESOURCE(IDI_CELESTIA_ICON));
  1684.     wc.hCursor = hDefaultCursor;
  1685.     wc.hbrBackground = NULL;
  1686.     wc.lpszMenuName = NULL;
  1687.     wc.lpszClassName = AppName;
  1688.     if (RegisterClass(&wc) == 0)
  1689.     {
  1690.         MessageBox(NULL,
  1691.                    "Failed to register the window class.", "Fatal Error",
  1692.                    MB_OK | MB_ICONERROR);
  1693.     return NULL;
  1694.     }
  1695.     newMode = currentScreenMode;
  1696.     if (mode != 0)
  1697.     {
  1698.         if (EnableFullScreen(displayModes->at(mode - 1)))
  1699.             newMode = mode;
  1700.     }
  1701.     else
  1702.     {
  1703.         DisableFullScreen();
  1704.         newMode = 0;
  1705.     }
  1706.     // Determine the proper window style to use
  1707.     DWORD dwStyle;
  1708.     if (newMode != 0)
  1709.     {
  1710.         dwStyle = WS_POPUP;
  1711.     }
  1712.     else
  1713.     {
  1714.         dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  1715.     }
  1716.     // Create the window
  1717.     HWND hwnd = CreateWindow(AppName,
  1718.                              AppName,
  1719.                              dwStyle,
  1720.                              x, y,
  1721.                              width, height,
  1722.                              NULL,
  1723.                              NULL,
  1724.                              appInstance,
  1725.                              NULL);
  1726.     if (hwnd == NULL)
  1727.         return NULL;
  1728.     ShowWindow(hwnd, SW_SHOW);
  1729.     SetForegroundWindow(hwnd);
  1730.     SetFocus(hwnd);
  1731.     deviceContext = GetDC(hwnd);
  1732.     if (!SetDCPixelFormat(deviceContext))
  1733.     {
  1734.         MessageBox(NULL,
  1735.                    "Could not get appropriate pixel format for OpenGL rendering.", "Fatal Error",
  1736.                    MB_OK | MB_ICONERROR);
  1737.     return NULL;
  1738.     }
  1739.     if (glContext == NULL)
  1740.         glContext = wglCreateContext(deviceContext);
  1741.     wglMakeCurrent(deviceContext, glContext);
  1742.     if (newMode == 0)
  1743.         SetMenu(hwnd, menuBar);
  1744.     else
  1745.         hideMenuBar = true;
  1746.     return hwnd;
  1747. }
  1748. void DestroyOpenGLWindow()
  1749. {
  1750. #if 0
  1751.     if (glContext != NULL)
  1752.     {
  1753.         wglMakeCurrent(NULL, NULL);
  1754.         if (!wglDeleteContext(glContext))
  1755.         {
  1756.             MessageBox(NULL,
  1757.                        "Releasing GL context failed.", "Error",
  1758.                        MB_OK | MB_ICONERROR);
  1759.         }
  1760.         glContext = NULL;
  1761.     }
  1762. #endif
  1763.     if (deviceContext != NULL)
  1764.     {
  1765.         if (!ReleaseDC(mainWindow, deviceContext))
  1766.         {
  1767.             MessageBox(NULL,
  1768.                        "Releasing device context failed.", "Error",
  1769.                        MB_OK | MB_ICONERROR);
  1770.         }
  1771.         deviceContext = NULL;
  1772.     }
  1773.     if (mainWindow != NULL)
  1774.     {
  1775.         SetMenu(mainWindow, NULL);
  1776.         DestroyWindow(mainWindow);
  1777.         mainWindow = NULL;
  1778.     }
  1779.     UnregisterClass(AppName, appInstance);
  1780. }
  1781. void handleKey(WPARAM key, bool down)
  1782. {
  1783.     int k = -1;
  1784.     int modifiers = 0;
  1785.     if (GetKeyState(VK_SHIFT) & 0x8000)
  1786.         modifiers |= CelestiaCore::ShiftKey;
  1787.     if (GetKeyState(VK_CONTROL) & 0x8000)
  1788.         modifiers |= CelestiaCore::ControlKey;
  1789.     switch (key)
  1790.     {
  1791.     case VK_UP:
  1792.         k = CelestiaCore::Key_Up;
  1793.         break;
  1794.     case VK_DOWN:
  1795.         k = CelestiaCore::Key_Down;
  1796.         break;
  1797.     case VK_LEFT:
  1798.         k = CelestiaCore::Key_Left;
  1799.         break;
  1800.     case VK_RIGHT:
  1801.         k = CelestiaCore::Key_Right;
  1802.         break;
  1803.     case VK_HOME:
  1804.         k = CelestiaCore::Key_Home;
  1805.         break;
  1806.     case VK_END:
  1807.         k = CelestiaCore::Key_End;
  1808.         break;
  1809.     case VK_PRIOR:
  1810.         k = CelestiaCore::Key_PageUp;
  1811.         break;
  1812.     case VK_NEXT:
  1813.         k = CelestiaCore::Key_PageDown;
  1814.         break;
  1815.     case VK_F1:
  1816.         k = CelestiaCore::Key_F1;
  1817.         break;
  1818.     case VK_F2:
  1819.         k = CelestiaCore::Key_F2;
  1820.         break;
  1821.     case VK_F3:
  1822.         k = CelestiaCore::Key_F3;
  1823.         break;
  1824.     case VK_F4:
  1825.         k = CelestiaCore::Key_F4;
  1826.         break;
  1827.     case VK_F5:
  1828.         k = CelestiaCore::Key_F5;
  1829.         break;
  1830.     case VK_F6:
  1831.         k = CelestiaCore::Key_F6;
  1832.         break;
  1833.     case VK_F7:
  1834.         k = CelestiaCore::Key_F7;
  1835.         break;
  1836.     case VK_F8:
  1837.         if (joystickAvailable && down)
  1838.         {
  1839.             appCore->joystickAxis(CelestiaCore::Joy_XAxis, 0);
  1840.             appCore->joystickAxis(CelestiaCore::Joy_YAxis, 0);
  1841.             appCore->joystickAxis(CelestiaCore::Joy_ZAxis, 0);
  1842.             useJoystick = !useJoystick;
  1843.         }
  1844.         break;
  1845.     case VK_F11:
  1846.         k = CelestiaCore::Key_F11;
  1847.         break;
  1848.     case VK_F12:
  1849.         k = CelestiaCore::Key_F12;
  1850.         break;
  1851.     case VK_NUMPAD2:
  1852.         k = CelestiaCore::Key_NumPad2;
  1853.         break;
  1854.     case VK_NUMPAD4:
  1855.         k = CelestiaCore::Key_NumPad4;
  1856.         break;
  1857.     case VK_NUMPAD5:
  1858.         k = CelestiaCore::Key_NumPad5;
  1859.         break;
  1860.     case VK_NUMPAD6:
  1861.         k = CelestiaCore::Key_NumPad6;
  1862.         break;
  1863.     case VK_NUMPAD7:
  1864.         k = CelestiaCore::Key_NumPad7;
  1865.         break;
  1866.     case VK_NUMPAD8:
  1867.         k = CelestiaCore::Key_NumPad8;
  1868.         break;
  1869.     case VK_NUMPAD9:
  1870.         k = CelestiaCore::Key_NumPad9;
  1871.         break;
  1872.     case VK_DELETE:
  1873.         if (!down)
  1874.             appCore->charEntered('177');
  1875.         break;
  1876.     case '0':
  1877.     case '1':
  1878.     case '2':
  1879.     case '3':
  1880.     case '4':
  1881.     case '5':
  1882.     case '6':
  1883.     case '7':
  1884.     case '8':
  1885.     case '9':
  1886.         // Special handling required to send Ctrl+number keys to
  1887.         // Celestia keyboard handler.
  1888.         if (!down && (modifiers & CelestiaCore::ControlKey))
  1889.             appCore->charEntered((char) key, modifiers);
  1890.         break;
  1891.     case 'A':
  1892.     case 'Z':
  1893.         if ((GetKeyState(VK_CONTROL) & 0x8000) == 0)
  1894.             k = key;
  1895.         break;
  1896.     }
  1897.     if (k >= 0)
  1898.     {
  1899.         if (down)
  1900.             appCore->keyDown(k, modifiers);
  1901.         else
  1902.             appCore->keyUp(k, modifiers);
  1903.     }
  1904. }
  1905. static void BuildScriptsMenu(HMENU menuBar, const string& scriptsDir)
  1906. {
  1907.     HMENU fileMenu = GetSubMenu(menuBar, 0);
  1908.     if (ScriptMenuItems != NULL)
  1909.         delete ScriptMenuItems;
  1910.     ScriptMenuItems = ScanScriptsDirectory(scriptsDir, false);
  1911.     if (ScriptMenuItems == NULL || ScriptMenuItems->size() == 0)
  1912.     {
  1913.         EnableMenuItem(fileMenu, ID_FILE_SCRIPTS, MF_GRAYED);
  1914.         return;
  1915.     }