"Fossies" - the Fresh Open Source Software Archive

Member "Celestia-1.6.2.2/src/celestia/winmain.cpp" (12 Jan 2021, 143623 Bytes) of package /linux/misc/Celestia-1.6.2.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "winmain.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.6.2.1_vs_1.6.2.2.

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