1 /*
2 * frame_events.cpp
3 * PHD Guiding
4 *
5 * Created by Craig Stark.
6 * Copyright (c) 2006-2010 Craig Stark.
7 * All rights reserved.
8 *
9 * This source code is distributed under the following "BSD" license
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * Neither the name of Craig Stark, Stark Labs nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 #include "phd.h"
36
37 #include "about_dialog.h"
38 #include "aui_controls.h"
39 #include "camcal_import_dialog.h"
40 #include "darks_dialog.h"
41 #include "image_math.h"
42 #include "log_uploader.h"
43 #include "pierflip_tool.h"
44 #include "Refine_DefMap.h"
45 #include "starcross_test.h"
46
47 #include <algorithm>
48 #include <memory>
49
50 #include <wx/spinctrl.h>
51 #include <wx/stdpaths.h>
52 #include <wx/textdlg.h>
53 #include <wx/textfile.h>
54 #include <wx/txtstrm.h>
55 #include <wx/utils.h>
56 #include <wx/wfstream.h>
57
58 wxDEFINE_EVENT(APPSTATE_NOTIFY_EVENT, wxCommandEvent);
59
OnExposureDurationSelected(wxCommandEvent & evt)60 void MyFrame::OnExposureDurationSelected(wxCommandEvent& evt)
61 {
62 unsigned int idx = Dur_Choice->GetSelection();
63
64 if (idx == 0)
65 {
66 // Auto-exposure
67 if (!m_autoExp.enabled)
68 {
69 Debug.AddLine("AutoExp: enabled");
70 m_autoExp.enabled = true;
71 NotifyExposureChanged();
72 }
73 }
74 else if (idx == Dur_Choice->GetCount() - 1)
75 {
76 // edit custom
77
78 int customVal = *(GetExposureDurations().end() - 1);
79 wxTextEntryDialog dlg(this, _("Custom exposure duration (seconds)"), _("Edit custom exposure"),
80 wxString::Format("%g", (double) customVal / 1000.), wxOK | wxCANCEL);
81 if (dlg.ShowModal() == wxID_OK)
82 {
83 double val;
84 if (dlg.GetValue().ToDouble(&val) && val > 0.0 && val < 3600.)
85 {
86 int ms = (int)(val * 1000.0);
87 SetCustomExposureDuration(ms);
88 }
89 }
90
91 // restore the actual selection
92 if (m_autoExp.enabled)
93 {
94 Dur_Choice->SetSelection(0);
95 }
96 else
97 {
98 const std::vector<int>& dur(GetExposureDurations());
99 auto pos = std::find(dur.begin(), dur.end(), m_exposureDuration);
100 if (pos == dur.end())
101 pos = std::find(dur.begin(), dur.end(), 1000); // safe fall-back, should not happen
102 Dur_Choice->SetSelection(pos - dur.begin() + 1); // skip Auto
103 }
104 }
105 else
106 {
107 // ordinary selection
108
109 const std::vector<int>& dur(GetExposureDurations());
110 int duration = dur[idx - 1];
111
112 if (m_autoExp.enabled || m_exposureDuration != duration)
113 {
114 Debug.Write(wxString::Format("OnExposureDurationSelected: duration = %d\n", duration));
115
116 m_exposureDuration = duration;
117 m_autoExp.enabled = false;
118
119 NotifyExposureChanged();
120
121 if (pCamera)
122 {
123 // select the best matching dark frame
124 pCamera->SelectDark(m_exposureDuration);
125 }
126 }
127 }
128 }
129
NotifyExposureChanged()130 void MyFrame::NotifyExposureChanged()
131 {
132 NotifyGuidingParam("Exposure", ExposureDurationSummary());
133 pConfig->Profile.SetInt("/ExposureDurationMs", m_autoExp.enabled ? -1 : m_exposureDuration);
134 }
135
RequestedExposureDuration()136 int MyFrame::RequestedExposureDuration()
137 {
138 if (!pCamera || !pCamera->Connected)
139 return 0;
140
141 return m_singleExposure.enabled ? m_singleExposure.duration : m_exposureDuration;
142 }
143
OnMenuHighlight(wxMenuEvent & evt)144 void MyFrame::OnMenuHighlight(wxMenuEvent& evt)
145 {
146 wxMenuItem *mi = pFrame->GetMenuBar()->FindItem(evt.GetMenuId());
147 if (mi)
148 {
149 const wxString& help = mi->GetHelp();
150 m_statusbar->OverlayMsg(help);
151 }
152 else
153 {
154 m_statusbar->ClearOverlayMsg();
155 }
156 }
157
OnAnyMenu(wxCommandEvent & evt)158 void MyFrame::OnAnyMenu(wxCommandEvent& evt)
159 {
160 m_statusbar->ClearOverlayMsg();
161 evt.Skip();
162 }
163
OnAnyMenuClose(wxMenuEvent & evt)164 void MyFrame::OnAnyMenuClose(wxMenuEvent& evt)
165 {
166 m_statusbar->ClearOverlayMsg();
167 evt.Skip();
168 }
169
OnQuit(wxCommandEvent & WXUNUSED (event))170 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
171 {
172 Close(false);
173 }
174
OnInstructions(wxCommandEvent & WXUNUSED (event))175 void MyFrame::OnInstructions(wxCommandEvent& WXUNUSED(event))
176 {
177 wxMessageBox(wxString::Format(_("Welcome to PHD2 (Push Here Dummy, Gen2) Guiding\n\n \
178 Basic operation is quite simple (hence the 'PHD')\n\n \
179 1) Press the green 'USB' button, select your camera and mount, click on 'Connect All'\n \
180 2) Pick an exposure duration from the drop-down list. Try 2 seconds to start.\n \
181 3) Hit the 'Loop' button, adjust your focus if necessary\n \
182 4) Click on a star away from the edge or use Alt-S to auto-select a star\n \
183 5) Press the guide (green target) icon\n\n \
184 PHD2 will then calibrate itself and begin guiding. That's it!\n\n \
185 To stop guiding, simply press the 'Loop' or 'Stop' buttons. If you need to \n \
186 tweak any options, click on the 'Brain' button to bring up the 'Advanced' \n \
187 panel. Use the 'View' menu to watch your guiding performance. If you have\n \
188 problems, read the 'Best Practices' document and the help files! ")),_("Instructions"));
189
190 }
191
OnHelp(wxCommandEvent & WXUNUSED (event))192 void MyFrame::OnHelp(wxCommandEvent& WXUNUSED(event))
193 {
194 help->Display(_("Introduction"));
195 }
196
OnAbout(wxCommandEvent & WXUNUSED (event))197 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
198 {
199 AboutDialog dlg;
200 dlg.ShowModal();
201 }
202
OnHelpOnline(wxCommandEvent & evt)203 void MyFrame::OnHelpOnline(wxCommandEvent& evt)
204 {
205 wxLaunchDefaultBrowser("https://openphdguiding.org/getting-help/");
206 }
207
_shell_open(const wxString & loc)208 static void _shell_open(const wxString& loc)
209 {
210 #if defined(__WXMSW__)
211 ::ShellExecute(NULL, _T("open"), loc.fn_str(), NULL, NULL, SW_SHOWNORMAL);
212 #elif defined(__WXOSX__)
213 ::wxExecute("/usr/bin/open '" + loc + "'", wxEXEC_ASYNC);
214 #else
215 ::wxExecute("xdg-open '" + loc + "'", wxEXEC_ASYNC);
216 #endif
217 }
218
OnHelpLogFolder(wxCommandEvent & evt)219 void MyFrame::OnHelpLogFolder(wxCommandEvent& evt)
220 {
221 _shell_open(Debug.GetLogDir());
222 }
223
OnHelpUploadLogs(wxCommandEvent & evt)224 void MyFrame::OnHelpUploadLogs(wxCommandEvent& evt)
225 {
226 LogUploader::UploadLogs();
227 }
228
OnOverlay(wxCommandEvent & evt)229 void MyFrame::OnOverlay(wxCommandEvent& evt)
230 {
231 pGuider->SetOverlayMode(evt.GetId() - MENU_XHAIR0);
232 }
233
234 struct SlitPropertiesDlg : public wxDialog
235 {
236 wxSpinCtrl *m_x;
237 wxSpinCtrl *m_y;
238 wxSpinCtrl *m_width;
239 wxSpinCtrl *m_height;
240 wxSpinCtrl *m_angle;
241
242 SlitPropertiesDlg(wxWindow *parent, wxWindowID id = wxID_ANY, const wxString& title = _("Spectrograph Slit Overlay"),
243 const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(390, 181), long style = wxDEFAULT_DIALOG_STYLE);
244 };
245
SlitPropertiesDlg(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,long style)246 SlitPropertiesDlg::SlitPropertiesDlg(wxWindow *parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style)
247 : wxDialog(parent, id, title, pos, size, style)
248 {
249 wxSize textSz = pFrame->GetTextExtent("888888");
250 wxBoxSizer *vSizer = new wxBoxSizer(wxVERTICAL);
251 wxBoxSizer *hSizer = new wxBoxSizer(wxHORIZONTAL);
252 wxStaticBoxSizer *szPosition = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Position (Center)")), wxVERTICAL);
253 wxStaticBoxSizer *szSlitSize = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Size")), wxVERTICAL);
254 // Position controls
255 wxBoxSizer *hXSizer = new wxBoxSizer(wxHORIZONTAL);
256 wxStaticText *xLabel = new wxStaticText(this, wxID_ANY, _("X"), wxDefaultPosition, wxDefaultSize, 0);
257 hXSizer->Add(xLabel, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
258 m_x = pFrame->MakeSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, textSz, wxSP_ARROW_KEYS, 0, 8000, 0);
259 hXSizer->Add(m_x, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
260 szPosition->Add(hXSizer, 0, wxEXPAND, 5);
261
262 wxBoxSizer *hYSizer = new wxBoxSizer(wxHORIZONTAL);
263 wxStaticText* yLabel = new wxStaticText(this, wxID_ANY, _("Y"), wxDefaultPosition, wxDefaultSize, 0);
264 hYSizer->Add(yLabel, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
265 m_y = pFrame->MakeSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, textSz, wxSP_ARROW_KEYS, 0, 8000, 0);
266 hYSizer->Add(m_y, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
267 szPosition->Add(hYSizer, 1, wxEXPAND, 5);
268 hSizer->Add(szPosition, 0, 0, 5);
269
270 // Size controls
271 wxBoxSizer *hWidthSizer = new wxBoxSizer(wxHORIZONTAL);
272 wxStaticText *widthLabel = new wxStaticText(this, wxID_ANY, _("Width"), wxDefaultPosition, wxDefaultSize, 0);
273 hWidthSizer->Add(widthLabel, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
274 m_width = pFrame->MakeSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, textSz, wxSP_ARROW_KEYS, 2, 1000, 2);
275 hWidthSizer->Add(m_width, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
276 szSlitSize->Add(hWidthSizer, 1, wxEXPAND, 5);
277
278 wxBoxSizer *hHeightSizer = new wxBoxSizer(wxHORIZONTAL);
279 wxStaticText *heightLabel = new wxStaticText(this, wxID_ANY, _("Height"), wxDefaultPosition, wxDefaultSize, 0);
280 hHeightSizer->Add(heightLabel, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
281 m_height = pFrame->MakeSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, textSz, wxSP_ARROW_KEYS, 2, 1000, 2);
282 hHeightSizer->Add(m_height, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
283 szSlitSize->Add(hHeightSizer, 1, wxEXPAND, 5);
284 hSizer->Add(szSlitSize, 0, 0, 5);
285
286 int ww = StringWidth(this, widthLabel->GetLabel());
287 int wh = StringWidth(this, heightLabel->GetLabel());
288 wxSize sz(wxMax(ww, wh), -1);
289 widthLabel->SetMinSize(sz);
290 heightLabel->SetMinSize(sz);
291
292 vSizer->Add(hSizer, 0, wxEXPAND, 5);
293 // Angle controls
294 wxBoxSizer* hAngleSizer = new wxBoxSizer(wxHORIZONTAL);
295 wxStaticText* staticText1 = new wxStaticText(this, wxID_ANY, _("Angle (degrees)"), wxDefaultPosition, wxDefaultSize, 0);
296 //staticText1->Wrap(-1);
297 hAngleSizer->Add(staticText1, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
298 m_angle = pFrame->MakeSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, textSz, wxSP_ARROW_KEYS, -90, 90, 0);
299 hAngleSizer->Add(m_angle, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
300
301 vSizer->Add(hAngleSizer, 0, wxEXPAND, 5);
302
303 // ok/cancel buttons
304 vSizer->Add(
305 CreateButtonSizer(wxOK | wxCANCEL),
306 wxSizerFlags(0).Expand().Border(wxALL, 10));
307
308 SetSizerAndFit(vSizer);
309 }
310
311 struct SlitPosCtx : public wxObject
312 {
313 SlitPropertiesDlg *dlg;
314 Guider *guider;
SlitPosCtxSlitPosCtx315 SlitPosCtx(SlitPropertiesDlg *d, Guider *g) : dlg(d), guider(g) { }
316 };
317
UpdateSlitPos(wxSpinEvent & event)318 static void UpdateSlitPos(wxSpinEvent& event)
319 {
320 SlitPosCtx *ctx = static_cast<SlitPosCtx *>(event.GetEventUserData());
321 wxPoint center(ctx->dlg->m_x->GetValue(), ctx->dlg->m_y->GetValue());
322 wxSize size(ctx->dlg->m_width->GetValue(), ctx->dlg->m_height->GetValue());
323 int angle = ctx->dlg->m_angle->GetValue();
324 ctx->guider->SetOverlaySlitCoords(center, size, angle);
325 }
326
OnOverlaySlitCoords(wxCommandEvent & evt)327 void MyFrame::OnOverlaySlitCoords(wxCommandEvent& evt)
328 {
329 wxPoint center;
330 wxSize size;
331 int angle;
332 pGuider->GetOverlaySlitCoords(¢er, &size, &angle);
333
334 SlitPropertiesDlg dlg(this);
335
336 dlg.m_x->SetValue(center.x);
337 dlg.m_y->SetValue(center.y);
338 dlg.m_width->SetValue(size.GetWidth());
339 dlg.m_height->SetValue(size.GetHeight());
340 dlg.m_angle->SetValue(angle);
341
342 dlg.Bind(wxEVT_SPINCTRL, &UpdateSlitPos, wxID_ANY, wxID_ANY, new SlitPosCtx(&dlg, pGuider));
343
344 if (dlg.ShowModal() != wxID_OK)
345 {
346 // revert to original values
347 pGuider->SetOverlaySlitCoords(center, size, angle);
348 }
349 }
350
OnSave(wxCommandEvent & WXUNUSED (event))351 void MyFrame::OnSave(wxCommandEvent& WXUNUSED(event))
352 {
353 if (!pGuider->CurrentImage()->ImageData)
354 return;
355
356 wxString fname = wxFileSelector( _("Save FITS Image"), (const wxChar *)NULL,
357 (const wxChar *)NULL,
358 wxT("fit"), wxT("FITS files (*.fit)|*.fit"),wxFD_SAVE | wxFD_OVERWRITE_PROMPT,
359 this);
360
361 if (fname.IsEmpty())
362 return; // Check for canceled dialog
363
364 if (pGuider->SaveCurrentImage(fname))
365 {
366 Alert(wxString::Format(_("The image could not be saved to %s"), fname));
367 }
368 else
369 {
370 pFrame->StatusMsg(wxString::Format(_("%s saved"), wxFileName(fname).GetFullName()));
371 }
372 }
373
OnIdle(wxIdleEvent & WXUNUSED (event))374 void MyFrame::OnIdle(wxIdleEvent& WXUNUSED(event))
375 {
376 }
377
StartLoopingInteractive(const wxString & context)378 void MyFrame::StartLoopingInteractive(const wxString& context)
379 {
380 Debug.Write(wxString::Format("StartLoopingInteractive: %s\n", context));
381
382 if (!pCamera || !pCamera->Connected)
383 {
384 Debug.Write(_T("Camera not connected\n"));
385 wxMessageBox(_("Please connect to a camera first"), _("Info"));
386 return;
387 }
388
389 if (CaptureActive && !pGuider->IsCalibratingOrGuiding())
390 {
391 Debug.Write(_T("cannot start looping when capture active\n"));
392 return;
393 }
394
395 StartLooping();
396 }
397
OnButtonLoop(wxCommandEvent & WXUNUSED (event))398 void MyFrame::OnButtonLoop(wxCommandEvent& WXUNUSED(event))
399 {
400 StartLoopingInteractive(_T("Loop button clicked"));
401 }
402
FinishStop(void)403 void MyFrame::FinishStop(void)
404 {
405 assert(!CaptureActive);
406 m_singleExposure.enabled = false;
407 EvtServer.NotifyLoopingStopped();
408 // when looping resumes, start with at least one full frame. This enables applications
409 // controlling PHD to auto-select a new star if the star is lost while looping was stopped.
410 pGuider->ForceFullFrame();
411 ResetAutoExposure();
412 UpdateButtonsStatus();
413 StatusMsg(_("Stopped."));
414 PhdController::AbortController("Stopped capturing");
415 }
416
RawModeWarningKey(void)417 static wxString RawModeWarningKey(void)
418 {
419 return wxString::Format("/Confirm/%d/RawModeWarningEnabled", pConfig->GetCurrentProfileId());
420 }
421
SuppressRawModeWarning(long)422 static void SuppressRawModeWarning(long)
423 {
424 pConfig->Global.SetBoolean(RawModeWarningKey(), false);
425 }
426
WarnRawImageMode(void)427 static void WarnRawImageMode(void)
428 {
429 if (pCamera->FullSize != pCamera->DarkFrameSize())
430 {
431 pFrame->SuppressableAlert(RawModeWarningKey(), _("For refining the Bad-pixel Map PHD2 is now displaying raw camera data frames, which are a different size from ordinary guide frames for this camera."),
432 SuppressRawModeWarning, 0);
433 }
434 }
435
436 /*
437 * OnExposeComplete is the dispatch routine that is called when an image has been taken
438 * by the background thread.
439 *
440 * It:
441 * - causes the image to be redrawn by calling pGuider->UpateImageDisplay()
442 * - calls the routine to update the guider state (which may do nothing)
443 * - calls any other appropriate state update routine depending upon the current state
444 * - updates button state based on appropriate state variables
445 * - schedules another exposure if CaptureActive is stil true
446 *
447 */
OnExposeComplete(usImage * pNewFrame,bool err)448 void MyFrame::OnExposeComplete(usImage *pNewFrame, bool err)
449 {
450 try
451 {
452 Debug.Write("OnExposeComplete: enter\n");
453
454 m_exposurePending = false;
455
456 if (pGuider->GetPauseType() == PAUSE_FULL)
457 {
458 delete pNewFrame;
459 Debug.Write("guider is paused, ignoring frame, not scheduling exposure\n");
460 return;
461 }
462
463 if (err)
464 {
465 Debug.Write("OnExposeComplete: Capture Error reported\n");
466
467 delete pNewFrame;
468
469 bool stopping = !m_continueCapturing;
470 StopCapturing();
471 if (pGuider->IsCalibratingOrGuiding())
472 {
473 pGuider->StopGuiding();
474 pGuider->UpdateImageDisplay();
475 }
476 m_singleExposure.enabled = false;
477 EvtServer.NotifyLoopingStopped();
478 pGuider->Reset(false);
479 CaptureActive = m_continueCapturing;
480 UpdateButtonsStatus();
481 PhdController::AbortController(stopping ? "Image capture stopped" : "Error reported capturing image");
482 StatusMsg(_("Stopped."));
483
484 // some camera drivers disconnect the camera on error
485 if (!pCamera->Connected)
486 m_statusbar->UpdateStates();
487
488 throw ERROR_INFO("Error reported capturing image");
489 }
490
491 pNewFrame->FrameNum = ++m_frameCounter;
492
493 if (m_rawImageMode && !m_rawImageModeWarningDone)
494 {
495 WarnRawImageMode();
496 m_rawImageModeWarningDone = true;
497 }
498
499 // check for dark frame compatibility in case the frame size changed (binning changed)
500 if (pCamera->DarkFrameSize() != m_prevDarkFrameSize)
501 {
502 CheckDarkFrameGeometry();
503 }
504
505 pGuider->UpdateGuideState(pNewFrame, !m_continueCapturing);
506 pNewFrame = NULL; // the guider owns it now
507
508 PhdController::UpdateControllerState();
509
510 Debug.Write(wxString::Format("OnExposeComplete: CaptureActive=%d m_continueCapturing=%d\n",
511 CaptureActive, m_continueCapturing));
512
513 CaptureActive = m_continueCapturing;
514
515 if (CaptureActive)
516 {
517 ScheduleExposure();
518 }
519 else
520 {
521 FinishStop();
522 }
523 }
524 catch (const wxString& Msg)
525 {
526 POSSIBLY_UNUSED(Msg);
527 UpdateButtonsStatus();
528 }
529 }
530
OnExposeComplete(wxThreadEvent & event)531 void MyFrame::OnExposeComplete(wxThreadEvent& event)
532 {
533 usImage *image = event.GetPayload<usImage *>();
534 bool err = event.GetInt() != 0;
535 OnExposeComplete(image, err);
536 }
537
OnMoveComplete(wxThreadEvent & event_)538 void MyFrame::OnMoveComplete(wxThreadEvent& event_)
539 {
540 try
541 {
542 MoveCompleteEvent& event = static_cast<MoveCompleteEvent&>(event_);
543
544 if (event.moveOptions & MOVEOPT_MANUAL)
545 {
546 Debug.Write(wxString::Format("Manual Move completed, result = %d\n", event.result));
547 ClearStatusBarGuiderInfo();
548 return;
549 }
550
551 Mount *mount = event.mount;
552 assert(mount->IsBusy());
553 mount->DecrementRequestCount();
554
555 Mount::MOVE_RESULT moveResult = event.result;
556
557 mount->LogGuideStepInfo();
558
559 // deliver the outstanding GuidingStopped notification if this is a late-arriving
560 // move completion event
561 if (!pGuider->IsCalibratingOrGuiding() &&
562 (!pMount || !pMount->IsBusy()) &&
563 (!pSecondaryMount || !pSecondaryMount->IsBusy()))
564 {
565 pFrame->NotifyGuidingStopped();
566 }
567
568 if (moveResult != Mount::MOVE_OK)
569 {
570 mount->IncrementErrorCount();
571
572 if (moveResult == Mount::MOVE_ERROR_SLEWING)
573 {
574 Debug.Write("mount move error indicates guiding should stop\n");
575 pGuider->StopGuiding();
576 }
577 else if (moveResult == Mount::MOVE_ERROR_AO_LIMIT_REACHED)
578 {
579 const StepInfo& step = TheAO()->GetFailedStepInfo();
580 int newval = step.dx ? abs(step.x + step.dx) : abs(step.y + step.dy);
581
582 if (pGuider->IsCalibrating())
583 {
584 pFrame->Alert(wxString::Format(_("The AO exceeded its travel limit stepping"
585 " from (%d, %d) to (%d, %d) and calibration cannot proceed. Try reducing"
586 " the AO Travel setting to %d or lower and try again."),
587 step.x, step.y, step.x + step.dx, step.y + step.dy, newval));
588
589 pGuider->StopGuiding();
590 }
591 else // Guiding
592 {
593 pFrame->Alert(wxString::Format(_("The AO exceeded its travel limit stepping"
594 " from (%d, %d) to (%d, %d) and has been re-centered. Try reducing the AO"
595 " Travel setting to %d or lower."),
596 step.x, step.y, step.x + step.dx, step.y + step.dy, newval));
597
598 // Attempt to restart guiding after centering, otherwise the AO will
599 // just bounce right back to the failing position
600 bool saveSticky = pGuider->LockPosIsSticky();
601 pGuider->SetLockPosIsSticky(false);
602 pGuider->StopGuiding();
603 pGuider->StartGuiding();
604 pGuider->SetLockPosIsSticky(saveSticky);
605 }
606 }
607
608 throw ERROR_INFO("Error reported moving");
609 }
610 }
611 catch (const wxString& Msg)
612 {
613 POSSIBLY_UNUSED(Msg);
614 }
615 }
616
OnButtonStop(wxCommandEvent & WXUNUSED (event))617 void MyFrame::OnButtonStop(wxCommandEvent& WXUNUSED(event))
618 {
619 Debug.Write("Stop button clicked\n");
620 StopCapturing();
621 }
622
OnButtonAutoStar(wxCommandEvent & WXUNUSED (event))623 void MyFrame::OnButtonAutoStar(wxCommandEvent& WXUNUSED(event))
624 {
625 if (!wxGetKeyState(WXK_SHIFT))
626 AutoSelectStar();
627 else
628 pGuider->InvalidateCurrentPosition(true);
629 }
630
OnGammaSlider(wxScrollEvent & WXUNUSED (event))631 void MyFrame::OnGammaSlider(wxScrollEvent& WXUNUSED(event))
632 {
633 int val = Gamma_Slider->GetValue();
634 pConfig->Profile.SetInt("/Gamma", val);
635 Stretch_gamma = (double) val / 100.0;
636 pGuider->UpdateImageDisplay();
637 }
638
OnDark(wxCommandEvent & WXUNUSED (event))639 void MyFrame::OnDark(wxCommandEvent& WXUNUSED(event))
640 {
641 if (!pCamera || !pCamera->Connected)
642 {
643 wxMessageBox(_("Please connect to a camera first"), _("Info"));
644 return;
645 }
646
647 DarksDialog dlg(this, true);
648 dlg.ShowModal();
649
650 pCamera->SelectDark(RequestedExposureDuration()); // Might be req'd if user cancelled in midstream
651 }
652
653 // Outside event handler because loading a dark library will automatically unload a defect map
LoadDarkHandler(bool checkIt)654 bool MyFrame::LoadDarkHandler(bool checkIt)
655 {
656 if (!pCamera || !pCamera->Connected)
657 {
658 Alert(_("You must connect a camera before loading a dark library"));
659 m_useDarksMenuItem->Check(false);
660 return false;
661 }
662 pConfig->Profile.SetBoolean("/camera/AutoLoadDarks", checkIt);
663 if (checkIt) // enable it
664 {
665 m_useDarksMenuItem->Check(true);
666 if (pCamera->CurrentDefectMap)
667 LoadDefectMapHandler(false);
668 if (LoadDarkLibrary())
669 return true;
670 else
671 {
672 m_useDarksMenuItem->Check(false);
673 return false;
674 }
675 }
676 else
677 {
678 if (!pCamera->CurrentDarkFrame)
679 {
680 m_useDarksMenuItem->Check(false); // shouldn't have gotten here
681 return false;
682 }
683 pCamera->ClearDarks();
684 m_useDarksMenuItem->Check(false);
685 StatusMsg(_("Dark library unloaded"));
686 return true;
687 }
688 }
689
OnLoadDark(wxCommandEvent & evt)690 void MyFrame::OnLoadDark(wxCommandEvent& evt)
691 {
692 LoadDarkHandler(evt.IsChecked());
693 pFrame->UpdateStatusBarStateLabels();
694 }
695
696 // Outside event handler because loading a defect map will automatically unload a dark library
LoadDefectMapHandler(bool checkIt)697 void MyFrame::LoadDefectMapHandler(bool checkIt)
698 {
699 if (!pCamera || !pCamera->Connected)
700 {
701 Alert(_("You must connect a camera before loading a bad-pixel map"));
702 darks_menu->FindItem(MENU_LOADDEFECTMAP)->Check(false);
703 return;
704 }
705 pConfig->Profile.SetBoolean("/camera/AutoLoadDefectMap", checkIt);
706 if (checkIt)
707 {
708 DefectMap *defectMap = DefectMap::LoadDefectMap(pConfig->GetCurrentProfileId());
709 if (defectMap)
710 {
711 if (pCamera->CurrentDarkFrame)
712 LoadDarkHandler(false);
713 pCamera->SetDefectMap(defectMap);
714 m_useDarksMenuItem->Check(false);
715 m_useDefectMapMenuItem->Check(true);
716 StatusMsg(_("Defect map loaded"));
717 }
718 else
719 {
720 StatusMsg(_("Defect map not loaded"));
721 }
722 }
723 else
724 {
725 if (!pCamera->CurrentDefectMap)
726 {
727 m_useDefectMapMenuItem->Check(false); // Shouldn't have gotten here
728 return;
729 }
730 pCamera->ClearDefectMap();
731 m_useDefectMapMenuItem->Check(false);
732 StatusMsg(_("Bad-pixel map unloaded"));
733 }
734 }
735
OnLoadDefectMap(wxCommandEvent & evt)736 void MyFrame::OnLoadDefectMap(wxCommandEvent& evt)
737 {
738 LoadDefectMapHandler(evt.IsChecked());
739 pFrame->UpdateStatusBarStateLabels();
740 }
741
OnRefineDefMap(wxCommandEvent & evt)742 void MyFrame::OnRefineDefMap(wxCommandEvent& evt)
743 {
744 if (!pCamera || !pCamera->Connected)
745 {
746 wxMessageBox(_("Please connect to a camera first"), _("Info"));
747 return;
748 }
749
750 if (!pRefineDefMap)
751 pRefineDefMap = new RefineDefMap(this);
752
753 if (pRefineDefMap->InitUI()) // Required data present, UI built and ready to go
754 {
755 pRefineDefMap->Show();
756
757 // Don't let the user build a new defect map while we're trying to refine one; and it almost certainly makes sense
758 // to have a defect map loaded if the user wants to refine it
759 m_takeDarksMenuItem->Enable(false); // Dialog restores it when its window is closed
760 LoadDefectMapHandler(true);
761 }
762 else
763 pRefineDefMap->Destroy(); // user cancelled out before starting the process
764 }
765
OnImportCamCal(wxCommandEvent & evt)766 void MyFrame::OnImportCamCal(wxCommandEvent& evt)
767 {
768 if (!pCamera)
769 {
770 wxMessageBox(_("Please connect a camera first."));
771 return;
772 }
773
774 CamCalImportDialog dlg(this);
775
776 dlg.ShowModal();
777 }
778
OnToolBar(wxCommandEvent & evt)779 void MyFrame::OnToolBar(wxCommandEvent& evt)
780 {
781 if (evt.IsChecked())
782 {
783 //wxSize toolBarSize = MainToolbar->GetSize();
784 m_mgr.GetPane(_T("MainToolBar")).Show().Bottom()/*.MinSize(toolBarSize)*/;
785 }
786 else
787 {
788 m_mgr.GetPane(_T("MainToolBar")).Hide();
789 }
790 m_mgr.Update();
791 }
792
OnGraph(wxCommandEvent & evt)793 void MyFrame::OnGraph(wxCommandEvent& evt)
794 {
795 if (evt.IsChecked())
796 {
797 m_mgr.GetPane(_T("GraphLog")).Show().Bottom().Position(0).MinSize(-1, 240);
798 }
799 else
800 {
801 m_mgr.GetPane(_T("GraphLog")).Hide();
802 }
803 pGraphLog->SetState(evt.IsChecked());
804 m_mgr.Update();
805 }
806
OnStats(wxCommandEvent & evt)807 void MyFrame::OnStats(wxCommandEvent& evt)
808 {
809 if (evt.IsChecked())
810 {
811 m_mgr.GetPane(_T("Stats")).Show().Bottom().Position(0).MinSize(-1, 240);
812 }
813 else
814 {
815 m_mgr.GetPane(_T("Stats")).Hide();
816 }
817 pStatsWin->SetState(evt.IsChecked());
818 m_mgr.Update();
819 }
820
OnAoGraph(wxCommandEvent & evt)821 void MyFrame::OnAoGraph(wxCommandEvent& evt)
822 {
823 if (pStepGuiderGraph->SetState(evt.IsChecked()))
824 {
825 m_mgr.GetPane(_T("AOPosition")).Show().Right().Position(1).MinSize(293,208);
826 }
827 else
828 {
829 m_mgr.GetPane(_T("AOPosition")).Hide();
830 }
831 m_mgr.Update();
832 }
833
OnStarProfile(wxCommandEvent & evt)834 void MyFrame::OnStarProfile(wxCommandEvent& evt)
835 {
836 if (evt.IsChecked())
837 {
838 #if defined (__APPLE__)
839 m_mgr.GetPane(_T("Profile")).Show().Float().MinSize(110,72);
840 #else
841 m_mgr.GetPane(_T("Profile")).Show().Right().Position(0).MinSize(115,85);
842 //m_mgr.GetPane(_T("Profile")).Show().Bottom().Layer(1).Position(2).MinSize(115,85);
843 #endif
844 }
845 else
846 {
847 m_mgr.GetPane(_T("Profile")).Hide();
848 }
849 pProfile->SetState(evt.IsChecked());
850 m_mgr.Update();
851 }
852
OnTarget(wxCommandEvent & evt)853 void MyFrame::OnTarget(wxCommandEvent& evt)
854 {
855 if (evt.IsChecked())
856 {
857 m_mgr.GetPane(_T("Target")).Show().Right().Position(2).MinSize(293,208);
858 }
859 else
860 {
861 m_mgr.GetPane(_T("Target")).Hide();
862 }
863 pTarget->SetState(evt.IsChecked());
864 m_mgr.Update();
865 }
866
867 // Redock windows and restore main window to size/position where everything should be readily accessible
OnRestoreWindows(wxCommandEvent & evt)868 void MyFrame::OnRestoreWindows(wxCommandEvent& evt)
869 {
870 wxAuiPaneInfoArray& panes = m_mgr.GetAllPanes();
871
872 // Start by restoring the main window although it doesn't seem like this could be much of a problem
873 pFrame->SetSize(wxSize(800, 600));
874 pFrame->SetPosition(wxPoint(20, 20)); // Should work on any screen size
875 // Now re-dock all the windows that are being managed by wxAuiManager
876 int lim = panes.GetCount();
877 for (int i = 0; i < lim; i++)
878 {
879 panes.Item(i).Dock(); // Already docked, shown or not, doesn't matter
880 if (panes.Item(i).name == _("Guider"))
881 panes.Item(i).Show(true);
882 }
883 m_mgr.Update();
884
885 if (pCometTool)
886 pCometTool->Center();
887 if (pDriftTool)
888 pDriftTool->Center();
889 if (pGuidingAssistant)
890 pGuidingAssistant->Center();
891 if (pierFlipToolWin)
892 pierFlipToolWin->Center();
893 if (pNudgeLock)
894 pNudgeLock->Center();
895 }
896
FlipCalibrationData()897 bool MyFrame::FlipCalibrationData()
898 {
899 bool bError = false;
900 Mount *scope = TheScope();
901
902 if (scope)
903 {
904 bError = scope->FlipCalibration();
905
906 if (!bError)
907 {
908 EvtServer.NotifyCalibrationDataFlipped(scope);
909 }
910 }
911
912 return bError;
913 }
914
OnAutoStar(wxCommandEvent & WXUNUSED (evt))915 void MyFrame::OnAutoStar(wxCommandEvent& WXUNUSED(evt))
916 {
917 AutoSelectStar();
918 }
919
OnSetupCamera(wxCommandEvent & WXUNUSED (event))920 void MyFrame::OnSetupCamera(wxCommandEvent& WXUNUSED(event))
921 {
922 if (pCamera &&
923 (((pCamera->PropertyDialogType & PROPDLG_WHEN_CONNECTED) != 0 && pCamera->Connected) ||
924 ((pCamera->PropertyDialogType & PROPDLG_WHEN_DISCONNECTED) != 0 && !pCamera->Connected)))
925 {
926 pCamera->ShowPropertyDialog();
927 }
928 }
929
OnAdvanced(wxCommandEvent & WXUNUSED (event))930 void MyFrame::OnAdvanced(wxCommandEvent& WXUNUSED(event))
931 {
932 pAdvancedDialog->LoadValues();
933
934 if (pAdvancedDialog->ShowModal() == wxID_OK)
935 {
936 Debug.Write("User exited setup dialog with 'ok'\n");
937 pAdvancedDialog->UnloadValues();
938 pGraphLog->UpdateControls();
939 TestGuide::ManualGuideUpdateControls();
940 pConfig->Flush();
941 }
942 else
943 {
944 // Cancel event may require non-trivial undos
945 Debug.Write("User exited setup dialog with 'cancel'\n");
946 pAdvancedDialog->Undo();
947 }
948 }
949
DarksWarningEnabledKey()950 static wxString DarksWarningEnabledKey()
951 {
952 // we want the key to be under "/Confirm" so ConfirmDialog::ResetAllDontAskAgain() resets it, but we also want the setting to be per-profile
953 return wxString::Format("/Confirm/%d/DarksWarningEnabled", pConfig->GetCurrentProfileId());
954 }
955
SuppressDarksAlert(long)956 static void SuppressDarksAlert(long)
957 {
958 pConfig->Global.SetBoolean(DarksWarningEnabledKey(), false);
959 }
960
ValidateDarksLoaded(void)961 static void ValidateDarksLoaded(void)
962 {
963 if (!pCamera->CurrentDarkFrame && !pCamera->CurrentDefectMap)
964 {
965 pFrame->SuppressableAlert(DarksWarningEnabledKey(),
966 _("For best results, use a Dark Library or a Bad-pixel Map "
967 "while guiding. This will help prevent PHD from locking on to a hot pixel. "
968 "Use the Darks menu to build a Dark Library or Bad-pixel Map."), SuppressDarksAlert, 0);
969 }
970 }
971
GuideButtonClick(bool interactive,const wxString & context)972 void MyFrame::GuideButtonClick(bool interactive, const wxString& context)
973 {
974 Debug.Write(wxString::Format(_T("GuideButtonClick i=%d ctx=%s\n"), interactive, context));
975
976 try
977 {
978 if (pMount == NULL)
979 {
980 // no mount selected -- should never happen
981 throw ERROR_INFO("pMount == NULL");
982 }
983
984 if (!pMount->IsConnected())
985 {
986 throw ERROR_INFO("Unable to guide with no scope Connected");
987 }
988
989 if (!pCamera || !pCamera->Connected)
990 {
991 throw ERROR_INFO("Unable to guide with no camera Connected");
992 }
993
994 if (pGuider->GetState() < STATE_SELECTED)
995 {
996 wxMessageBox(_T("Please select a guide star before attempting to guide"));
997 throw ERROR_INFO("Unable to guide with state < STATE_SELECTED");
998 }
999
1000 ValidateDarksLoaded();
1001
1002 if (wxGetKeyState(WXK_SHIFT))
1003 {
1004 bool recalibrate = true;
1005 if (pMount->IsCalibrated() || (pSecondaryMount && pSecondaryMount->IsCalibrated()))
1006 {
1007 recalibrate = ConfirmDialog::Confirm(_("Are you sure you want force recalibration?"),
1008 "/force_recalibration_ok", _("Force Recalibration"));
1009 }
1010 if (recalibrate)
1011 {
1012 pMount->ClearCalibration();
1013 if (pSecondaryMount)
1014 pSecondaryMount->ClearCalibration();
1015 }
1016 }
1017
1018 if (interactive && pPointingSource && pPointingSource->IsConnected() && pPointingSource->CanReportPosition())
1019 {
1020 bool proceed = true;
1021 bool error = pPointingSource->PreparePositionInteractive();
1022
1023 if (!error && fabs(pPointingSource->GetDeclination()) > Scope::DEC_COMP_LIMIT && !TheScope()->IsCalibrated() )
1024 {
1025 proceed = ConfirmDialog::Confirm(
1026 _("Calibration this far from the celestial equator will be error-prone. For best results, calibrate at a declination of -20 to +20."),
1027 "/highdec_calibration_ok", _("Confirm Calibration at Large Declination")
1028 );
1029 }
1030 if (error || !proceed)
1031 return;
1032 }
1033
1034 StartGuiding();
1035 }
1036 catch (const wxString& Msg)
1037 {
1038 POSSIBLY_UNUSED(Msg);
1039 pGuider->Reset(false);
1040 }
1041 }
1042
OnButtonGuide(wxCommandEvent & WXUNUSED (event))1043 void MyFrame::OnButtonGuide(wxCommandEvent& WXUNUSED(event))
1044 {
1045 GuideButtonClick(true, _T("Guide button clicked"));
1046 }
1047
OnTestGuide(wxCommandEvent & WXUNUSED (evt))1048 void MyFrame::OnTestGuide(wxCommandEvent& WXUNUSED(evt))
1049 {
1050 if (!pMount || !pMount->IsConnected())
1051 {
1052 if (!pSecondaryMount || !pSecondaryMount->IsConnected())
1053 {
1054 wxMessageBox(_("Please connect a mount first."), _("Manual Guide"));
1055 return;
1056 }
1057 }
1058
1059 if (!pManualGuide)
1060 pManualGuide = TestGuide::CreateManualGuideWindow();
1061
1062 pManualGuide->Show();
1063 }
1064
OnStarCrossTest(wxCommandEvent & evt)1065 void MyFrame::OnStarCrossTest(wxCommandEvent& evt)
1066 {
1067 if (!pMount || !pMount->IsConnected())
1068 {
1069 if (!pSecondaryMount || !pSecondaryMount->IsConnected())
1070 {
1071 wxMessageBox(_("Please connect a mount first."), _("Star-Cross Test"));
1072 return;
1073 }
1074 }
1075
1076 if (!pStarCrossDlg)
1077 pStarCrossDlg = new StarCrossDialog(this);
1078
1079 pStarCrossDlg->Show();
1080 }
1081
OnPierFlipTool(wxCommandEvent & evt)1082 void MyFrame::OnPierFlipTool(wxCommandEvent& evt)
1083 {
1084 wxString error;
1085 if (!PierFlipTool::CanRunTool(&error))
1086 {
1087 wxMessageBox(error, _("Meridian Flip Calibration Tool"));
1088 return;
1089 }
1090 PierFlipTool::ShowPierFlipCalTool();
1091 }
1092
OnPanelClose(wxAuiManagerEvent & evt)1093 void MyFrame::OnPanelClose(wxAuiManagerEvent& evt)
1094 {
1095 wxAuiPaneInfo *p = evt.GetPane();
1096 if (p->name == _T("MainToolBar"))
1097 {
1098 Menubar->Check(MENU_TOOLBAR, false);
1099 }
1100 if (p->name == _T("GraphLog"))
1101 {
1102 Menubar->Check(MENU_GRAPH, false);
1103 pGraphLog->SetState(false);
1104 }
1105 if (p->name == _T("Stats"))
1106 {
1107 Menubar->Check(MENU_STATS, false);
1108 pStatsWin->SetState(false);
1109 }
1110 if (p->name == _T("Profile"))
1111 {
1112 Menubar->Check(MENU_STARPROFILE, false);
1113 pProfile->SetState(false);
1114 }
1115 if (p->name == _T("AOPosition"))
1116 {
1117 Menubar->Check(MENU_AO_GRAPH, false);
1118 pStepGuiderGraph->SetState(false);
1119 }
1120 if (p->name == _T("Target"))
1121 {
1122 Menubar->Check(MENU_TARGET, false);
1123 pTarget->SetState(false);
1124 }
1125 }
1126
AlertSetRAOnly(long param)1127 static void AlertSetRAOnly(long param)
1128 {
1129 pFrame->SetDitherRaOnly(true);
1130 pFrame->m_infoBar->Dismiss();
1131 }
1132
CheckDecGuideModeAlert()1133 static void CheckDecGuideModeAlert()
1134 {
1135 // One-time, per-profile check to highlight new dithering behavior when Dec guide mode is north or south
1136
1137 if (pConfig->Profile.GetBoolean("/ShowDecModeWarning", true))
1138 {
1139 if (pMount && !pMount->IsStepGuider())
1140 {
1141 Scope *scope = static_cast<Scope *>(pMount);
1142 DEC_GUIDE_MODE dgm = scope->GetDecGuideMode();
1143
1144 if ((dgm == DEC_NORTH || dgm == DEC_SOUTH) && !pFrame->GetDitherRaOnly())
1145 {
1146 wxString msg = _("With your current setting for Dec guide mode, this version of PHD2 will "
1147 "dither in Declination. To restore the old behavior you can set the RA-only "
1148 "option in the Dither Settings of the Advanced Dialog.");
1149 pFrame->Alert(msg, 0,
1150 _("Set RA-only now"), AlertSetRAOnly, 0);
1151
1152 pConfig->Profile.SetBoolean("/ShowDecModeWarning", false);
1153 }
1154 }
1155 }
1156 }
1157
OnSelectGear(wxCommandEvent & evt)1158 void MyFrame::OnSelectGear(wxCommandEvent& evt)
1159 {
1160 try
1161 {
1162 if (CaptureActive)
1163 {
1164 throw ERROR_INFO("OnSelectGear called while CaptureActive");
1165 }
1166
1167 if (pConfig->NumProfiles() == 1 && pGearDialog->IsEmptyProfile())
1168 {
1169 if (ConfirmDialog::Confirm(
1170 _("It looks like this is a first-time connection to your camera and mount. The Setup Wizard can help\n"
1171 "you with that and will also establish baseline guiding parameters for your new configuration.\n"
1172 "Would you like to use the Setup Wizard now?"),
1173 "/use_new_profile_wizard", _("Yes"), _("No"), _("Setup Wizard Recommendation")))
1174 {
1175 pGearDialog->ShowProfileWizard(evt);
1176 return;
1177 }
1178 }
1179
1180 pGearDialog->ShowGearDialog(wxGetKeyState(WXK_SHIFT));
1181
1182 CheckDecGuideModeAlert();
1183 }
1184 catch (const wxString& Msg)
1185 {
1186 POSSIBLY_UNUSED(Msg);
1187 }
1188 }
1189
OnBookmarksShow(wxCommandEvent & evt)1190 void MyFrame::OnBookmarksShow(wxCommandEvent& evt)
1191 {
1192 pGuider->SetBookmarksShown(evt.IsChecked());
1193 }
1194
OnBookmarksSetAtLockPos(wxCommandEvent & evt)1195 void MyFrame::OnBookmarksSetAtLockPos(wxCommandEvent& evt)
1196 {
1197 pGuider->BookmarkLockPosition();
1198 }
1199
OnBookmarksSetAtCurPos(wxCommandEvent & evt)1200 void MyFrame::OnBookmarksSetAtCurPos(wxCommandEvent& evt)
1201 {
1202 pGuider->BookmarkCurPosition();
1203 }
1204
OnBookmarksClearAll(wxCommandEvent & evt)1205 void MyFrame::OnBookmarksClearAll(wxCommandEvent& evt)
1206 {
1207 pGuider->DeleteAllBookmarks();
1208 }
1209
OnTextControlSetFocus(wxFocusEvent & evt)1210 void MyFrame::OnTextControlSetFocus(wxFocusEvent& evt)
1211 {
1212 m_showBookmarksMenuItem->SetAccel(0);
1213 m_bookmarkLockPosMenuItem->SetAccel(0);
1214 evt.Skip();
1215 }
1216
OnTextControlKillFocus(wxFocusEvent & evt)1217 void MyFrame::OnTextControlKillFocus(wxFocusEvent& evt)
1218 {
1219 m_showBookmarksMenuItem->SetAccel(m_showBookmarksAccel);
1220 m_bookmarkLockPosMenuItem->SetAccel(m_bookmarkLockPosAccel);
1221 evt.Skip();
1222 }
1223
OnCharHook(wxKeyEvent & evt)1224 void MyFrame::OnCharHook(wxKeyEvent& evt)
1225 {
1226 bool handled = false;
1227
1228 // This never gets called on OSX (since we moved to wxWidgets-3.0.0), so we
1229 // rely on the menu accelerators on the MyFrame to provide the keyboard
1230 // responses. For Windows and Linux, we keep this here so the keystrokes
1231 // work when other windows like the Drift Tool window have focus.
1232
1233 if (evt.GetKeyCode() == 'B')
1234 {
1235 if (!evt.GetEventObject()->IsKindOf(wxCLASSINFO(wxTextCtrl)))
1236 {
1237 int modifiers;
1238 #ifdef __WXOSX__
1239 modifiers = 0;
1240 if (wxGetKeyState(WXK_ALT))
1241 modifiers |= wxMOD_ALT;
1242 if (wxGetKeyState(WXK_CONTROL))
1243 modifiers |= wxMOD_CONTROL;
1244 if (wxGetKeyState(WXK_SHIFT))
1245 modifiers |= wxMOD_SHIFT;
1246 if (wxGetKeyState(WXK_RAW_CONTROL))
1247 modifiers |= wxMOD_RAW_CONTROL;
1248 #else
1249 modifiers = evt.GetModifiers();
1250 #endif
1251 if (!modifiers)
1252 {
1253 pGuider->ToggleShowBookmarks();
1254 bookmarks_menu->Check(MENU_BOOKMARKS_SHOW, pGuider->GetBookmarksShown());
1255 handled = true;
1256 }
1257 else if (modifiers == wxMOD_CONTROL)
1258 {
1259 pGuider->DeleteAllBookmarks();
1260 handled = true;
1261 }
1262 else if (modifiers == wxMOD_SHIFT)
1263 {
1264 pGuider->BookmarkLockPosition();
1265 handled = true;
1266 }
1267 }
1268 }
1269
1270 if (!handled)
1271 {
1272 evt.Skip();
1273 }
1274 }
1275