CelestiaContent/src/celestia/win32/wineclipses.cpp

505 lines
17 KiB
C++

// wineclipses.cpp by Kendrix <kendrix@wanadoo.fr>
// modified by Chris Laurel
//
// Compute Solar Eclipses for our Solar System planets
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
#include <string>
#include <sstream>
#include <algorithm>
#include <set>
#include <cassert>
#include <windows.h>
#include <commctrl.h>
#include "celestia/eclipsefinder.h"
#include "wineclipses.h"
#include "res/resource.h"
#include "celmath/geomutil.h"
#include "celutil/util.h"
#include "celutil/winutil.h"
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace Eigen;
using namespace std;
using namespace celmath;
WNDPROC oldListViewProc;
static vector<Eclipse> eclipseList;
extern void SetMouseCursor(LPCTSTR lpCursor);
char* MonthNames[12] =
{
"Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"
};
bool InitEclipseFinderColumns(HWND listView)
{
LVCOLUMN lvc;
LVCOLUMN columns[5];
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_CENTER;
lvc.pszText = "";
int nColumns = sizeof(columns) / sizeof(columns[0]);
int i;
for (i = 0; i < nColumns; i++)
columns[i] = lvc;
bind_textdomain_codeset("celestia", CurrentCP());
columns[0].pszText = _("Planet");
columns[0].cx = 65;
columns[1].pszText = _("Satellite");
columns[1].cx = 65;
columns[2].pszText = _("Date");
columns[2].cx = 80;
columns[3].pszText = _("Start");
columns[3].cx = 55;
columns[4].pszText = _("Duration");
columns[4].cx = 135;
bind_textdomain_codeset("celestia", "UTF8");
for (i = 0; i < nColumns; i++)
{
columns[i].iSubItem = i;
if (ListView_InsertColumn(listView, i, &columns[i]) == -1)
return false;
}
return true;
}
bool InitEclipseFinderItems(HWND listView, const vector<Eclipse>& eclipses)
{
LVITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
lvi.state = 0;
lvi.stateMask = 0;
lvi.pszText = LPSTR_TEXTCALLBACK;
for (unsigned int i = 0; i < eclipses.size(); i++)
{
lvi.iItem = i;
lvi.iSubItem = 0;
lvi.lParam = (LPARAM) &(eclipses[i]);
ListView_InsertItem(listView, &lvi);
}
return true;
}
static char callbackScratch[256];
void EclipseFinderDisplayItem(LPNMLVDISPINFOA nm)
{
Eclipse* eclipse = reinterpret_cast<Eclipse*>(nm->item.lParam);
if (eclipse == NULL)
{
nm->item.pszText = "";
return;
}
switch (nm->item.iSubItem)
{
case 0:
strncpy(callbackScratch, UTF8ToCurrentCP(_(eclipse->receiver->getName().c_str())).c_str(), sizeof(callbackScratch) - 1);
nm->item.pszText = callbackScratch;
break;
case 1:
strncpy(callbackScratch, UTF8ToCurrentCP(_(eclipse->occulter->getName().c_str())).c_str(), sizeof(callbackScratch) - 1);
nm->item.pszText = callbackScratch;
break;
case 2:
{
bind_textdomain_codeset("celestia", CurrentCP());
astro::Date startDate(eclipse->startTime);
sprintf(callbackScratch, "%2d %s %4d",
startDate.day,
_(MonthNames[startDate.month - 1]),
startDate.year);
nm->item.pszText = callbackScratch;
bind_textdomain_codeset("celestia", "UTF8");
}
break;
case 3:
{
astro::Date startDate(eclipse->startTime);
sprintf(callbackScratch, "%02d:%02d",
startDate.hour, startDate.minute);
nm->item.pszText = callbackScratch;
}
break;
case 4:
{
int minutes = (int) ((eclipse->endTime - eclipse->startTime) * 24 * 60);
sprintf(callbackScratch, "%02d:%02d", minutes / 60, minutes % 60);
nm->item.pszText = callbackScratch;
}
break;
}
}
void InitDateControls(HWND hDlg, const astro::Date& newTime, SYSTEMTIME& fromTime, SYSTEMTIME& toTime)
{
HWND dateItem = NULL;
fromTime.wYear = newTime.year - 1;
fromTime.wMonth = newTime.month;
fromTime.wDay = newTime.day;
fromTime.wDayOfWeek = ((int) ((double) newTime + 0.5) + 1) % 7;
fromTime.wHour = 0;
fromTime.wMinute = 0;
fromTime.wSecond = (int) 0;
fromTime.wMilliseconds = 0;
toTime = fromTime;
toTime.wYear += 2;
dateItem = GetDlgItem(hDlg, IDC_DATEFROM);
if (dateItem != NULL)
{
DateTime_SetFormat(dateItem, "dd' 'MMM' 'yyy");
DateTime_SetSystemtime(dateItem, GDT_VALID, &fromTime);
}
dateItem = GetDlgItem(hDlg, IDC_DATETO);
if (dateItem != NULL)
{
DateTime_SetFormat(dateItem, "dd' 'MMM' 'yyy");
DateTime_SetSystemtime(dateItem, GDT_VALID, &toTime);
}
}
struct EclipseFinderSortInfo
{
int subItem;
int32_t Year;
int8_t Month;
int8_t Day;
int8_t Hour;
int Type;
};
int CALLBACK EclipseFinderCompareFunc(LPARAM lParam0, LPARAM lParam1,
LPARAM lParamSort)
{
EclipseFinderSortInfo* sortInfo = reinterpret_cast<EclipseFinderSortInfo*>(lParamSort);
Eclipse* eclipse0 = reinterpret_cast<Eclipse*>(lParam0);
Eclipse* eclipse1 = reinterpret_cast<Eclipse*>(lParam1);
switch (sortInfo->subItem)
{
case 1:
if (sortInfo->Type == Eclipse::Solar)
return eclipse0->occulter->getName(true).compare(eclipse1->occulter->getName(true));
return eclipse0->receiver->getName(true).compare(eclipse1->receiver->getName(true));
case 4:
{
double duration0 = eclipse0->endTime - eclipse0->startTime;
double duration1 = eclipse1->endTime - eclipse1->startTime;
if (duration0 < duration1)
return -1;
else if (duration1 < duration0)
return 1;
else
return 0;
}
default:
if (eclipse0->startTime < eclipse1->startTime)
return -1;
else if (eclipse1->startTime < eclipse0->startTime)
return 1;
else
return 0;
}
}
BOOL APIENTRY EclipseListViewProc(HWND hWnd,
UINT message,
UINT wParam,
LONG lParam)
{
switch(message)
{
case WM_LBUTTONDBLCLK:
{
LVHITTESTINFO lvHit;
lvHit.pt.x = LOWORD(lParam);
lvHit.pt.y = HIWORD(lParam);
int listIndex = ListView_HitTest(hWnd, &lvHit);
if (listIndex >= 0)
{
SendMessage(GetParent(hWnd), WM_COMMAND, MAKEWPARAM(IDSETDATEANDGO, 0), NULL);
}
}
break;
}
return CallWindowProc(oldListViewProc, hWnd, message, wParam, lParam);
}
BOOL APIENTRY EclipseFinderProc(HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
//EclipseFinderDialog* eclipseFinderDlg = reinterpret_cast<EclipseFinderDialog*>(GetWindowLong(hDlg, DWL_USER));
EclipseFinderDialog* eclipseFinderDlg = reinterpret_cast<EclipseFinderDialog*>(GetWindowLongPtr(hDlg, DWLP_USER));
switch (message)
{
case WM_INITDIALOG:
{
EclipseFinderDialog* efd = reinterpret_cast<EclipseFinderDialog*>(lParam);
if (efd == NULL)
return EndDialog(hDlg, 0);
//SetWindowLong(hDlg, DWL_USER, lParam);
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
HWND hwnd = GetDlgItem(hDlg, IDC_ECLIPSES_LIST);
InitEclipseFinderColumns(hwnd);
SendDlgItemMessage(hDlg, IDC_ECLIPSES_LIST, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
bind_textdomain_codeset("celestia", CurrentCP());
CheckRadioButton(hDlg, IDC_SOLARECLIPSE, IDC_LUNARECLIPSE, IDC_SOLARECLIPSE);
efd->type = Eclipse::Solar;
SendDlgItemMessage(hDlg, IDC_ECLIPSETARGET, CB_ADDSTRING, 0, (LPARAM)_("Earth"));
SendDlgItemMessage(hDlg, IDC_ECLIPSETARGET, CB_ADDSTRING, 0, (LPARAM)_("Jupiter"));
SendDlgItemMessage(hDlg, IDC_ECLIPSETARGET, CB_ADDSTRING, 0, (LPARAM)_("Saturn"));
SendDlgItemMessage(hDlg, IDC_ECLIPSETARGET, CB_ADDSTRING, 0, (LPARAM)_("Uranus"));
SendDlgItemMessage(hDlg, IDC_ECLIPSETARGET, CB_ADDSTRING, 0, (LPARAM)_("Neptune"));
SendDlgItemMessage(hDlg, IDC_ECLIPSETARGET, CB_ADDSTRING, 0, (LPARAM)_("Pluto"));
SendDlgItemMessage(hDlg, IDC_ECLIPSETARGET, CB_SETCURSEL, 0, 0);
efd->strPlaneteToFindOn = "Earth";
bind_textdomain_codeset("celestia", "UTF8");
InitDateControls(hDlg, astro::Date(efd->appCore->getSimulation()->getTime()), efd->fromTime, efd->toTime);
// Subclass the ListView to intercept WM_LBUTTONUP messages
HWND hCtrl;
if (hCtrl = GetDlgItem(hDlg, IDC_ECLIPSES_LIST))
oldListViewProc = (WNDPROC)SetWindowLongPtr(hCtrl, GWLP_WNDPROC, (LPARAM)EclipseListViewProc);
//oldListViewProc = (WNDPROC) SetWindowLong(hCtrl, GWL_WNDPROC, (DWORD) EclipseListViewProc);
}
return(TRUE);
case WM_DESTROY:
if (eclipseFinderDlg != NULL && eclipseFinderDlg->parent != NULL)
{
SendMessage(eclipseFinderDlg->parent, WM_COMMAND, IDCLOSE,
reinterpret_cast<LPARAM>(eclipseFinderDlg));
}
break;
case WM_CLOSE:
DestroyWindow(hDlg);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCOMPUTE:
{
HWND hwnd = GetDlgItem(hDlg, IDC_ECLIPSES_LIST);
ListView_DeleteAllItems(hwnd);
if (eclipseFinderDlg->strPlaneteToFindOn.empty())
eclipseFinderDlg->strPlaneteToFindOn = "Earth";
SetMouseCursor(IDC_WAIT);
astro::Date from(eclipseFinderDlg->fromTime.wYear,
eclipseFinderDlg->fromTime.wMonth,
eclipseFinderDlg->fromTime.wDay);
astro::Date to(eclipseFinderDlg->toTime.wYear,
eclipseFinderDlg->toTime.wMonth,
eclipseFinderDlg->toTime.wDay);
const SolarSystem* sys = eclipseFinderDlg->appCore->getSimulation()->getNearestSolarSystem();
if (sys != nullptr && sys->getStar()->getCatalogNumber() == 0)
{
Body* planete = sys->getPlanets()->find(eclipseFinderDlg->strPlaneteToFindOn);
if (planete != nullptr)
{
EclipseFinder ef(planete);
ef.findEclipses((double)from, (double)to, eclipseFinderDlg->type, eclipseList);
}
}
InitEclipseFinderItems(hwnd, eclipseList);
SetMouseCursor(IDC_ARROW);
break;
}
case IDCLOSE:
{
if (eclipseFinderDlg != NULL && eclipseFinderDlg->parent != NULL)
{
SendMessage(eclipseFinderDlg->parent, WM_COMMAND, IDCLOSE,
reinterpret_cast<LPARAM>(eclipseFinderDlg));
}
EndDialog(hDlg, 0);
break;
}
case IDSETDATEANDGO:
if (eclipseFinderDlg->BodytoSet_)
{
Simulation* sim = eclipseFinderDlg->appCore->getSimulation();
sim->setTime(eclipseFinderDlg->TimetoSet_);
Selection target(eclipseFinderDlg->BodytoSet_);
Selection ref(eclipseFinderDlg->BodytoSet_->getSystem()->getStar());
// Use the phase lock coordinate system to set a position
// on the line between the sun and the body where the eclipse
// is occurring.
sim->setFrame(ObserverFrame::PhaseLock, target, ref);
sim->update(0.0);
double distance = target.radius() * 4.0;
sim->gotoLocation(UniversalCoord::Zero().offsetKm(Vector3d::UnitX() * distance),
YRotation(-PI / 2) * XRotation(-PI / 2),
2.5);
}
break;
case IDC_SOLARECLIPSE:
eclipseFinderDlg->type = Eclipse::Solar;
break;
case IDC_LUNARECLIPSE:
eclipseFinderDlg->type = Eclipse::Lunar;
break;
case IDC_ECLIPSETARGET:
if(HIWORD(wParam) == CBN_SELCHANGE)
{
switch(SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0))
{
case 0:
eclipseFinderDlg->strPlaneteToFindOn = "Earth";
break;
case 1:
eclipseFinderDlg->strPlaneteToFindOn = "Jupiter";
break;
case 2:
eclipseFinderDlg->strPlaneteToFindOn = "Saturn";
break;
case 3:
eclipseFinderDlg->strPlaneteToFindOn = "Uranus";
break;
case 4:
eclipseFinderDlg->strPlaneteToFindOn = "Neptune";
break;
case 5:
eclipseFinderDlg->strPlaneteToFindOn = "Pluto";
break;
}
}
}
return TRUE;
case WM_NOTIFY:
{
LPNMHDR hdr = (LPNMHDR) lParam;
if(hdr->idFrom == IDC_ECLIPSES_LIST)
{
switch(hdr->code)
{
case LVN_GETDISPINFO:
EclipseFinderDisplayItem((LPNMLVDISPINFOA) lParam);
break;
case LVN_ITEMCHANGED:
{
LPNMLISTVIEW nm = (LPNMLISTVIEW) lParam;
if ((nm->uNewState & LVIS_SELECTED) != 0)
{
Eclipse* eclipse = reinterpret_cast<Eclipse*>(nm->lParam);
if (eclipse != NULL)
{
eclipseFinderDlg->TimetoSet_ =
(eclipse->startTime + eclipse->endTime) / 2.0f;
eclipseFinderDlg->BodytoSet_ =
eclipseFinderDlg->type == Eclipse::Solar ?
eclipse->receiver :
eclipse->occulter;
}
}
break;
}
case LVN_COLUMNCLICK:
{
HWND hwnd = GetDlgItem(hDlg, IDC_ECLIPSES_LIST);
if (hwnd != 0)
{
LPNMLISTVIEW nm = (LPNMLISTVIEW) lParam;
EclipseFinderSortInfo sortInfo;
sortInfo.subItem = nm->iSubItem;
sortInfo.Type = eclipseFinderDlg->type;
// sortInfo.sattelite = ;
// sortInfo.pos = eclipseFinderDlg->pos;
ListView_SortItems(hwnd, EclipseFinderCompareFunc,
reinterpret_cast<LPARAM>(&sortInfo));
}
}
}
}
if (hdr->code == DTN_DATETIMECHANGE)
{
LPNMDATETIMECHANGE change = (LPNMDATETIMECHANGE) lParam;
if (change->dwFlags == GDT_VALID)
{
if (wParam == IDC_DATEFROM)
{
eclipseFinderDlg->fromTime.wYear = change->st.wYear;
eclipseFinderDlg->fromTime.wMonth = change->st.wMonth;
eclipseFinderDlg->fromTime.wDay = change->st.wDay;
}
else if (wParam == IDC_DATETO)
{
eclipseFinderDlg->toTime.wYear = change->st.wYear;
eclipseFinderDlg->toTime.wMonth = change->st.wMonth;
eclipseFinderDlg->toTime.wDay = change->st.wDay;
}
}
}
}
break;
}
return FALSE;
}
EclipseFinderDialog::EclipseFinderDialog(HINSTANCE appInstance,
HWND _parent,
CelestiaCore* _appCore) :
appCore(_appCore),
parent(_parent),
BodytoSet_(NULL)
{
hwnd = CreateDialogParam(appInstance,
MAKEINTRESOURCE(IDD_ECLIPSEFINDER),
parent,
(DLGPROC)EclipseFinderProc,
reinterpret_cast<LPARAM>(this));
}