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

OpenGL

开发平台:

Visual C++

  1.     
  2.     MENUITEMINFO info;
  3.     memset(&info, sizeof(info), 0);
  4.     info.cbSize = sizeof(info);
  5.     info.fMask = MIIM_SUBMENU;
  6.     BOOL result = GetMenuItemInfo(fileMenu, 1, TRUE, &info);
  7.     if (result)
  8.     {
  9.         HMENU scriptMenu = info.hSubMenu;
  10.         // Remove the old menu items
  11.         int count = GetMenuItemCount(scriptMenu);
  12.         while (count-- > 0)
  13.             DeleteMenu(scriptMenu, 0, MF_BYPOSITION);
  14.         for (unsigned int i = 0; i < ScriptMenuItems->size(); i++)
  15.         {
  16.             AppendMenu(scriptMenu, MF_STRING, ID_FIRST_SCRIPT + i, (*ScriptMenuItems)[i].title.c_str());
  17.         }
  18.     }
  19. }
  20. static void syncMenusWithRendererState()
  21. {
  22.     int renderFlags = appCore->getRenderer()->getRenderFlags();
  23.     int labelMode = appCore->getRenderer()->getLabelMode();
  24.     float ambientLight = appCore->getRenderer()->getAmbientLightLevel();
  25.     unsigned int textureRes = appCore->getRenderer()->getResolution();
  26.     setMenuItemCheck(ID_VIEW_SHOW_FRAMES,
  27.                      appCore->getFramesVisible());
  28.     setMenuItemCheck(ID_VIEW_SYNC_TIME,
  29.                      appCore->getSimulation()->getSyncTime());
  30.     if(abs(0.0 - (double)ambientLight) < 1.0e-3)
  31.     {
  32.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE,   MF_CHECKED);
  33.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW,    MF_UNCHECKED);
  34.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
  35.     }
  36.     else if(abs(0.1 - (double)ambientLight) < 1.0e-3)
  37.     {
  38.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE,   MF_UNCHECKED);
  39.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW,    MF_CHECKED);
  40.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
  41.     }
  42.     else if(abs(0.25 - (double)ambientLight) < 1.0e-3)
  43.     {
  44.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE,   MF_UNCHECKED);
  45.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW,    MF_UNCHECKED);
  46.         CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_CHECKED);
  47.     }
  48.     Renderer::StarStyle style = appCore->getRenderer()->getStarStyle();
  49.     CheckMenuItem(menuBar, ID_RENDER_STARSTYLE_FUZZY,
  50.                   style == Renderer::FuzzyPointStars ? MF_CHECKED : MF_UNCHECKED);
  51.     CheckMenuItem(menuBar, ID_RENDER_STARSTYLE_POINTS,
  52.                   style == Renderer::PointStars ? MF_CHECKED : MF_UNCHECKED);
  53.     CheckMenuItem(menuBar, ID_RENDER_STARSTYLE_DISCS,
  54.                   style == Renderer::ScaledDiscStars ? MF_CHECKED : MF_UNCHECKED);
  55.     CheckMenuItem(menuBar, ID_RENDER_TEXTURERES_LOW,
  56.                   textureRes == 0 ? MF_CHECKED : MF_UNCHECKED);
  57.     CheckMenuItem(menuBar, ID_RENDER_TEXTURERES_MEDIUM,
  58.                   textureRes == 1 ? MF_CHECKED : MF_UNCHECKED);
  59.     CheckMenuItem(menuBar, ID_RENDER_TEXTURERES_HIGH,
  60.                   textureRes == 2 ? MF_CHECKED : MF_UNCHECKED);
  61.     MENUITEMINFO menuInfo;
  62.     menuInfo.cbSize = sizeof(MENUITEMINFO);
  63.     menuInfo.fMask = MIIM_STATE;
  64.     if (GetMenuItemInfo(menuBar, ID_TIME_SHOWLOCAL, FALSE, &menuInfo))
  65.     {
  66.         if (appCore->getTimeZoneBias() == 0)
  67.             CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_UNCHECKED);
  68.         else
  69.             CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_CHECKED);
  70.     }
  71.     CheckMenuItem(menuBar, ID_RENDER_ANTIALIASING,
  72.         (renderFlags & Renderer::ShowSmoothLines)?MF_CHECKED:MF_UNCHECKED);
  73.     CheckMenuItem(menuBar, ID_RENDER_AUTOMAG,
  74.         (renderFlags & Renderer::ShowAutoMag)?MF_CHECKED:MF_UNCHECKED);
  75. }
  76. class WinAlerter : public CelestiaCore::Alerter
  77. {
  78. private:
  79.     HWND hwnd;
  80. public:
  81.     WinAlerter() : hwnd(NULL) {};
  82.     ~WinAlerter() {};
  83.     void fatalError(const std::string& msg)
  84.     {
  85. if (s_splash != NULL)
  86. s_splash->close();
  87.         MessageBox(NULL,
  88.                    msg.c_str(),
  89.                    "Fatal Error",
  90.                    MB_OK | MB_ICONERROR | MB_SETFOREGROUND);
  91.     }
  92. };
  93. static bool InitJoystick(JOYCAPS& caps)
  94. {
  95.     int nJoysticks = joyGetNumDevs();
  96.     if (nJoysticks <= 0)
  97.         return false;
  98.     if (joyGetDevCaps(JOYSTICKID1, &caps, sizeof caps) != JOYERR_NOERROR)
  99.     {
  100.         cout << "Error getting joystick caps.n";
  101.         return false;
  102.     }
  103.     cout << "Using joystick: " << caps.szPname << 'n';
  104.     return true;
  105. }
  106. static void HandleJoystick()
  107. {
  108.     JOYINFOEX info;
  109.     info.dwSize = sizeof info;
  110.     info.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNBUTTONS;
  111.     MMRESULT err = joyGetPosEx(JOYSTICKID1, &info);
  112.     if (err == JOYERR_NOERROR)
  113.     {
  114.         float x = (float) info.dwXpos / 32768.0f - 1.0f;
  115.         float y = (float) info.dwYpos / 32768.0f - 1.0f;
  116.         appCore->joystickAxis(CelestiaCore::Joy_XAxis, x);
  117.         appCore->joystickAxis(CelestiaCore::Joy_YAxis, y);
  118.         appCore->joystickButton(CelestiaCore::JoyButton1,
  119.                                 (info.dwButtons & 0x1) != 0);
  120.         appCore->joystickButton(CelestiaCore::JoyButton2,
  121.                                 (info.dwButtons & 0x2) != 0);
  122.         appCore->joystickButton(CelestiaCore::JoyButton7,
  123.                                 (info.dwButtons & 0x40) != 0);
  124.         appCore->joystickButton(CelestiaCore::JoyButton8,
  125.                                 (info.dwButtons & 0x80) != 0);
  126.     }
  127. }
  128. static bool GetRegistryValue(HKEY hKey, LPSTR cpValueName, LPVOID lpBuf, DWORD iBufSize)
  129. {
  130. /*
  131.     Function retrieves a value from the registry.
  132.     Key specified by open handle.
  133.     hKey        - Handle of open key for which a key value is requested.
  134.     cpValueName    - Name of Key Value to obtain value for.
  135.     lpBuf      - Buffer to receive value of Key Value.
  136.     iBufSize   - Size of buffer pointed to by lpBuf.
  137.     RETURN     - true if value was successfully retrieved, false otherwise.
  138.     Remarks: If requesting a string value, pass the character buffer to lpBuf.
  139.              If requesting a DWORD value, pass the DWORD variable's address to lpBuf.
  140.              If requesting binary data, pass the address of the binary buffer.
  141.              This function assumes you have an open registry key. Be sure to call
  142.              CloseKey() when finished.
  143. */
  144.     DWORD dwValueType, dwDataSize=0;
  145.     bool bRC=false;
  146.     dwDataSize = iBufSize;
  147.     if(RegQueryValueEx(hKey, cpValueName, NULL, &dwValueType,
  148.         (LPBYTE)lpBuf, &dwDataSize) == ERROR_SUCCESS)
  149.         bRC = true;
  150.     return bRC;
  151. }
  152. static bool SetRegistryInt(HKEY key, LPCTSTR value, int intVal)
  153. {
  154.     LONG err = RegSetValueEx(key,
  155.                              value,
  156.                              0,
  157.                              REG_DWORD,
  158.                              reinterpret_cast<CONST BYTE*>(&intVal),
  159.                              sizeof(DWORD));
  160.     return err == ERROR_SUCCESS;
  161. }
  162. static bool SetRegistry(HKEY key, LPCTSTR value, const string& strVal)
  163. {
  164.     LONG err = RegSetValueEx(key,
  165.                              value,
  166.                              0,
  167.                              REG_SZ,
  168.                              reinterpret_cast<CONST BYTE*> (strVal.c_str()),
  169.                              strVal.length() + 1);
  170.     return err == ERROR_SUCCESS;
  171. }
  172. static bool SetRegistryBin(HKEY hKey, LPSTR cpValueName, LPVOID lpData, int iDataSize)
  173. {
  174. /*
  175.     Function sets BINARY data in the registry.
  176.     Key specified by open handle.
  177.     hKey        - Handle of Key for which a value is to be set.
  178.     cpValueName - Name of Key Value to obtain value for.
  179.     lpData      - Address of binary data to store.
  180.     iDataSize   - Size of binary data contained in lpData.
  181.     RETURN      - Boolean true if value is successfully stored, false otherwise.
  182.     Remarks:        This function assumes you have an open registry key. Be sure to call
  183.                     CloseKey() when finished.
  184. */
  185.     bool bRC = false;
  186.     if (RegSetValueEx(hKey, cpValueName, 0, REG_BINARY,
  187.                       (LPBYTE) lpData, (DWORD) iDataSize) == ERROR_SUCCESS)
  188.     {
  189.         bRC = true;
  190.     }
  191.     return bRC;
  192. }
  193. static bool LoadPreferencesFromRegistry(LPTSTR regkey, AppPreferences& prefs)
  194. {
  195.     LONG err;
  196.     HKEY key;
  197.     DWORD disposition;
  198.     err = RegCreateKeyEx(HKEY_CURRENT_USER,
  199.                          regkey,
  200.                          0,
  201.                          NULL,
  202.                          REG_OPTION_NON_VOLATILE,
  203.                          KEY_ALL_ACCESS,
  204.                          NULL,
  205.                          &key,
  206.                          &disposition);
  207.     if (err  != ERROR_SUCCESS)
  208.     {
  209.         cout << "Error opening registry key: " << err << 'n';
  210.         return false;
  211.     }
  212.     GetRegistryValue(key, "Width", &prefs.winWidth, sizeof(prefs.winWidth));
  213.     GetRegistryValue(key, "Height", &prefs.winHeight, sizeof(prefs.winHeight));
  214.     GetRegistryValue(key, "XPos", &prefs.winX, sizeof(prefs.winX));
  215.     GetRegistryValue(key, "YPos", &prefs.winY, sizeof(prefs.winY));
  216.     GetRegistryValue(key, "RenderFlags", &prefs.renderFlags, sizeof(prefs.renderFlags));
  217.     GetRegistryValue(key, "LabelMode", &prefs.labelMode, sizeof(prefs.labelMode));
  218.     GetRegistryValue(key, "OrbitMask", &prefs.orbitMask, sizeof(prefs.orbitMask));
  219.     GetRegistryValue(key, "VisualMagnitude", &prefs.visualMagnitude, sizeof(prefs.visualMagnitude));
  220.     GetRegistryValue(key, "AmbientLight", &prefs.ambientLight, sizeof(prefs.ambientLight));
  221.     GetRegistryValue(key, "GalaxyLightGain", &prefs.galaxyLightGain, sizeof(prefs.galaxyLightGain));
  222.     GetRegistryValue(key, "ShowLocalTime", &prefs.showLocalTime, sizeof(prefs.showLocalTime));
  223.     GetRegistryValue(key, "DateFormat", &prefs.dateFormat, sizeof(prefs.dateFormat));
  224.     GetRegistryValue(key, "HudDetail", &prefs.hudDetail, sizeof(prefs.hudDetail));
  225.     GetRegistryValue(key, "FullScreenMode", &prefs.fullScreenMode, sizeof(prefs.fullScreenMode));
  226.     prefs.starStyle = Renderer::FuzzyPointStars;
  227.     GetRegistryValue(key, "StarStyle", &prefs.starStyle, sizeof(prefs.starStyle));
  228. prefs.renderPath = GLContext::GLPath_Basic;
  229. prefs.renderPathSet = GetRegistryValue(key, "RenderPath", &prefs.renderPath, sizeof(prefs.renderPath));
  230.     GetRegistryValue(key, "LastVersion", &prefs.lastVersion, sizeof(prefs.lastVersion));
  231.     GetRegistryValue(key, "TextureResolution", &prefs.textureResolution, sizeof(prefs.textureResolution));
  232.     char surfaceName[512];
  233.     surfaceName[0] = '';
  234.     if (GetRegistryValue(key, "AltSurface", surfaceName, sizeof(surfaceName)))
  235.         prefs.altSurfaceName = string(surfaceName);
  236.     if (prefs.lastVersion < 0x01020500)
  237.     {
  238.         prefs.renderFlags |= Renderer::ShowCometTails;
  239.         prefs.renderFlags |= Renderer::ShowRingShadows;
  240.     }
  241.     RegCloseKey(key);
  242.     return true;
  243. }
  244. static bool SavePreferencesToRegistry(LPTSTR regkey, AppPreferences& prefs)
  245. {
  246.     LONG err;
  247.     HKEY key;
  248.     cout << "Saving preferences . . .n";
  249.     err = RegOpenKeyEx(HKEY_CURRENT_USER,
  250.                        regkey,
  251.                        0,
  252.                        KEY_ALL_ACCESS,
  253.                        &key);
  254.     if (err  != ERROR_SUCCESS)
  255.     {
  256.         cout << "Error opening registry key: " << err << 'n';
  257.         return false;
  258.     }
  259.     cout << "Opened registry keyn";
  260.     SetRegistryInt(key, "Width", prefs.winWidth);
  261.     SetRegistryInt(key, "Height", prefs.winHeight);
  262.     SetRegistryInt(key, "XPos", prefs.winX);
  263.     SetRegistryInt(key, "YPos", prefs.winY);
  264.     SetRegistryInt(key, "RenderFlags", prefs.renderFlags);
  265.     SetRegistryInt(key, "LabelMode", prefs.labelMode);
  266.     SetRegistryInt(key, "OrbitMask", prefs.orbitMask);
  267.     SetRegistryBin(key, "VisualMagnitude", &prefs.visualMagnitude, sizeof(prefs.visualMagnitude));
  268.     SetRegistryBin(key, "AmbientLight", &prefs.ambientLight, sizeof(prefs.ambientLight));
  269.     SetRegistryBin(key, "GalaxyLightGain", &prefs.galaxyLightGain, sizeof(prefs.galaxyLightGain));
  270.     SetRegistryInt(key, "ShowLocalTime", prefs.showLocalTime);
  271.     SetRegistryInt(key, "DateFormat", prefs.dateFormat);
  272.     SetRegistryInt(key, "HudDetail", prefs.hudDetail);
  273.     SetRegistryInt(key, "FullScreenMode", prefs.fullScreenMode);
  274.     SetRegistryInt(key, "LastVersion", prefs.lastVersion);
  275.     SetRegistryInt(key, "StarStyle", prefs.starStyle);
  276.   SetRegistryInt(key, "RenderPath", prefs.renderPath);
  277.     SetRegistry(key, "AltSurface", prefs.altSurfaceName);
  278.     SetRegistryInt(key, "TextureResolution", prefs.textureResolution);
  279.     RegCloseKey(key);
  280.     return true;
  281. }
  282. static bool GetCurrentPreferences(AppPreferences& prefs)
  283. {
  284.     WINDOWPLACEMENT placement;
  285.     placement.length = sizeof(placement);
  286.     if (!GetWindowPlacement(mainWindow, &placement))
  287.         return false;
  288.     RECT rect = placement.rcNormalPosition;
  289.     prefs.winX = rect.left;
  290.     prefs.winY = rect.top;
  291.     prefs.winWidth = rect.right - rect.left;
  292.     prefs.winHeight = rect.bottom - rect.top;
  293.     prefs.renderFlags = appCore->getRenderer()->getRenderFlags();
  294.     prefs.labelMode = appCore->getRenderer()->getLabelMode();
  295.     prefs.orbitMask = appCore->getRenderer()->getOrbitMask();
  296.     prefs.visualMagnitude = appCore->getSimulation()->getFaintestVisible();
  297.     prefs.ambientLight = appCore->getRenderer()->getAmbientLightLevel();
  298.     prefs.galaxyLightGain = Galaxy::getLightGain();
  299.     prefs.showLocalTime = (appCore->getTimeZoneBias() != 0);
  300.     prefs.dateFormat = appCore->getDateFormat();
  301.     prefs.hudDetail = appCore->getHudDetail();
  302.     prefs.fullScreenMode = lastFullScreenMode;
  303.     prefs.lastVersion = 0x01040100;
  304.     prefs.altSurfaceName = appCore->getSimulation()->getActiveObserver()->getDisplayedSurface();
  305.     prefs.starStyle = appCore->getRenderer()->getStarStyle();
  306. prefs.renderPath = appCore->getRenderer()->getGLContext()->getRenderPath();
  307.     prefs.textureResolution = appCore->getRenderer()->getResolution();
  308.     return true;
  309. }
  310. static void HandleCaptureImage(HWND hWnd)
  311. {
  312.     // Display File SaveAs dialog to allow user to specify name and location of
  313.     // of captured screen image.
  314.     OPENFILENAME Ofn;
  315.     char szFile[_MAX_PATH+1], szFileTitle[_MAX_PATH+1];
  316.     szFile[0] = '';
  317.     szFileTitle[0] = '';
  318.     // Initialize OPENFILENAME
  319.     ZeroMemory(&Ofn, sizeof(OPENFILENAME));
  320.     Ofn.lStructSize = sizeof(OPENFILENAME);
  321.     Ofn.hwndOwner = hWnd;
  322.     Ofn.lpstrFilter = "JPEG - JFIF Compliant*.jpg;*.jif;*.jpegPortable Network Graphics*.png";
  323.     Ofn.lpstrFile= szFile;
  324.     Ofn.nMaxFile = sizeof(szFile);
  325.     Ofn.lpstrFileTitle = szFileTitle;
  326.     Ofn.nMaxFileTitle = sizeof(szFileTitle);
  327.     Ofn.lpstrInitialDir = (LPSTR)NULL;
  328.     // Comment this out if you just want the standard "Save As" caption.
  329.     Ofn.lpstrTitle = "Save As - Specify File to Capture Image";
  330.     // OFN_HIDEREADONLY - Do not display read-only JPEG or PNG files
  331.     // OFN_OVERWRITEPROMPT - If user selected a file, prompt for overwrite confirmation.
  332.     Ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
  333.     // Display the Save dialog box.
  334.     if (GetSaveFileName(&Ofn))
  335.     {
  336.         // If you got here, a path and file has been specified.
  337.         // Ofn.lpstrFile contains full path to specified file
  338.         // Ofn.lpstrFileTitle contains just the filename with extension
  339.         // Get the dimensions of the current viewport
  340.         int viewport[4];
  341.         glGetIntegerv(GL_VIEWPORT, viewport);
  342.         bool success = false;
  343.         DWORD nFileType=0;
  344.         char defaultExtensions[][4] = {"jpg", "png"};
  345.         if (Ofn.nFileExtension == 0)
  346.         {
  347.             // If no extension was specified, use the selection of filter to
  348.             // determine which type of file should be created, instead of
  349.             // just defaulting to JPEG.
  350.             nFileType = Ofn.nFilterIndex;
  351.             strcat(Ofn.lpstrFile, ".");
  352.             strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
  353.         }
  354.         else if (*(Ofn.lpstrFile + Ofn.nFileExtension) == '')
  355.         {
  356.             // If just a period was specified for the extension, use the
  357.             // selection of filter to determine which type of file should be
  358.             // created instead of just defaulting to JPEG.
  359.             nFileType = Ofn.nFilterIndex;
  360.             strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
  361.         }
  362.         else
  363.         {
  364.             switch (DetermineFileType(Ofn.lpstrFile))
  365.             {
  366.             case Content_JPEG:
  367.                 nFileType = 1;
  368.                 break;
  369.             case Content_PNG:
  370.                 nFileType = 2;
  371.                 break;
  372.             default:
  373.                 nFileType = 0;
  374.                 break;
  375.             }
  376.         }
  377.         // Redraw to make sure that the back buffer is up to date
  378.         appCore->draw();
  379.         if (nFileType == 1)
  380.         {
  381.             success = CaptureGLBufferToJPEG(string(Ofn.lpstrFile),
  382.                                             viewport[0], viewport[1],
  383.                                             viewport[2], viewport[3]);
  384.         }
  385.         else if (nFileType == 2)
  386.         {
  387.             success = CaptureGLBufferToPNG(string(Ofn.lpstrFile),
  388.                                            viewport[0], viewport[1],
  389.                                            viewport[2], viewport[3]);
  390.         }
  391.         else
  392.         {
  393.             // Invalid file extension specified.
  394.             DPRINTF(0, "WTF? Unknown file extension specified for screen capture.n");
  395.         }
  396.         if (!success)
  397.         {
  398.             char errorMsg[64];
  399.             if(nFileType == 0)
  400.                 sprintf(errorMsg, "Specified file extension is not recognized.");
  401.             else
  402.                 sprintf(errorMsg, "Could not save image file.");
  403.             MessageBox(hWnd, errorMsg, "Error", MB_OK | MB_ICONERROR);
  404.         }
  405.     }
  406. }
  407. static void HandleCaptureMovie(HWND hWnd)
  408. {
  409.     // TODO: The menu item should be disable so that the user doesn't even
  410.     // have the opportunity to record two movies simultaneously; the only
  411.     // thing missing to make this happen is notification when recording
  412.     // is complete.
  413.     if (appCore->isCaptureActive())
  414.     {
  415.         MessageBox(hWnd, "Stop current movie capture before starting another one.", "Error", MB_OK | MB_ICONERROR);
  416.         return;
  417.     }
  418.     // Display File SaveAs dialog to allow user to specify name and location of
  419.     // of captured movie
  420.     OPENFILENAME Ofn;
  421.     char szFile[_MAX_PATH+1], szFileTitle[_MAX_PATH+1];
  422.     szFile[0] = '';
  423.     szFileTitle[0] = '';
  424.     // Initialize OPENFILENAME
  425.     ZeroMemory(&Ofn, sizeof(OPENFILENAME));
  426.     Ofn.lStructSize = sizeof(OPENFILENAME);
  427.     Ofn.hwndOwner = hWnd;
  428.     Ofn.lpstrFilter = "Microsoft AVI*.avi";
  429.     Ofn.lpstrFile= szFile;
  430.     Ofn.nMaxFile = sizeof(szFile);
  431.     Ofn.lpstrFileTitle = szFileTitle;
  432.     Ofn.nMaxFileTitle = sizeof(szFileTitle);
  433.     Ofn.lpstrInitialDir = (LPSTR)NULL;
  434.     // Comment this out if you just want the standard "Save As" caption.
  435.     Ofn.lpstrTitle = "Save As - Specify Output File for Capture Movie";
  436.     // OFN_HIDEREADONLY - Do not display read-only video files
  437.     // OFN_OVERWRITEPROMPT - If user selected a file, prompt for overwrite confirmation.
  438.     Ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT  | OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_NOCHANGEDIR;
  439.     Ofn.hInstance = appInstance;
  440.     Ofn.lpTemplateName = MAKEINTRESOURCE(IDD_MOVIE_PARAMS_CHOOSER);
  441.     Ofn.lpfnHook = ChooseMovieParamsProc;
  442.     // Display the Save dialog box.
  443.     if (GetSaveFileName(&Ofn))
  444.     {
  445.         // If you got here, a path and file has been specified.
  446.         // Ofn.lpstrFile contains full path to specified file
  447.         // Ofn.lpstrFileTitle contains just the filename with extension
  448.         bool success = false;
  449.         DWORD nFileType=0;
  450.         char defaultExtensions[][4] = { "avi" };
  451.         if (Ofn.nFileExtension == 0)
  452.         {
  453.             // If no extension was specified, use the selection of filter to
  454.             // determine which type of file should be created, instead of
  455.             // just defaulting to AVI.
  456.             nFileType = Ofn.nFilterIndex;
  457.             strcat(Ofn.lpstrFile, ".");
  458.             strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
  459.         }
  460.         else if (*(Ofn.lpstrFile + Ofn.nFileExtension) == '')
  461.         {
  462.             // If just a period was specified for the extension, use
  463.             // the selection of filter to determine which type of file
  464.             // should be created, instead of just defaulting to AVI.
  465.             nFileType = Ofn.nFilterIndex;
  466.             strcat(Ofn.lpstrFile, defaultExtensions[nFileType-1]);
  467.         }
  468.         else
  469.         {
  470.             switch (DetermineFileType(Ofn.lpstrFile))
  471.             {
  472.             case Content_AVI:
  473.                 nFileType = 1;
  474.                 break;
  475.             default:
  476.                 nFileType = 0;
  477.                 break;
  478.             }
  479.         }
  480.         if (nFileType != 1)
  481.         {
  482.             // Invalid file extension specified.
  483.             DPRINTF(0, "Unknown file extension specified for movie capture.n");
  484.         }
  485.         else
  486.         {
  487.             success = BeginMovieCapture(string(Ofn.lpstrFile),
  488.                                         MovieSizes[movieSize][0],
  489.                                         MovieSizes[movieSize][1],
  490.                                         MovieFramerates[movieFramerate]);
  491.         }
  492.         if (!success)
  493.         {
  494.             char errorMsg[64];
  495.             if (nFileType == 0)
  496.                 sprintf(errorMsg, "Specified file extension is not recognized.");
  497.             else
  498.                 sprintf(errorMsg, "Could not capture movie.");
  499.             MessageBox(hWnd, errorMsg, "Error", MB_OK | MB_ICONERROR);
  500.         }
  501.     }
  502. }
  503. static void HandleOpenScript(HWND hWnd, CelestiaCore* appCore)
  504. {
  505.     // Display File Open dialog to allow user to specify name and location of
  506.     // of captured screen image.
  507.     OPENFILENAME Ofn;
  508.     char szFile[_MAX_PATH + 1];
  509.     char szFileTitle[_MAX_PATH + 1];
  510.     // Save the current directory
  511.     char currentDir[_MAX_PATH + 1];
  512.     currentDir[0] = '';
  513.     GetCurrentDirectory(sizeof(currentDir), currentDir);
  514.     szFile[0] = '';
  515.     szFileTitle[0] = '';
  516.     // Initialize OPENFILENAME
  517.     ZeroMemory(&Ofn, sizeof(OPENFILENAME));
  518.     Ofn.lStructSize = sizeof(OPENFILENAME);
  519.     Ofn.hwndOwner = hWnd;
  520.     Ofn.lpstrFilter = "Celestia Script*.celx;*.clx;*.cel";
  521.     Ofn.lpstrFile= szFile;
  522.     Ofn.nMaxFile = sizeof(szFile);
  523.     Ofn.lpstrFileTitle = szFileTitle;
  524.     Ofn.nMaxFileTitle = sizeof(szFileTitle);
  525.     Ofn.lpstrInitialDir = (LPSTR)NULL;
  526.     // Comment this out if you just want the standard "Save As" caption.
  527.     // Ofn.lpstrTitle = "Save As - Specify File to Capture Image";
  528.     // Display the Open dialog box.
  529.     if (GetOpenFileName(&Ofn))
  530.     {
  531.         // If you got here, a path and file has been specified.
  532.         // Ofn.lpstrFile contains full path to specified file
  533.         // Ofn.lpstrFileTitle contains just the filename with extension
  534.         ContentType type = DetermineFileType(Ofn.lpstrFile);
  535.         if (type == Content_CelestiaScript)
  536.         {
  537.             appCore->runScript(Ofn.lpstrFile);
  538.         }
  539.         else if (type == Content_CelestiaLegacyScript)
  540.         {
  541.             ifstream scriptfile(Ofn.lpstrFile);
  542.             if (!scriptfile.good())
  543.             {
  544.                 MessageBox(hWnd, "Error opening script file.", "Error",
  545.                            MB_OK | MB_ICONERROR);
  546.             }
  547.             else
  548.             {
  549.                 CommandParser parser(scriptfile);
  550.                 CommandSequence* script = parser.parse();
  551.                 if (script == NULL)
  552.                 {
  553.                     const vector<string>* errors = parser.getErrors();
  554.                     const char* errorMsg = "";
  555.                     if (errors->size() > 0)
  556.                         errorMsg = (*errors)[0].c_str();
  557.                     MessageBox(hWnd, errorMsg, "Error in script file.",
  558.                                MB_OK | MB_ICONERROR);
  559.                 }
  560.                 else
  561.                 {
  562.                     appCore->cancelScript(); // cancel any running script
  563.                     appCore->runScript(script);
  564.                 }
  565.             }
  566.         }
  567.     }
  568.     if (strlen(currentDir) != 0)
  569.         SetCurrentDirectory(currentDir);
  570. }
  571. bool operator<(const DEVMODE& a, const DEVMODE& b)
  572. {
  573.     if (a.dmBitsPerPel != b.dmBitsPerPel)
  574.         return a.dmBitsPerPel < b.dmBitsPerPel;
  575.     if (a.dmPelsWidth != b.dmPelsWidth)
  576.         return a.dmPelsWidth < b.dmPelsWidth;
  577.     if (a.dmPelsHeight != b.dmPelsHeight)
  578.         return a.dmPelsHeight < b.dmPelsHeight;
  579.     return a.dmDisplayFrequency < b.dmDisplayFrequency;
  580. }
  581. vector<DEVMODE>* EnumerateDisplayModes(unsigned int minBPP)
  582. {
  583.     vector<DEVMODE>* modes = new vector<DEVMODE>();
  584.     if (modes == NULL)
  585.         return NULL;
  586.     DEVMODE dm;
  587.     int i = 0;
  588.     while (EnumDisplaySettings(NULL, i, &dm))
  589.     {
  590.         if (dm.dmBitsPerPel >= minBPP)
  591.             modes->insert(modes->end(), dm);
  592.         i++;
  593.     }
  594.     sort(modes->begin(), modes->end());
  595.     // Bail out early if EnumDisplaySettings fails for some messed up reason
  596.     if (i == 0)
  597.         return modes;
  598.     // Go through the sorted list and eliminate modes that differ only
  599.     // by refresh rates.
  600.     vector<DEVMODE>::iterator keepIter = modes->begin();
  601.     vector<DEVMODE>::const_iterator iter = modes->begin();
  602.     iter++;
  603.     for (; iter != modes->end(); iter++)
  604.     {
  605.         if (iter->dmPelsWidth != keepIter->dmPelsWidth ||
  606.             iter->dmPelsHeight != keepIter->dmPelsHeight ||
  607.             iter->dmBitsPerPel != keepIter->dmBitsPerPel)
  608.         {
  609.             *++keepIter = *iter;
  610.         }
  611.     }
  612.     modes->resize(keepIter - modes->begin() + 1);
  613.     // Select the fallback display mode--choose 640x480 at the current
  614.     // pixel depth.  If for some bizarre reason that's not available,
  615.     // fall back to the first mode in the list.
  616.     fallbackFullScreenMode = 0;
  617.     for (iter = modes->begin(); iter != modes->end(); iter++)
  618.     {
  619.         if (iter->dmPelsWidth == 640 && iter->dmPelsHeight == 480)
  620.         {
  621.             // Add one to the mode index, since mode 0 means windowed mode
  622.             fallbackFullScreenMode = (iter - modes->begin()) + 1;
  623.             break;
  624.         }
  625.     }
  626.     if (fallbackFullScreenMode == 0 && modes->size() > 0)
  627.         fallbackFullScreenMode = 1;
  628.     lastFullScreenMode = fallbackFullScreenMode;
  629.     return modes;
  630. }
  631. static char* skipSpace(char* s)
  632. {
  633.     while (*s == ' ')
  634.         s++;
  635.     return s;
  636. }
  637. static char* skipUntilQuote(char* s)
  638. {
  639.     while (*s != '"' && s != '')
  640.         s++;
  641.     return s;
  642. }
  643. static char* nextWord(char* s)
  644. {
  645.     while (*s != ' ' && *s != '')
  646.         s++;
  647.     return s;
  648. }
  649. static char** splitCommandLine(LPSTR cmdLine,
  650.                                int& argc)
  651. {
  652.     int nArgs = 0;
  653.     int maxArgs = 50;
  654.     char** argv = new char*[maxArgs];
  655.     cmdLine = skipSpace(cmdLine);
  656.     while (*cmdLine != '')
  657.     {
  658.         char* startOfWord = cmdLine;
  659.         char* endOfWord = cmdLine;
  660.         int wordLength = 0;
  661.         if (*cmdLine == '"')
  662.         {
  663.             // Handle quoted strings
  664.             startOfWord = cmdLine + 1;
  665.             endOfWord = skipUntilQuote(startOfWord);
  666.             wordLength = endOfWord - startOfWord;
  667.             if (*endOfWord != '')
  668.                 endOfWord++;
  669.         }
  670.         else
  671.         {
  672.             endOfWord = nextWord(cmdLine);
  673.             wordLength = endOfWord - startOfWord;
  674.             assert(wordLength != 0);
  675.         }
  676.         char* newWord = new char[wordLength + 1];
  677.         strncpy(newWord, startOfWord, wordLength);
  678.         newWord[wordLength] = '';
  679.         if (nArgs == maxArgs)
  680.         {
  681.             char** newArgv = new char*[maxArgs * 2];
  682.             copy(argv, argv + nArgs, newArgv);
  683.             delete argv;
  684.             argv = newArgv;
  685.             maxArgs *= 2;
  686.         }
  687.         argv[nArgs] = newWord;
  688.         nArgs++;
  689.         cmdLine = endOfWord;
  690.         cmdLine = skipSpace(cmdLine);
  691.     }
  692.     argc = nArgs;
  693.     return argv;
  694. }
  695. static bool startFullscreen = false;
  696. static bool runOnce = false;
  697. static string startURL;
  698. static string startDirectory;
  699. static string startScript;
  700. static vector<string> extrasDirectories;
  701. static string configFileName;
  702. static bool useAlternateConfigFile = false;
  703. static bool skipSplashScreen = false;
  704. static bool parseCommandLine(int argc, char* argv[])
  705. {
  706.     int i = 0;
  707.     while (i < argc)
  708.     {
  709.         bool isLastArg = (i == argc - 1);
  710.         if (strcmp(argv[i], "--verbose") == 0)
  711.         {
  712.             SetDebugVerbosity(1);
  713.         }
  714.         else if (strcmp(argv[i], "--fullscreen") == 0)
  715.         {
  716.             startFullscreen = true;
  717.         }
  718.         else if (strcmp(argv[i], "--once") == 0)
  719.         {
  720.             runOnce = true;
  721.         }
  722.         else if (strcmp(argv[i], "--dir") == 0)
  723.         {
  724.             if (isLastArg)
  725.             {
  726.                 MessageBox(NULL,
  727.                            "Directory expected after --dir", "Celestia Command Line Error",
  728.                            MB_OK | MB_ICONERROR);
  729.                 return false;
  730.             }
  731.             i++;
  732.             startDirectory = string(argv[i]);
  733.         }
  734.         else if (strcmp(argv[i], "--conf") == 0)
  735.         {
  736.             if (isLastArg)
  737.             {
  738.                 MessageBox(NULL,
  739.                            "Configuration file name expected after --conf",
  740.                            "Celestia Command Line Error",
  741.                            MB_OK | MB_ICONERROR);
  742.                 return false;
  743.             }
  744.             i++;
  745.             configFileName = string(argv[i]);
  746.             useAlternateConfigFile = true;
  747.         }
  748.         else if (strcmp(argv[i], "--extrasdir") == 0)
  749.         {
  750.             if (isLastArg)
  751.             {
  752.                 MessageBox(NULL,
  753.                            "Directory expected after --extrasdir", "Celestia Command Line Error",
  754.                            MB_OK | MB_ICONERROR);
  755.                 return false;
  756.             }
  757.             i++;
  758.             extrasDirectories.push_back(string(argv[i]));
  759.         }
  760.         else if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "--url") == 0)
  761.         {
  762.             if (isLastArg)
  763.             {
  764.                 MessageBox(NULL,
  765.                            "URL expected after --url", "Celestia Command Line Error",
  766.                            MB_OK | MB_ICONERROR);
  767.                 return false;
  768.             }
  769.             i++;
  770.             startURL = string(argv[i]);
  771.         }
  772.         else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--nosplash") == 0)
  773.         {
  774.             skipSplashScreen = true;
  775.         }
  776.         else
  777.         {
  778.             char* buf = new char[strlen(argv[i]) + 256];
  779.             sprintf(buf, "Invalid command line option '%s'", argv[i]);
  780.             MessageBox(NULL,
  781.                        buf, "Celestia Command Line Error",
  782.                        MB_OK | MB_ICONERROR);
  783.             delete[] buf;
  784.             return false;
  785.         }
  786.         i++;
  787.     }
  788.     return true;
  789. }
  790. class WinSplashProgressNotifier : public ProgressNotifier
  791. {
  792. public:
  793.     WinSplashProgressNotifier(SplashWindow* _splash) : splash(_splash) {};
  794.     virtual ~WinSplashProgressNotifier() {};
  795.     
  796.     virtual void update(const string& filename)
  797.     {
  798.         splash->setMessage(UTF8ToCurrentCP(_("Loading: ")) + filename);
  799.     }
  800.     
  801. private:
  802.     SplashWindow* splash;
  803. };
  804. int APIENTRY WinMain(HINSTANCE hInstance,
  805.                      HINSTANCE hPrevInstance,
  806.                      LPSTR     lpCmdLine,
  807.                      int       nCmdShow)
  808. {
  809.         
  810.     // Say we're not ready to render yet.
  811.     bReady = false;
  812.     appInstance = hInstance;
  813.     int argc;
  814.     char** argv;
  815.     argv = splitCommandLine(lpCmdLine, argc);
  816.     bool cmdLineOK = parseCommandLine(argc, argv);
  817.     if (!cmdLineOK)
  818.         return 1;
  819.     // If Celestia was invoked with the --once command line parameter,
  820.     // check to see if there's already an instance of Celestia running.
  821.     if (runOnce)
  822.     {
  823.         // The FindWindow method of checking for another running instance
  824.         // is broken, and we should be using CreateMutex instead.  But we'll
  825.         // sort that out later . . .
  826.         HWND existingWnd = FindWindow(AppName, AppName);
  827.         if (existingWnd)
  828.         {
  829.             // If there's an existing instance and we've been given a
  830.             // URL on the command line, send the URL to the running instance
  831.             // of Celestia before terminating.
  832.             if (startURL != "")
  833.             {
  834.                 COPYDATASTRUCT cd;
  835.                 cd.dwData = 0;
  836.                 cd.cbData = startURL.length();
  837.                 cd.lpData = reinterpret_cast<void*>(const_cast<char*>(startURL.c_str()));
  838.                 SendMessage(existingWnd, WM_COPYDATA, 0, reinterpret_cast<LPARAM>(&cd));
  839.             }
  840.             SetForegroundWindow(existingWnd);
  841.             Sleep(1000);
  842.             exit(0);
  843.         }
  844.     }
  845.     // If a start directory was given on the command line, switch to it
  846.     // now.
  847.     if (startDirectory != "")
  848.         SetCurrentDirectory(startDirectory.c_str());
  849. s_splash = new SplashWindow("splash.png");
  850.     s_splash->setMessage("Loading data files...");
  851.     if (!skipSplashScreen)
  852.         s_splash->showSplash();
  853.     
  854.     OleInitialize(NULL);
  855.     dropTarget = new CelestiaDropTarget();
  856.     if (dropTarget)
  857.     {
  858.         if (CoLockObjectExternal(dropTarget, TRUE, TRUE) != S_OK)
  859.         {
  860.             cout << "Error locking drop targetn";
  861.             delete dropTarget;
  862.             dropTarget = NULL;
  863.         }
  864.     }
  865.     // Specify some default values in case registry keys are not found. Ideally, these
  866.     // defaults should never be used--they should be overridden by settings in
  867.     // celestia.cfg.
  868.     AppPreferences prefs;
  869.     prefs.winWidth = 800;
  870.     prefs.winHeight = 600;
  871.     prefs.winX = CW_USEDEFAULT;
  872.     prefs.winY = CW_USEDEFAULT;
  873.     prefs.ambientLight = 0.1f;  // Low
  874.     prefs.galaxyLightGain = 0.0f;
  875.     prefs.labelMode = 0;
  876.     prefs.orbitMask = Body::Planet | Body::Moon;
  877.     prefs.renderFlags = Renderer::DefaultRenderFlags;
  878.     prefs.visualMagnitude = 6.0f;   //Default specified in Simulation::Simulation()
  879.     prefs.showLocalTime = 0;
  880.     prefs.dateFormat = 0;
  881.     prefs.hudDetail = 1;
  882.     prefs.fullScreenMode = -1;
  883.     prefs.lastVersion = 0x00000000;
  884.     prefs.textureResolution = 1;
  885.     LoadPreferencesFromRegistry(CelestiaRegKey, prefs);
  886.     // Adjust window dimensions for screen dimensions
  887.     int screenWidth = GetSystemMetrics(SM_CXSCREEN);
  888.     int screenHeight = GetSystemMetrics(SM_CYSCREEN);
  889.     if (prefs.winWidth > screenWidth)
  890.         prefs.winWidth = screenWidth;
  891.     if (prefs.winHeight > screenHeight)
  892.         prefs.winHeight = screenHeight;
  893.     if (prefs.winX != CW_USEDEFAULT && prefs.winY != CW_USEDEFAULT)
  894.     {
  895.         if (prefs.winX + prefs.winWidth > screenWidth)
  896.             prefs.winX = screenWidth - prefs.winWidth;
  897.         if (prefs.winY + prefs.winHeight > screenHeight)
  898.             prefs.winY = screenHeight - prefs.winHeight;
  899.     }
  900.     // Make sure windowRect contains default window size in case Celestia is
  901.     // launched in fullscreen mode. Without this code, CreateOpenGLWindow()
  902.     // will be called with windowRect = {0, 0, 0, 0} when the user switches to
  903.     // windowed mode.
  904.     windowRect.left = prefs.winX;
  905.     windowRect.top = prefs.winY;
  906.     windowRect.right = windowRect.left + prefs.winWidth;
  907.     windowRect.bottom = windowRect.top + prefs.winHeight;
  908.     joystickAvailable = InitJoystick(joystickCaps);
  909.     displayModes = EnumerateDisplayModes(16);
  910.     // If full screen mode was found in registry, override default with it.
  911.     if (prefs.fullScreenMode != -1)
  912.         lastFullScreenMode = prefs.fullScreenMode;
  913.     // If the user changes the computer's graphics card or driver, the
  914.     // number of display modes may change. Since we're storing a display
  915.     // index this can cause troubles. Avoid out of range errors by
  916.     // checking against the size of the mode list, falling back to a
  917.     // safe mode if the last used mode index is now out of range.
  918.     // TODO: A MUCH better fix for the problem of changing GPU/driver
  919.     // is to store the mode parameters instead of just the mode index.
  920.     if (lastFullScreenMode > displayModes->size())
  921.     {
  922.         lastFullScreenMode = fallbackFullScreenMode;
  923.     }
  924.     appCore = new CelestiaCore();
  925.     if (appCore == NULL)
  926.     {
  927.         if (s_splash != NULL)
  928.         {
  929.             s_splash->close();
  930.             delete s_splash;
  931.             s_splash = NULL;
  932.         }
  933.         MessageBox(NULL,
  934.                    "Out of memory.", "Fatal Error",
  935.                    MB_OK | MB_ICONERROR | MB_TOPMOST);    
  936.         return false;
  937.     }
  938.     // Gettext integration
  939.     setlocale(LC_ALL, ""); 
  940.     setlocale(LC_NUMERIC, "C"); 
  941.     bindtextdomain("celestia","locale");
  942.     bind_textdomain_codeset("celestia", "UTF-8"); 
  943.     bindtextdomain("celestia_constellations","locale");
  944.     bind_textdomain_codeset("celestia_constellations", "UTF-8"); 
  945.     textdomain("celestia");
  946.     // Loading localized resources
  947.     char res[255];
  948.     sprintf(res, "locale\res_%s.dll", _("LANGUAGE"));
  949.     int langID = 0;
  950.     if (sscanf(_("WinLangID"), "%x", &langID) == 1)
  951.         SetThreadLocale(langID);
  952.     if ((hRes = LoadLibrary(res)) == NULL) {
  953.         cout << "Couldn't load localized resources: "<< res<< "n";
  954.         hRes = hInstance;
  955.     }
  956.     appCore->setAlerter(new WinAlerter());
  957.     WinSplashProgressNotifier* progressNotifier = NULL;
  958.     if (!skipSplashScreen)
  959.         progressNotifier = new WinSplashProgressNotifier(s_splash);
  960.         
  961.     string* altConfig = useAlternateConfigFile ? &configFileName : NULL;
  962.     bool initSucceeded = appCore->initSimulation(altConfig, &extrasDirectories, progressNotifier);
  963.     
  964.     delete progressNotifier;
  965.     // Close the splash screen after all data has been loaded
  966. if (s_splash != NULL)
  967. {
  968. s_splash->close();
  969. delete s_splash;
  970.         s_splash = NULL;
  971. }
  972. // Give up now if we failed initialization
  973. if (!initSucceeded)
  974. return 1;
  975.     if (startURL != "")
  976.         appCore->setStartURL(startURL);
  977.     menuBar = CreateMenuBar();
  978.     acceleratorTable = LoadAccelerators(hRes,
  979.                                         MAKEINTRESOURCE(IDR_ACCELERATORS));
  980.     if (appCore->getConfig() != NULL)
  981.     {
  982.         if (!compareIgnoringCase(appCore->getConfig()->cursor, "arrow"))
  983.             hDefaultCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
  984.         else if (!compareIgnoringCase(appCore->getConfig()->cursor, "inverting crosshair"))
  985.             hDefaultCursor = LoadCursor(hRes, MAKEINTRESOURCE(IDC_CROSSHAIR));
  986.         else
  987.             hDefaultCursor = LoadCursor(hRes, MAKEINTRESOURCE(IDC_CROSSHAIR_OPAQUE));
  988.     }
  989.     cursorHandler = new WinCursorHandler(hDefaultCursor);
  990.     appCore->setCursorHandler(cursorHandler);
  991.     InitWGLExtensions(appInstance);
  992.     HWND hWnd;
  993.     if (startFullscreen)
  994.     {
  995.         hWnd = CreateOpenGLWindow(0, 0, 800, 600,
  996.                                   lastFullScreenMode, currentScreenMode);
  997.         // Prevent unnecessary destruction and recreation of OpenGLWindow in
  998.         // while() loop below.
  999.         newScreenMode = currentScreenMode;
  1000.     }
  1001.     else
  1002.     {
  1003.         hWnd = CreateOpenGLWindow(prefs.winX, prefs.winY,
  1004.                                   prefs.winWidth, prefs.winHeight,
  1005.                                   0, currentScreenMode);
  1006.     }
  1007.     if (hWnd == NULL)
  1008.     {
  1009.         MessageBox(NULL,
  1010.                    "Failed to create the application window.",
  1011.                    "Fatal Error",
  1012.            MB_OK | MB_ICONERROR);
  1013.         return FALSE;
  1014.     }
  1015.     if (dropTarget != NULL)
  1016.     {
  1017.         HRESULT hr = RegisterDragDrop(hWnd, dropTarget);
  1018.         if (hr != S_OK)
  1019.         {
  1020.             cout << "Failed to register drop target as OLE object.n";
  1021.         }
  1022.     }
  1023.     mainWindow = hWnd;
  1024.     UpdateWindow(mainWindow);
  1025.     // Initialize common controls
  1026.     INITCOMMONCONTROLSEX icex;
  1027.     icex.dwSize = sizeof(icex);
  1028.     icex.dwICC = ICC_DATE_CLASSES;
  1029.     InitCommonControlsEx(&icex);
  1030.     extern void RegisterDatePicker();
  1031.     RegisterDatePicker();
  1032.     if (!appCore->initRenderer())
  1033.     {
  1034.         return 1;
  1035.     }
  1036.     // Set values saved in registry: renderFlags, visualMagnitude, labelMode and timezone bias.
  1037.     if (prefs.lastVersion != 0)
  1038.     {
  1039.         appCore->getSimulation()->setFaintestVisible(prefs.visualMagnitude);
  1040.         appCore->getRenderer()->setRenderFlags(prefs.renderFlags);
  1041.         appCore->getRenderer()->setLabelMode(prefs.labelMode);
  1042.         appCore->getRenderer()->setOrbitMask(prefs.orbitMask);
  1043.         appCore->getRenderer()->setAmbientLightLevel(prefs.ambientLight);
  1044.         Galaxy::setLightGain(prefs.galaxyLightGain);
  1045.         appCore->getRenderer()->setStarStyle(prefs.starStyle);
  1046.         appCore->setHudDetail(prefs.hudDetail);
  1047.         if (prefs.showLocalTime == 1)
  1048.             ShowLocalTime(appCore);
  1049.         else
  1050.             ShowUniversalTime(appCore);
  1051.         appCore->setDateFormat((astro::Date::Format) prefs.dateFormat);
  1052.         appCore->getSimulation()->getActiveObserver()->setDisplayedSurface(prefs.altSurfaceName);
  1053.         appCore->getRenderer()->setResolution(prefs.textureResolution);
  1054.         if (prefs.renderPathSet)
  1055.         {
  1056.             GLContext* glContext = appCore->getRenderer()->getGLContext();
  1057.             if (glContext->renderPathSupported(prefs.renderPath))
  1058.                 glContext->setRenderPath(prefs.renderPath);
  1059.         }
  1060.     }
  1061.     else
  1062.     {
  1063.         // Set default render flags for a new installation
  1064.         appCore->getRenderer()->setRenderFlags(Renderer::DefaultRenderFlags);
  1065.     }
  1066.     
  1067.     BuildFavoritesMenu(menuBar, appCore, appInstance, &odAppMenu);
  1068.     BuildScriptsMenu(menuBar, ScriptsDirectory);
  1069.     syncMenusWithRendererState();
  1070.     appCore->setContextMenuCallback(ContextMenu);
  1071.     bReady = true;
  1072.     
  1073.     // Get the current time
  1074.     time_t systime = time(NULL);
  1075.     struct tm *gmt = gmtime(&systime);
  1076.     double timeTDB = astro::J2000;
  1077.     if (gmt != NULL)
  1078.     {
  1079.         astro::Date d;
  1080.         d.year = gmt->tm_year + 1900;
  1081.         d.month = gmt->tm_mon + 1;
  1082.         d.day = gmt->tm_mday;
  1083.         d.hour = gmt->tm_hour;
  1084.         d.minute = gmt->tm_min;
  1085.         d.seconds = (int) gmt->tm_sec;
  1086.         timeTDB = astro::UTCtoTDB(d);
  1087.     }
  1088.     appCore->start(timeTDB);
  1089.     if (startURL != "")
  1090.     {
  1091.         COPYDATASTRUCT cd;
  1092.         cd.dwData = 0;
  1093.         cd.cbData = startURL.length();
  1094.         cd.lpData = reinterpret_cast<void*>(const_cast<char*>(startURL.c_str()));
  1095.         SendMessage(mainWindow, WM_COPYDATA, 0, reinterpret_cast<LPARAM>(&cd));
  1096.     }
  1097.     // Initial tick required before first frame is rendered; this gives
  1098.     // start up scripts a chance to run.
  1099.     appCore->tick();
  1100.     MSG msg;
  1101.     PeekMessage(&msg, NULL, 0U, 0U, PM_NOREMOVE);
  1102.     while (msg.message != WM_QUIT)
  1103.     {
  1104.         bool isVisible = !IsIconic(mainWindow);
  1105.         // If Celestia is in an inactive state, use GetMessage to avoid
  1106.         // sucking CPU cycles.  If time is paused and the camera isn't
  1107.         // moving in any view, we can probably also avoid constantly
  1108.         // rendering.
  1109.         BOOL haveMessage;
  1110.         if (isVisible)
  1111.             haveMessage = PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE);
  1112.         else
  1113.             haveMessage = GetMessage(&msg, NULL, 0U, 0U);
  1114.         if (!haveMessage)
  1115.         {
  1116.             // Tick the simulation
  1117.             appCore->tick();
  1118.         }
  1119.         if (haveMessage)
  1120.         {
  1121.             bool dialogMessage = false;
  1122.             if (starBrowser != NULL &&
  1123.                 IsDialogMessage(starBrowser->hwnd, &msg))
  1124.                 dialogMessage = true;
  1125.             if (solarSystemBrowser != NULL &&
  1126.                 IsDialogMessage(solarSystemBrowser->hwnd, &msg))
  1127.                 dialogMessage = true;
  1128.             if (tourGuide != NULL &&
  1129.                 IsDialogMessage(tourGuide->hwnd, &msg))
  1130.                 dialogMessage = true;
  1131.             if (gotoObjectDlg != NULL &&
  1132.                 IsDialogMessage(gotoObjectDlg->hwnd, &msg))
  1133.                 dialogMessage = true;
  1134.             if (viewOptionsDlg != NULL &&
  1135.                 IsDialogMessage(viewOptionsDlg->hwnd, &msg))
  1136.                 dialogMessage = true;
  1137.             if (eclipseFinder != NULL &&
  1138.                 IsDialogMessage(eclipseFinder->hwnd, &msg))
  1139.                 dialogMessage = true;
  1140.             if (locationsDlg != NULL &&
  1141.                 IsDialogMessage(locationsDlg->hwnd, &msg))
  1142.                 dialogMessage = true;
  1143.             // Translate and dispatch the message
  1144.             if (!dialogMessage)
  1145.             {
  1146.                 if (!TranslateAccelerator(mainWindow, acceleratorTable, &msg))
  1147.                     TranslateMessage(&msg);
  1148.                 DispatchMessage(&msg);
  1149.             }
  1150.         }
  1151.         else
  1152.         {
  1153.             // And force a redraw
  1154.             InvalidateRect(mainWindow, NULL, FALSE);
  1155.         }
  1156.         if (useJoystick)
  1157.             HandleJoystick();
  1158.         if (currentScreenMode != newScreenMode)
  1159.         {
  1160.             if (currentScreenMode == 0)
  1161.                 GetWindowRect(mainWindow, &windowRect);
  1162.             else
  1163.                 lastFullScreenMode = currentScreenMode;
  1164.             DestroyOpenGLWindow();
  1165.             mainWindow = CreateOpenGLWindow(windowRect.left,
  1166.                                             windowRect.top,
  1167.                                             windowRect.right-windowRect.left,
  1168.                                             windowRect.bottom-windowRect.top,
  1169.                                             newScreenMode,
  1170.                                             currentScreenMode);
  1171.             UpdateWindow(mainWindow);
  1172.         }
  1173.     }
  1174.     // Save application preferences
  1175.     {
  1176.         AppPreferences prefs;
  1177.         if (GetCurrentPreferences(prefs))
  1178.             SavePreferencesToRegistry(CelestiaRegKey, prefs);
  1179.     }
  1180.     // Not ready to render anymore.
  1181.     bReady = false;
  1182.     // Clean up the window
  1183.     if (currentScreenMode != 0)
  1184.         RestoreDisplayMode();
  1185.     DestroyOpenGLWindow();
  1186.     if (appCore != NULL)
  1187.         delete appCore;
  1188.     OleUninitialize();
  1189.     return msg.wParam;
  1190. }
  1191. bool modifiers(WPARAM wParam, WPARAM mods)
  1192. {
  1193.     return (wParam & mods) == mods;
  1194. }
  1195. static void RestoreCursor()
  1196. {
  1197.     ShowCursor(TRUE);
  1198.     cursorVisible = true;
  1199.     SetCursorPos(saveCursorPos.x, saveCursorPos.y);
  1200. }
  1201. LRESULT CALLBACK MainWindowProc(HWND hWnd,
  1202.                                 UINT uMsg,
  1203.                                 WPARAM wParam, LPARAM lParam)
  1204. {
  1205.     switch(uMsg)
  1206.     {
  1207.     case WM_CREATE:
  1208.         //Instruct menu class to enumerate menu structure
  1209.         odAppMenu.Init(hWnd, menuBar);
  1210.         //Associate some menu items with bitmap resources
  1211.         odAppMenu.SetItemImage(appInstance, ID_FILE_OPENSCRIPT, IDB_SCRIPT);
  1212.         odAppMenu.SetItemImage(appInstance, ID_FILE_CAPTUREIMAGE, IDB_CAMERA);
  1213.         odAppMenu.SetItemImage(appInstance, ID_FILE_CAPTUREMOVIE, IDB_CAMCORDER);
  1214.         odAppMenu.SetItemImage(appInstance, ID_FILE_EXIT, IDB_EXIT);
  1215.         odAppMenu.SetItemImage(appInstance, ID_TIME_SETTIME, IDB_CLOCK);
  1216.         odAppMenu.SetItemImage(appInstance, ID_TIME_FREEZE, IDB_STOP);
  1217.         odAppMenu.SetItemImage(appInstance, ID_RENDER_VIEWOPTIONS, IDB_SUNGLASSES);
  1218.         odAppMenu.SetItemImage(appInstance, ID_RENDER_LOCATIONS, IDB_GLOBE);
  1219.         odAppMenu.SetItemImage(appInstance, ID_HELP_RUNDEMO, IDB_SCRIPT);
  1220.         odAppMenu.SetItemImage(appInstance, ID_HELP_CONTROLS, IDB_CONFIG);
  1221.         odAppMenu.SetItemImage(appInstance, ID_HELP_ABOUT, IDB_ABOUT);
  1222.         DragAcceptFiles(hWnd, TRUE);
  1223.         break;
  1224.     case WM_DROPFILES:
  1225.         break;
  1226.     case WM_MEASUREITEM:
  1227.         odAppMenu.MeasureItem(hWnd, lParam);
  1228.         return TRUE;
  1229.     case WM_DRAWITEM:
  1230.         odAppMenu.DrawItem(hWnd, lParam);
  1231.         return TRUE;
  1232.     case WM_MOUSEMOVE:
  1233.         {
  1234.     int x, y;
  1235.     x = LOWORD(lParam);
  1236.     y = HIWORD(lParam);
  1237.             bool reallyMoved = x != lastMouseMove.x || y != lastMouseMove.y;
  1238.             lastMouseMove.x = x;
  1239.             lastMouseMove.y = y;
  1240.             if (reallyMoved)
  1241.             {
  1242.                 appCore->mouseMove((float) x, (float) y);
  1243.                 if ((wParam & (MK_LBUTTON | MK_RBUTTON)) != 0)
  1244.                 {
  1245. #ifdef INFINITE_MOUSE
  1246.                     // A bit of mouse tweaking here . . .  we want to allow the
  1247.                     // user to rotate and zoom continuously, without having to
  1248.                     // pick up the mouse every time it leaves the window.  So,
  1249.                     // once we start dragging, we'll hide the mouse and reset
  1250.                     // its position every time it's moved.
  1251.                     POINT pt;
  1252.                     pt.x = lastX;
  1253.                     pt.y = lastY;
  1254.                     ClientToScreen(hWnd, &pt);
  1255.                     // If the cursor is still visible, this is the first mouse
  1256.                     // move message of this drag.  Hide the cursor and set the
  1257.                     // cursor position to the center of the window.  Once the
  1258.                     // drag is complete, we'll restore the cursor position and
  1259.                     // make it visible again.
  1260.                     if (ignoreNextMoveEvent)
  1261.                     {
  1262.                         // This hack is required because there's a move event
  1263.                         // right after canceling a context menu by clicking
  1264.                         // outside of it.  Because it was canceled by clicking,
  1265.                         // the mouse button down bits are set, and the infinite
  1266.                         // mouse code gets confused.
  1267.                         ignoreNextMoveEvent = false;
  1268.                     }
  1269.                     else if (cursorVisible)
  1270.                     {
  1271.                         // Hide the cursor
  1272.                         ShowCursor(FALSE);
  1273.                         cursorVisible = false;
  1274.                         // Save the cursor position
  1275.                         saveCursorPos = pt;
  1276.                         // Compute the center point of the client area
  1277.                         RECT rect;
  1278.                         GetClientRect(hWnd, &rect);
  1279.                         POINT center;
  1280.                         center.x = (rect.right - rect.left) / 2;
  1281.                         center.y = (rect.bottom - rect.top) / 2;
  1282.                         // Set the cursor position to the center of the window
  1283.                         x = center.x + (x - lastX);
  1284.                         y = center.y + (y - lastY);
  1285.                         lastX = center.x;
  1286.                         lastY = center.y;
  1287.                         ClientToScreen(hWnd, &center);
  1288.                         SetCursorPos(center.x, center.y);
  1289.                     }
  1290.                     else
  1291.                     {
  1292.                         if (x - lastX != 0 || y - lastY != 0)
  1293.                             SetCursorPos(pt.x, pt.y);
  1294.                     }
  1295. #else
  1296.                     lastX = x;
  1297.                     lastY = y;
  1298. #endif // INFINITE_MOUSE
  1299.                 }
  1300.                 int buttons = 0;
  1301.                 if ((wParam & MK_LBUTTON) != 0)
  1302.                     buttons |= CelestiaCore::LeftButton;
  1303.                 if ((wParam & MK_RBUTTON) != 0)
  1304.                     buttons |= CelestiaCore::RightButton;
  1305.                 if ((wParam & MK_MBUTTON) != 0)
  1306.                     buttons |= CelestiaCore::MiddleButton;
  1307.                 if ((wParam & MK_SHIFT) != 0)
  1308.                     buttons |= CelestiaCore::ShiftKey;
  1309.                 if ((wParam & MK_CONTROL) != 0)
  1310.                     buttons |= CelestiaCore::ControlKey;
  1311.                 appCore->mouseMove((float) (x - lastX),
  1312.                                    (float) (y - lastY),
  1313.                                    buttons);
  1314.                 if (currentScreenMode != 0)
  1315.                 {
  1316.                     if (hideMenuBar && y < 10)
  1317.                     {
  1318.                         SetMenu(mainWindow, menuBar);
  1319.                         hideMenuBar = false;
  1320.                     }
  1321.                     else if (!hideMenuBar && y >= 10)
  1322.                     {
  1323.                         SetMenu(mainWindow, NULL);
  1324.                         hideMenuBar = true;
  1325.                     }
  1326.                 }
  1327.             }
  1328.         }
  1329.         break;
  1330.     case WM_LBUTTONDOWN:
  1331.         lastX = LOWORD(lParam);
  1332.         lastY = HIWORD(lParam);
  1333.         appCore->mouseButtonDown(LOWORD(lParam), HIWORD(lParam),
  1334.                                  CelestiaCore::LeftButton);
  1335.         break;
  1336.     case WM_RBUTTONDOWN:
  1337.         lastX = LOWORD(lParam);
  1338.         lastY = HIWORD(lParam);
  1339.         appCore->mouseButtonDown(LOWORD(lParam), HIWORD(lParam),
  1340.                                  CelestiaCore::RightButton);
  1341.         break;
  1342.     case WM_MBUTTONDOWN:
  1343.         lastX = LOWORD(lParam);
  1344.         lastY = HIWORD(lParam);
  1345.         appCore->mouseButtonDown(LOWORD(lParam), HIWORD(lParam),
  1346.                                  CelestiaCore::MiddleButton);
  1347.         break;
  1348.     case WM_LBUTTONUP:
  1349.         if (!cursorVisible)
  1350.             RestoreCursor();
  1351.         appCore->mouseButtonUp(LOWORD(lParam), HIWORD(lParam),
  1352.                                CelestiaCore::LeftButton);
  1353.         break;
  1354.     case WM_RBUTTONUP:
  1355.         if (!cursorVisible)
  1356.             RestoreCursor();
  1357.         appCore->mouseButtonUp(LOWORD(lParam), HIWORD(lParam),
  1358.                                CelestiaCore::RightButton);
  1359.         break;
  1360.     case WM_MBUTTONUP:
  1361.         lastX = LOWORD(lParam);
  1362.         lastY = HIWORD(lParam);
  1363.         appCore->mouseButtonUp(LOWORD(lParam), HIWORD(lParam),
  1364.                                CelestiaCore::MiddleButton);
  1365.         break;
  1366.     case WM_MOUSEWHEEL:
  1367.         {
  1368.             int modifiers = 0;
  1369.             if ((wParam & MK_SHIFT) != 0)
  1370.                 modifiers |= CelestiaCore::ShiftKey;
  1371.             appCore->mouseWheel((short) HIWORD(wParam) > 0 ? -1.0f : 1.0f,
  1372.                                 modifiers);
  1373.         }
  1374.         break;
  1375.     case WM_KEYDOWN:
  1376.         switch (wParam)
  1377.         {
  1378.         case VK_ESCAPE:
  1379.             appCore->charEntered('33');
  1380.             break;
  1381.         case VK_INSERT:
  1382.         case 'C':
  1383.             if ((GetKeyState(VK_LCONTROL) | GetKeyState(VK_RCONTROL)) & 0x8000)
  1384.             {
  1385.                 CopyStateURLToClipboard();
  1386.                 appCore->flash(_("Copied URL"));
  1387.             }
  1388.             break;
  1389.         default:
  1390.             handleKey(wParam, true);
  1391.             break;
  1392.         }
  1393.         break;
  1394.     case WM_KEYUP:
  1395.         handleKey(wParam, false);
  1396.         break;
  1397.     case WM_CHAR:
  1398.         {
  1399.             // Bits 16-23 of lParam specify the scan code of the key pressed.
  1400.             // Ignore all keypad input, this will be handled by WM_KEYDOWN
  1401.             // messages.
  1402.             char cScanCode = (char) (HIWORD(lParam) & 0xFF);
  1403.             if((cScanCode >= 71 && cScanCode <= 73) ||
  1404.                (cScanCode >= 75 && cScanCode <= 77) ||
  1405.                (cScanCode >= 79 && cScanCode <= 83))
  1406.             {
  1407.                 break;
  1408.             }
  1409.             int charCode = (char) wParam;
  1410.             int modifiers = 0;
  1411.             if (GetKeyState(VK_SHIFT) & 0x8000)
  1412.                 modifiers |= CelestiaCore::ShiftKey;
  1413.             // Catch backtab (shift+Tab)
  1414.             if (charCode == '11' && (modifiers & CelestiaCore::ShiftKey))
  1415.                 charCode = CelestiaCore::Key_BackTab;
  1416.             Renderer* r = appCore->getRenderer();
  1417.             int oldRenderFlags = r->getRenderFlags();
  1418.             int oldLabelMode = r->getLabelMode();
  1419.             //  Convert charCode from current locale to UTF-8
  1420.             char utf8CharCode[7];
  1421.             memset(utf8CharCode, 0, sizeof(utf8CharCode));
  1422.             WCHAR wCharCode;
  1423.             MultiByteToWideChar(CP_ACP, 0, (char*)&charCode, 1, &wCharCode, 1);
  1424.             WideCharToMultiByte(CP_UTF8, 0, &wCharCode, 1, utf8CharCode, 7, 0, 0);
  1425.             /*cerr << "Char input: (ANSI) " << (int)(unsigned char)charCode << " - UTF8 -> ";
  1426.             for(int i=0; utf8CharCode[i] != ''; i++) cerr << (int)(unsigned char)(utf8CharCode[i]) << " ";
  1427.             cerr << "[" << utf8CharCode << "]" << endl;*/
  1428.             Renderer::StarStyle oldStarStyle = r->getStarStyle();
  1429.             appCore->charEntered(utf8CharCode, modifiers);
  1430.             if (r->getRenderFlags() != oldRenderFlags ||
  1431.                 r->getLabelMode() != oldLabelMode ||
  1432.                 r->getStarStyle() != oldStarStyle)
  1433.             {
  1434.                 syncMenusWithRendererState();
  1435.             }
  1436.         }
  1437.         break;
  1438.     case WM_IME_CHAR:
  1439.         {
  1440.             char ch[2];
  1441.             char utf8CharCode[7];
  1442.             memset(utf8CharCode, 0, sizeof(utf8CharCode));
  1443.             WCHAR wCharCode;
  1444.             ch[0] = (wParam >> 8);
  1445.             ch[1] = (wParam & 0xff);
  1446.             if (ch[0]) MultiByteToWideChar(CP_ACP, 0, ch, 2, &wCharCode, 1);
  1447.             else MultiByteToWideChar(CP_ACP, 0, &ch[1], 1, &wCharCode, 1);
  1448.             WideCharToMultiByte(CP_UTF8, 0, &wCharCode, 1, utf8CharCode, 7, 0, 0);
  1449.             appCore->charEntered(utf8CharCode);
  1450.             /*cerr << "IME input: (ANSI) " << (int)(unsigned char)ch[0] << " " << (int)(unsigned char)ch[1] << " - UTF8 -> ";
  1451.             for(int i=0; utf8CharCode[i] != ''; i++) cerr << (int)(unsigned char)(utf8CharCode[i]) << " ";
  1452.             cerr << "[" << utf8CharCode << "]" << endl;*/
  1453.         }
  1454.         break;
  1455.     case WM_COPYDATA:
  1456.         // The copy data message is used to send URL strings between
  1457.         // processes.
  1458.         {
  1459.             COPYDATASTRUCT* cd = reinterpret_cast<COPYDATASTRUCT*>(lParam);
  1460.             if (cd != NULL && cd->lpData != NULL)
  1461.             {
  1462.                 char* urlChars = reinterpret_cast<char*>(cd->lpData);
  1463.                 if (cd->cbData > 3) // minimum of "cel:" or ".cel"
  1464.                 {
  1465.                     string urlString(urlChars, cd->cbData);
  1466.                     if (!urlString.substr(0,4).compare("cel:"))
  1467.                     {
  1468.                         appCore->flash(_("Loading URL"));
  1469.                         appCore->goToUrl(urlString);
  1470.                     }
  1471.                     else if (DetermineFileType(urlString) == Content_CelestiaScript)
  1472.                     {
  1473.                         appCore->runScript(urlString);
  1474.                     }
  1475.                     else
  1476.                     {
  1477.                         ifstream scriptfile(urlString.c_str());
  1478.                         if (!scriptfile.good())
  1479.                         {
  1480.                             appCore->flash(_("Error opening script"));
  1481.                         }
  1482.                         else
  1483.                         {
  1484.                             // TODO: Need to fix memory leak with scripts;
  1485.                             // a refcount is probably required.
  1486.                             CommandParser parser(scriptfile);
  1487.                             CommandSequence* script = parser.parse();
  1488.                             if (script == NULL)
  1489.                             {
  1490.                                 const vector<string>* errors = parser.getErrors();
  1491.                                 const char* errorMsg = "";
  1492.                                 if (errors->size() > 0)
  1493.                                 {
  1494.                                     errorMsg = (*errors)[0].c_str();
  1495.                                     appCore->flash(errorMsg);
  1496.                                 }
  1497.                                 else
  1498.                                 {
  1499.                                     appCore->flash(_("Error loading script"));
  1500.                                 }
  1501.                             }
  1502.                             else
  1503.                             {
  1504.                                 appCore->flash(_("Running script"));
  1505.                                 appCore->runScript(script);
  1506.                             }
  1507.                         }
  1508.                     }
  1509.                 }
  1510.             }
  1511.         }
  1512.         break;
  1513.     case WM_COMMAND:
  1514.         switch (LOWORD(wParam))
  1515.         {
  1516.         case ID_NAVIGATION_CENTER:
  1517.             appCore->charEntered('c');
  1518.             break;
  1519.         case ID_NAVIGATION_GOTO:
  1520.             appCore->charEntered('G');
  1521.             break;
  1522.         case ID_NAVIGATION_FOLLOW:
  1523.             appCore->charEntered('F');
  1524.             break;
  1525.         case ID_NAVIGATION_SYNCORBIT:
  1526.             appCore->charEntered('Y');
  1527.             break;
  1528.         case ID_NAVIGATION_TRACK:
  1529.             appCore->charEntered('T');
  1530.             break;
  1531.         case ID_NAVIGATION_HOME:
  1532.             appCore->charEntered('H');
  1533.             break;
  1534.         case ID_NAVIGATION_SELECT:
  1535.             DialogBox(hRes, MAKEINTRESOURCE(IDD_FINDOBJECT), hWnd, FindObjectProc);
  1536.             break;
  1537.         case ID_NAVIGATION_GOTO_OBJECT:
  1538.             if (gotoObjectDlg == NULL)
  1539.                 gotoObjectDlg = new GotoObjectDialog(hRes, hWnd, appCore);
  1540.             break;
  1541.         case IDCLOSE:
  1542.             if (reinterpret_cast<LPARAM>(gotoObjectDlg) == lParam &&
  1543.                 gotoObjectDlg != NULL)
  1544.             {
  1545.                 delete gotoObjectDlg;
  1546.                 gotoObjectDlg = NULL;
  1547.             }
  1548.             else if (reinterpret_cast<LPARAM>(tourGuide) == lParam &&
  1549.                      tourGuide != NULL)
  1550.             {
  1551.                 delete tourGuide;
  1552.                 tourGuide = NULL;
  1553.             }
  1554.             else if (reinterpret_cast<LPARAM>(starBrowser) == lParam &&
  1555.                      starBrowser != NULL)
  1556.             {
  1557.                 delete starBrowser;
  1558.                 starBrowser = NULL;
  1559.             }
  1560.             else if (reinterpret_cast<LPARAM>(solarSystemBrowser) == lParam &&
  1561.                      solarSystemBrowser != NULL)
  1562.             {
  1563.                 delete solarSystemBrowser;
  1564.                 solarSystemBrowser = NULL;
  1565.             }
  1566.             else if (reinterpret_cast<LPARAM>(viewOptionsDlg) == lParam &&
  1567.                 viewOptionsDlg != NULL)
  1568.             {
  1569.                 delete viewOptionsDlg;
  1570.                 viewOptionsDlg = NULL;
  1571.             }
  1572.             else if (reinterpret_cast<LPARAM>(eclipseFinder) == lParam &&
  1573.                      eclipseFinder != NULL)
  1574.             {
  1575.                 delete eclipseFinder;
  1576.                 eclipseFinder = NULL;
  1577.             }
  1578.             else if (reinterpret_cast<LPARAM>(locationsDlg) == lParam &&
  1579.                      locationsDlg != NULL)
  1580.             {
  1581.                 delete locationsDlg;
  1582.                 locationsDlg = NULL;
  1583.             }
  1584.             break;
  1585.         case ID_NAVIGATION_TOURGUIDE:
  1586.             if (tourGuide == NULL)
  1587.                 tourGuide = new TourGuide(hRes, hWnd, appCore);
  1588.             break;
  1589.         case ID_NAVIGATION_SSBROWSER:
  1590.             if (solarSystemBrowser == NULL)
  1591.                 solarSystemBrowser = new SolarSystemBrowser(hRes, hWnd, appCore);
  1592.             break;
  1593.         case ID_NAVIGATION_STARBROWSER:
  1594.             if (starBrowser == NULL)
  1595.                 starBrowser = new StarBrowser(hRes, hWnd, appCore);
  1596.             break;
  1597.         case ID_NAVIGATION_ECLIPSEFINDER:
  1598.             if (eclipseFinder == NULL)
  1599.                 eclipseFinder = new EclipseFinderDialog(hRes, hWnd, appCore);
  1600.             break;
  1601.         case ID_RENDER_DISPLAYMODE:
  1602.             newScreenMode = currentScreenMode;
  1603.             CreateDialogParam(hRes,
  1604.                               MAKEINTRESOURCE(IDD_DISPLAYMODE),
  1605.                               hWnd,
  1606.                               SelectDisplayModeProc,
  1607.                               NULL);
  1608.             break;
  1609.         case ID_RENDER_FULLSCREEN:
  1610.             if (currentScreenMode == 0)
  1611.                 newScreenMode = lastFullScreenMode;
  1612.             else
  1613.                 newScreenMode = 0;
  1614.             break;
  1615.         case ID_RENDER_VIEWOPTIONS:
  1616.             if (viewOptionsDlg == NULL)
  1617.                 viewOptionsDlg = new ViewOptionsDialog(hRes, hWnd, appCore);
  1618.             break;
  1619.         case ID_RENDER_LOCATIONS:
  1620.             if (locationsDlg == NULL)
  1621.                 locationsDlg = new LocationsDialog(hRes, hWnd, appCore);
  1622.             break;
  1623.         case ID_RENDER_MORESTARS:
  1624.             appCore->charEntered(']');
  1625.             break;
  1626.         case ID_RENDER_FEWERSTARS:
  1627.             appCore->charEntered('[');
  1628.             break;
  1629.         case ID_RENDER_AUTOMAG:
  1630.             appCore->charEntered('31');
  1631.             syncMenusWithRendererState();
  1632.             break;
  1633.         case ID_RENDER_AMBIENTLIGHT_NONE:
  1634.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE,   MF_CHECKED);
  1635.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW,    MF_UNCHECKED);
  1636.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
  1637.             appCore->getRenderer()->setAmbientLightLevel(0.0f);
  1638.             break;
  1639.         case ID_RENDER_AMBIENTLIGHT_LOW:
  1640.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE,   MF_UNCHECKED);
  1641.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW,    MF_CHECKED);
  1642.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_UNCHECKED);
  1643.             appCore->getRenderer()->setAmbientLightLevel(0.1f);
  1644.             break;
  1645.         case ID_RENDER_AMBIENTLIGHT_MEDIUM:
  1646.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_NONE,   MF_UNCHECKED);
  1647.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_LOW,    MF_UNCHECKED);
  1648.             CheckMenuItem(menuBar, ID_RENDER_AMBIENTLIGHT_MEDIUM, MF_CHECKED);
  1649.             appCore->getRenderer()->setAmbientLightLevel(0.25f);
  1650.             break;
  1651.         case ID_RENDER_STARSTYLE_FUZZY:
  1652.             appCore->getRenderer()->setStarStyle(Renderer::FuzzyPointStars);
  1653.             syncMenusWithRendererState();
  1654.             break;
  1655.         case ID_RENDER_STARSTYLE_POINTS:
  1656.             appCore->getRenderer()->setStarStyle(Renderer::PointStars);
  1657.             syncMenusWithRendererState();
  1658.             break;
  1659.         case ID_RENDER_STARSTYLE_DISCS:
  1660.             appCore->getRenderer()->setStarStyle(Renderer::ScaledDiscStars);
  1661.             syncMenusWithRendererState();
  1662.             break;
  1663.         case ID_RENDER_TEXTURERES_LOW:
  1664.             appCore->getRenderer()->setResolution(0);
  1665.             syncMenusWithRendererState();
  1666.             break;
  1667.         case ID_RENDER_TEXTURERES_MEDIUM:
  1668.             appCore->getRenderer()->setResolution(1);
  1669.             syncMenusWithRendererState();
  1670.             break;
  1671.         case ID_RENDER_TEXTURERES_HIGH:
  1672.             appCore->getRenderer()->setResolution(2);
  1673.             syncMenusWithRendererState();
  1674.             break;
  1675.         case ID_RENDER_ANTIALIASING:
  1676.             appCore->charEntered('30');
  1677.             syncMenusWithRendererState();
  1678.             break;
  1679.         case ID_RENDER_BODY_AXES:
  1680.             appCore->toggleReferenceMark("body axes");
  1681.             break;
  1682.         case ID_RENDER_FRAME_AXES:
  1683.             appCore->toggleReferenceMark("frame axes");
  1684.             break;
  1685.         case ID_RENDER_SUN_DIRECTION:
  1686.             appCore->toggleReferenceMark("sun direction");
  1687.             break;
  1688.         case ID_RENDER_VELOCITY_VECTOR:
  1689.             appCore->toggleReferenceMark("velocity vector");
  1690.             break;
  1691.         case ID_RENDER_PLANETOGRAPHIC_GRID:
  1692.             appCore->toggleReferenceMark("planetographic grid");
  1693.             break;
  1694.         case ID_RENDER_TERMINATOR:
  1695.             appCore->toggleReferenceMark("terminator");
  1696.             break;
  1697.         case ID_TIME_FASTER:
  1698.             appCore->charEntered('l');
  1699.             break;
  1700.         case ID_TIME_SLOWER:
  1701.             appCore->charEntered('k');
  1702.             break;
  1703.         case ID_TIME_REALTIME:
  1704.             appCore->charEntered('\');
  1705.             break;
  1706.         case ID_TIME_FREEZE:
  1707.             appCore->charEntered(' ');
  1708.             break;
  1709.         case ID_TIME_REVERSE:
  1710.             appCore->charEntered('J');
  1711.             break;
  1712.         case ID_TIME_SETTIME:
  1713.             ShowSetTimeDialog(hRes, hWnd, appCore);
  1714.             
  1715.             // Update the local time menu item--since the set time dialog handles setting the time zone,
  1716.             // should we just get rid of the menu item?
  1717.             if (appCore->getTimeZoneBias() == 0)
  1718.                 CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_UNCHECKED);
  1719.             else
  1720.                 CheckMenuItem(menuBar, ID_TIME_SHOWLOCAL, MF_CHECKED);
  1721.             break;
  1722.         case ID_TIME_SHOWLOCAL:
  1723.             if (ToggleMenuItem(menuBar, ID_TIME_SHOWLOCAL))
  1724.                 ShowLocalTime(appCore);
  1725.             else
  1726.                 ShowUniversalTime(appCore);
  1727.             break;
  1728.         case ID_VIEW_HSPLIT:
  1729.             appCore->splitView(View::HorizontalSplit);
  1730.             break;
  1731.         case ID_VIEW_VSPLIT:
  1732.             appCore->splitView(View::VerticalSplit);
  1733.             break;
  1734.         case ID_VIEW_SINGLE:
  1735.             appCore->singleView();
  1736.             break;
  1737.         case ID_VIEW_DELETE_ACTIVE:
  1738.             appCore->deleteView();
  1739.             break;
  1740.         case ID_VIEW_SHOW_FRAMES:
  1741.             appCore->setFramesVisible(!appCore->getFramesVisible());
  1742.             syncMenusWithRendererState();
  1743.             break;
  1744.         case ID_VIEW_SYNC_TIME:
  1745.             {
  1746.                 Simulation* sim = appCore->getSimulation();
  1747.                 sim->setSyncTime(!sim->getSyncTime());
  1748.                 if (sim->getSyncTime())
  1749.                     sim->synchronizeTime();
  1750.                 syncMenusWithRendererState();
  1751.             }
  1752.             break;
  1753.         case ID_BOOKMARKS_ADDBOOKMARK:
  1754.             DialogBox(hRes, MAKEINTRESOURCE(IDD_ADDBOOKMARK), hWnd, AddBookmarkProc);
  1755.             break;
  1756.         case ID_BOOKMARKS_ORGANIZE:
  1757.             DialogBox(hRes, MAKEINTRESOURCE(IDD_ORGANIZE_BOOKMARKS), hWnd, OrganizeBookmarksProc);
  1758.             break;
  1759.         case ID_HELP_RUNDEMO:
  1760.             appCore->charEntered('D');
  1761.             break;
  1762.         case ID_HELP_CONTROLS:
  1763.             CreateDialogParam(hRes,
  1764.                               MAKEINTRESOURCE(IDD_CONTROLSHELP),
  1765.                               hWnd,
  1766.                               ControlsHelpProc,
  1767.                               NULL);
  1768.             break;
  1769.         case ID_HELP_ABOUT:
  1770.             DialogBox(hRes, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutProc);
  1771.             break;
  1772.         case ID_HELP_GLINFO:
  1773.             DialogBox(hRes, MAKEINTRESOURCE(IDD_GLINFO), hWnd, GLInfoProc);
  1774.             break;
  1775.         case ID_HELP_LICENSE:
  1776.             DialogBox(hRes, MAKEINTRESOURCE(IDD_LICENSE), hWnd, LicenseProc);
  1777.             break;
  1778.         case ID_INFO:
  1779.             ShowWWWInfo(appCore->getSimulation()->getSelection());
  1780.             break;
  1781.         case ID_FILE_OPENSCRIPT:
  1782.             HandleOpenScript(hWnd, appCore);
  1783.             break;
  1784.         case ID_FILE_CAPTUREIMAGE:
  1785.             HandleCaptureImage(hWnd);
  1786.             break;
  1787.         case ID_FILE_CAPTUREMOVIE:
  1788.             HandleCaptureMovie(hWnd);
  1789.             break;
  1790.         case ID_FILE_EXIT:
  1791.             SendMessage(hWnd, WM_CLOSE, 0, 0);
  1792.             break;
  1793.         case ID_GOTO_URL:
  1794.             {
  1795.                 // Relies on a pointer in lparam, do this does not
  1796.                 // work cross-process.
  1797.                 char* urlString = reinterpret_cast<char*>(lParam);
  1798.                 if (urlString != NULL)
  1799.                 {
  1800.                     appCore->flash(string("URL: ") + string(urlString));
  1801. appCore->goToUrl(urlString);
  1802.                 }
  1803.             }
  1804.             break;
  1805.         case ID_TOOLS_MARK:
  1806.             {
  1807.                 Simulation* sim = appCore->getSimulation();
  1808.                 if (sim->getUniverse() != NULL)
  1809.                 {
  1810.                     MarkerRepresentation markerRep(MarkerRepresentation::Diamond,
  1811.                                                    10.0f,
  1812.                                                    Color(0.0f, 1.0f, 0.0f, 0.9f));
  1813.  
  1814.                     sim->getUniverse()->markObject(sim->getSelection(),
  1815.                                                    markerRep,
  1816.                                                    1);
  1817.                     appCore->getRenderer()->setRenderFlags(appCore->getRenderer()->getRenderFlags() | Renderer::ShowMarkers);
  1818.                 }
  1819.             }
  1820.             break;
  1821.         case ID_TOOLS_UNMARK:
  1822.             {
  1823.                 Simulation* sim = appCore->getSimulation();
  1824.                 if (sim->getUniverse() != NULL)
  1825.                     sim->getUniverse()->unmarkObject(sim->getSelection(), 1);
  1826.             }
  1827.             break;
  1828.         default:
  1829.             {
  1830.                 const FavoritesList* favorites = appCore->getFavorites();
  1831.                 if (favorites != NULL &&
  1832.                     LOWORD(wParam) >= ID_BOOKMARKS_FIRSTBOOKMARK &&
  1833.                     LOWORD(wParam) - ID_BOOKMARKS_FIRSTBOOKMARK < (int) favorites->size())
  1834.                 {
  1835.                     int whichFavorite = LOWORD(wParam) - ID_BOOKMARKS_FIRSTBOOKMARK;
  1836.                     appCore->activateFavorite(*(*favorites)[whichFavorite]);
  1837.                 }
  1838.                 else if (LOWORD(wParam) >= MENU_CHOOSE_PLANET &&
  1839.                          LOWORD(wParam) < MENU_CHOOSE_PLANET + 1000)
  1840.                 {
  1841.                     // Handle the satellite/child object submenu
  1842.                     Selection sel = appCore->getSimulation()->getSelection();
  1843.                     switch (sel.getType())
  1844.                     {
  1845.                     case Selection::Type_Star:
  1846.                         appCore->getSimulation()->selectPlanet(LOWORD(wParam) - MENU_CHOOSE_PLANET);
  1847.                         break;
  1848.                     case Selection::Type_Body:
  1849.                         {
  1850.                             PlanetarySystem* satellites = (PlanetarySystem*) sel.body()->getSatellites();
  1851.                             appCore->getSimulation()->setSelection(Selection(satellites->getBody(LOWORD(wParam) - MENU_CHOOSE_PLANET)));
  1852.                             break;
  1853.                         }
  1854.                     case Selection::Type_DeepSky:
  1855.                         // Current deep sky object/galaxy implementation does
  1856.                         // not have children to select.
  1857.                         break;
  1858.                     case Selection::Type_Location:
  1859.                         break;
  1860.                     default:
  1861.                         break;
  1862.                     }
  1863.                 }
  1864.                 else if (LOWORD(wParam) >= MENU_CHOOSE_SURFACE &&
  1865.                          LOWORD(wParam) < MENU_CHOOSE_SURFACE + 1000)
  1866.                 {
  1867.                     // Handle the alternate surface submenu
  1868.                     Selection sel = appCore->getSimulation()->getSelection();
  1869.                     if (sel.body() != NULL)
  1870.                     {
  1871.                         int index = (int) LOWORD(wParam) - MENU_CHOOSE_SURFACE - 1;
  1872.                         vector<string>* surfNames = sel.body()->getAlternateSurfaceNames();
  1873.                         if (surfNames != NULL)
  1874.                         {
  1875.                             string surfName;
  1876.                             if (index >= 0 && index < (int) surfNames->size())
  1877.                                 surfName = surfNames->at(index);
  1878.                             appCore->getSimulation()->getActiveObserver()->setDisplayedSurface(surfName);
  1879.                             delete surfNames;
  1880.                         }
  1881.                     }
  1882.                 }
  1883.                 else if (LOWORD(wParam) >= ID_FIRST_SCRIPT &&
  1884.                          LOWORD(wParam) <  ID_FIRST_SCRIPT + ScriptMenuItems->size())
  1885.                 {
  1886.                     // Handle the script menu
  1887.                     unsigned int scriptIndex = LOWORD(wParam) - ID_FIRST_SCRIPT;
  1888.                     appCore->runScript((*ScriptMenuItems)[scriptIndex].filename);
  1889.                 }
  1890.             }
  1891.             break;
  1892.         }
  1893.         break;
  1894.     case WM_CLOSE:
  1895.         PostQuitMessage(0);
  1896.         break;
  1897.     case WM_SIZE:
  1898.         appCore->resize(LOWORD(lParam), HIWORD(lParam));
  1899.         break;
  1900.     case WM_PAINT:
  1901.         if (bReady)
  1902.         {
  1903.             appCore->draw();
  1904.             SwapBuffers(deviceContext);
  1905.             ValidateRect(hWnd, NULL);
  1906.         }
  1907.         break;
  1908.     default:
  1909.         return DefWindowProc( hWnd, uMsg, wParam, lParam );
  1910.     }
  1911.     return 0;
  1912. }