1 /*
2  *    indi_gui.cpp
3  *    PHD Guiding
4  *
5  *    Copyright(c) 2009 Geoffrey Hausheer. All rights reserved.
6  *
7  *    Redraw for libindi/baseclient by Patrick Chevalley
8  *    Copyright (c) 2014 Patrick Chevalley
9  *    All rights reserved.
10  *
11  *    This library is free software; you can redistribute it and/or
12  *    modify it under the terms of the GNU Lesser General Public
13  *    License as published by the Free Software Foundation; either
14  *    version 2.1 of the License, or (at your option) any later version.
15  *
16  *    This library is distributed in the hope that it will be useful,
17  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *    Lesser General Public License for more details.
20  *
21  *    You should have received a copy of the GNU Lesser General Public
22  *    License along with this library; if not, write to the Free Software
23  *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24  *
25  *  Contact Information: gcx@phracturedblue.com <Geoffrey Hausheer>
26  *******************************************************************************/
27 
28 #ifdef _WIN32
29 #pragma warning(disable: 4996)
30 #endif
31 
32 #include "phd.h"
33 #include "indi_gui.h"
34 
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <wx/progdlg.h>
39 
40 /*
41  *  Status LED
42  */
43 class IndiStatus : public wxLed
44 {
45 public:
IndiStatus(wxWindow * parent,wxWindowID id,IPState state)46     IndiStatus(wxWindow *parent, wxWindowID id, IPState state) : wxLed(parent, id)
47     {
48         SetState(state);
49         Enable();
50     }
51 
SetState(int state)52     void SetState(int state)
53     {
54         static const char indi_state[4][6] = {
55             "Idle",
56             "Ok",
57             "Busy",
58             "Alert",
59         };
60         switch(state) {
61         case IPS_IDLE:  SetColor("808080"); break;
62         case IPS_OK:    SetColor("008000"); break;
63         case IPS_BUSY:  SetColor("FFFF00"); break;
64         case IPS_ALERT: SetColor("FF0000"); break;
65         }
66         SetToolTip(wxString::FromAscii(indi_state[state]));
67     }
68 };
69 
70 /*
71  *  A device page and related properties
72  */
73 class IndiDev
74 {
75 public:
76     wxNotebook *page;
77     INDI::BaseDevice *dp;
78     PtrHash groups;
79     PtrHash properties;
80 };
81 
82 /*
83  *  Property Information
84  */
85 class IndiProp
86 {
87 public:
88     wxString PropName;
89     PtrHash ctrl;
90     PtrHash entry;
91     IndiStatus *state;
92     wxStaticText *name;
93     wxPanel *page;
94     wxPanel *panel;
95     wxGridBagSizer *gbs;
96     INDI::Property *property;
97     IndiDev *idev;
98 };
99 
100 enum
101 {
102     SWITCH_CHECKBOX,
103     SWITCH_BUTTON,
104     SWITCH_COMBOBOX,
105 };
106 
107 #define POS(r, c)        wxGBPosition(r,c)
108 #define SPAN(r, c)       wxGBSpan(r,c)
109 
110 wxDEFINE_EVENT(INDIGUI_THREAD_NEWDEVICE_EVENT, wxThreadEvent);
111 wxDEFINE_EVENT(INDIGUI_THREAD_NEWPROPERTY_EVENT, wxThreadEvent);
112 wxDEFINE_EVENT(INDIGUI_THREAD_NEWNUMBER_EVENT, wxThreadEvent);
113 wxDEFINE_EVENT(INDIGUI_THREAD_NEWTEXT_EVENT, wxThreadEvent);
114 wxDEFINE_EVENT(INDIGUI_THREAD_NEWSWITCH_EVENT, wxThreadEvent);
115 wxDEFINE_EVENT(INDIGUI_THREAD_NEWMESSAGE_EVENT, wxThreadEvent);
116 wxDEFINE_EVENT(INDIGUI_THREAD_REMOVEPROPERTY_EVENT, wxThreadEvent);
117 
wxBEGIN_EVENT_TABLE(IndiGui,wxDialog)118 wxBEGIN_EVENT_TABLE(IndiGui, wxDialog)
119   EVT_CLOSE(IndiGui::OnQuit)
120   EVT_THREAD(INDIGUI_THREAD_NEWDEVICE_EVENT, IndiGui::OnNewDeviceFromThread)
121   EVT_THREAD(INDIGUI_THREAD_NEWPROPERTY_EVENT, IndiGui::OnNewPropertyFromThread)
122   EVT_THREAD(INDIGUI_THREAD_NEWNUMBER_EVENT, IndiGui::OnNewNumberFromThread)
123   EVT_THREAD(INDIGUI_THREAD_NEWTEXT_EVENT, IndiGui::OnNewTextFromThread)
124   EVT_THREAD(INDIGUI_THREAD_NEWSWITCH_EVENT, IndiGui::OnNewSwitchFromThread)
125   EVT_THREAD(INDIGUI_THREAD_NEWMESSAGE_EVENT, IndiGui::OnNewMessageFromThread)
126   EVT_THREAD(INDIGUI_THREAD_REMOVEPROPERTY_EVENT, IndiGui::OnRemovePropertyFromThread)
127 wxEND_EVENT_TABLE()
128 
129 //////////////////////////////////////////////////////////////////////
130 // Functions running in the INDI client thread
131 //////////////////////////////////////////////////////////////////////
132 
133 void IndiGui::newDevice(INDI::BaseDevice *dp)
134 {
135     wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, INDIGUI_THREAD_NEWDEVICE_EVENT);
136     event->SetExtraLong((long) dp);
137     wxQueueEvent(this, event);
138 }
139 
newProperty(INDI::Property * property)140 void IndiGui::newProperty(INDI::Property *property)
141 {
142     wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, INDIGUI_THREAD_NEWPROPERTY_EVENT);
143     event->SetExtraLong((long) property);
144     wxQueueEvent(this, event);
145 }
146 
newNumber(INumberVectorProperty * nvp)147 void IndiGui::newNumber(INumberVectorProperty *nvp)
148 {
149     wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, INDIGUI_THREAD_NEWNUMBER_EVENT);
150     event->SetExtraLong((long) nvp);
151     wxQueueEvent(this, event);
152 }
153 
newSwitch(ISwitchVectorProperty * svp)154 void IndiGui::newSwitch(ISwitchVectorProperty *svp)
155 {
156     wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, INDIGUI_THREAD_NEWSWITCH_EVENT);
157     event->SetExtraLong((long) svp);
158     wxQueueEvent(this, event);
159 }
160 
newText(ITextVectorProperty * tvp)161 void IndiGui::newText(ITextVectorProperty *tvp)
162 {
163     wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, INDIGUI_THREAD_NEWTEXT_EVENT);
164     event->SetExtraLong((long) tvp);
165     wxQueueEvent(this, event);
166 }
167 
newMessage(INDI::BaseDevice * dp,int messageID)168 void IndiGui::newMessage(INDI::BaseDevice *dp, int messageID)
169 {
170     wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, INDIGUI_THREAD_NEWMESSAGE_EVENT);
171     event->SetString(dp->messageQueue(messageID));
172     wxQueueEvent(this, event);
173 }
174 
removeProperty(INDI::Property * property)175 void IndiGui::removeProperty(INDI::Property *property)
176 {
177     if (property)
178     {
179         wxString devname =  wxString::FromAscii(property->getDeviceName());
180         wxString groupname =  wxString::FromAscii(property->getGroupName());
181         wxString propname =  wxString::FromAscii(property->getName());
182         IndiDev *indiDev = (IndiDev *)devlist[devname];
183         if (!indiDev) return;
184         IndiProp *indiProp = (IndiProp *)indiDev->properties[propname];
185         if (!indiProp) return;
186 
187         wxThreadEvent *event = new wxThreadEvent(wxEVT_THREAD, INDIGUI_THREAD_REMOVEPROPERTY_EVENT);
188         event->SetExtraLong((long) indiProp);
189         wxQueueEvent(this, event);
190     }
191 }
192 
193 //////////////////////////////////////////////////////////////////////
194 
ConnectServer(const wxString & INDIhost,long INDIport)195 void IndiGui::ConnectServer(const wxString& INDIhost, long INDIport)
196 {
197     setServer(INDIhost.mb_str(wxConvUTF8), INDIport);
198     connectServer();
199 }
200 
serverConnected()201 void IndiGui::serverConnected()
202 {
203     setBLOBMode(B_NEVER, "", nullptr);
204     m_lastUpdate = wxGetUTCTimeMillis();
205 }
206 
IndiServerDisconnected(int exit_code)207 void IndiGui::IndiServerDisconnected(int exit_code)
208 {
209     if (m_deleted)
210     {
211         // nothing to do if we're getting the notification via the
212         // disconnectServer() call from the destructor
213         return;
214     }
215 
216     if (wxThread::IsMain())
217     {
218         Destroy();
219     }
220     else
221     {
222         wxCloseEvent *event = new wxCloseEvent(wxEVT_CLOSE_WINDOW, GetId());
223         event->SetEventObject(this);
224         event->SetCanVeto(false);
225         wxQueueEvent(this, event);
226     }
227 }
228 
OnNewDeviceFromThread(wxThreadEvent & event)229 void IndiGui::OnNewDeviceFromThread(wxThreadEvent& event)
230 {
231     INDI::BaseDevice *dp = (INDI::BaseDevice *) event.GetExtraLong();
232     //printf("newdevice from thread %s \n",dp->getDeviceName());
233     wxString devname =  wxString::FromAscii(dp->getDeviceName());
234     IndiDev *indiDev = new IndiDev();
235     wxPanel *panel = new wxPanel(parent_notebook);
236     indiDev->page = new wxNotebook(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
237     wxBoxSizer *nb_sizer = new wxBoxSizer(wxVERTICAL);
238     panel->SetSizer(nb_sizer);
239     nb_sizer->Add(indiDev->page, 1,  wxEXPAND | wxALL);
240     parent_notebook->AddPage(panel, devname);
241     indiDev->dp = dp;
242     devlist[devname] = indiDev;
243     panel->Fit();
244     sizer->Layout();
245     Fit();
246     m_lastUpdate = wxGetUTCTimeMillis();
247 }
248 
OnNewPropertyFromThread(wxThreadEvent & event)249 void IndiGui::OnNewPropertyFromThread(wxThreadEvent& event)
250 {
251     INDI::Property *property = (INDI::Property *) event.GetExtraLong();
252     //printf("newproperty from thread %s %s %s\n",property->getDeviceName(),property->getGroupName(),property->getName());
253     wxString devname =  wxString::FromAscii(property->getDeviceName());
254     wxString groupname =  wxString::FromAscii(property->getGroupName());
255     wxString propname =  wxString::FromAscii(property->getName());
256 
257     IndiProp *indiProp = new IndiProp();
258     wxPanel *page;
259     wxGridBagSizer *gbs;
260     int next_free_row;
261     IndiDev *indiDev = (IndiDev *)devlist[devname];
262     if (! indiDev) return;
263     indiProp->idev = indiDev;
264 
265     page = (wxPanel *)indiDev->groups[groupname];
266     if (!page)
267     {
268         page = new wxPanel(indiDev->page);
269         indiDev->page->AddPage(page, groupname);
270         page->SetSizer(new wxGridBagSizer(0, 20));
271         indiDev->groups[groupname] = page;
272     }
273 
274     gbs = (wxGridBagSizer *)page->GetSizer();
275     gbs->Layout();
276     next_free_row = gbs->GetRows();
277     BuildPropWidget(property, page, indiProp);
278 
279     gbs->Add(indiProp->state, POS(next_free_row, 0), SPAN(1, 1), wxALIGN_LEFT | wxALL);
280     gbs->Add(indiProp->name, POS(next_free_row, 1), SPAN(1, 1), wxALIGN_LEFT | wxALL);
281     gbs->Add(indiProp->panel,POS(next_free_row, 2), SPAN(1, 1), wxALIGN_LEFT | wxEXPAND | wxALL);
282     gbs->Layout();
283     page->Fit();
284     panel->Fit();
285     indiDev->properties[propname] = indiProp;
286     indiDev->page->Fit();
287     indiDev->page->Layout();
288     indiDev->page->Show();
289     sizer->Layout();
290     Fit();
291     m_lastUpdate = wxGetUTCTimeMillis();
292 }
293 
BuildPropWidget(INDI::Property * property,wxPanel * parent,IndiProp * indiProp)294 void IndiGui::BuildPropWidget(INDI::Property *property, wxPanel *parent, IndiProp *indiProp)
295 {
296     wxString propname =  wxString::FromAscii(property->getName());
297     wxString proplbl =  wxString::FromAscii(property->getLabel());
298     if (! proplbl) proplbl = propname;
299 #ifdef INDI_PRE_1_1_0
300     INDI_TYPE proptype = property->getType();
301 #else
302     INDI_PROPERTY_TYPE proptype = property->getType();
303 #endif
304 
305     indiProp->page = parent;
306     indiProp->panel = new wxPanel(parent);
307     indiProp->gbs  = new wxGridBagSizer(0, 20);
308     indiProp->panel->SetSizer(indiProp->gbs);
309 
310     indiProp->state = new IndiStatus(parent, wxID_ANY, property->getState());
311     indiProp->name  = new wxStaticText(parent, wxID_ANY,proplbl);
312     indiProp->PropName = propname;
313     indiProp->property = property;
314 
315     switch (proptype) {
316     case INDI_TEXT:
317         CreateTextWidget(property, indiProp);
318         break;
319     case INDI_SWITCH:
320         CreateSwitchWidget(property, indiProp);
321         break;
322     case INDI_NUMBER:
323         CreateNumberWidget(property, indiProp);
324         break;
325     case INDI_LIGHT:
326         CreateLightWidget(property, indiProp);
327         break;
328     case INDI_BLOB:
329         CreateBlobWidget(property, indiProp);
330         break;
331     case INDI_UNKNOWN:
332         CreateUnknowWidget(property, indiProp);
333         break;
334     }
335     indiProp->gbs->Layout();
336 }
337 
GetSwitchType(ISwitchVectorProperty * svp)338 int IndiGui::GetSwitchType(ISwitchVectorProperty *svp)
339 {
340     int num_props = svp->nsp;
341 
342     if (svp->r == ISR_NOFMANY)
343         return SWITCH_CHECKBOX;
344 
345     if (num_props <= 4)
346         return SWITCH_BUTTON;
347 
348     return SWITCH_COMBOBOX;
349 }
350 
CreateSwitchWidget(INDI::Property * property,IndiProp * indiProp)351 void IndiGui::CreateSwitchWidget(INDI::Property *property, IndiProp *indiProp)
352 {
353     //printf("CreateSwitchWidget\n");
354     int guitype = GetSwitchType(property->getSwitch());
355 
356     switch (guitype) {
357     case SWITCH_COMBOBOX: CreateSwitchCombobox(property->getSwitch(), indiProp); break;
358     case SWITCH_CHECKBOX: CreateSwitchCheckbox(property->getSwitch(), indiProp); break;
359     case SWITCH_BUTTON:   CreateSwitchButton(property->getSwitch(), indiProp);   break;
360     }
361 }
362 
CreateSwitchCombobox(ISwitchVectorProperty * svp,IndiProp * indiProp)363 void IndiGui::CreateSwitchCombobox(ISwitchVectorProperty *svp, IndiProp *indiProp)
364 {
365     wxString *choices = new wxString[svp->nsp];
366     wxPanel *p = indiProp->panel;
367     wxGridBagSizer *gbs = indiProp->gbs;
368 
369     int idx = 0;
370     for (int i = 0; i < svp->nsp; i++)
371     {
372         if(svp->sp[i].s == ISS_ON)
373             idx = i;
374         indiProp->ctrl[wxString::FromAscii(svp->sp[i].name)] = (void *) (intptr_t) i;
375         wxString swlbl = wxString::FromAscii(svp->sp[i].label);
376         if (swlbl.empty())
377             swlbl = wxString::FromAscii(svp->sp[i].name);
378         choices[i] = swlbl;
379     }
380     wxChoice *combo = new wxChoice(p, wxID_ANY, wxDefaultPosition, wxDefaultSize, svp->nsp, choices);
381     combo->SetSelection(idx);
382     combo->SetClientData(indiProp);
383     Connect(combo->GetId(), wxEVT_COMMAND_CHOICE_SELECTED,
384             wxCommandEventHandler(IndiGui::SetComboboxEvent));
385     gbs->Add(combo, POS(0, 0), SPAN(1, 1), wxALIGN_LEFT | wxALL);
386     indiProp->ctrl[wxString::FromAscii(svp->name)] = (void *) combo;
387     delete [] choices;
388 }
389 
CreateSwitchCheckbox(ISwitchVectorProperty * svp,IndiProp * indiProp)390 void IndiGui::CreateSwitchCheckbox(ISwitchVectorProperty *svp, IndiProp *indiProp)
391 {
392     wxPanel *p = indiProp->panel;
393     wxGridBagSizer *gbs = indiProp->gbs;
394     for (int pos = 0; pos < svp->nsp; pos++)
395     {
396         wxString swlbl = wxString::FromAscii(svp->sp[pos].label);
397         if (swlbl.empty())
398             swlbl = wxString::FromAscii(svp->sp[pos].name);
399         wxCheckBox *button = new wxCheckBox(p, wxID_ANY, swlbl);
400         indiProp->ctrl[wxString::FromAscii(svp->sp[pos].name)] = button;
401         if (svp->sp[pos].s == ISS_ON)
402             button->SetValue(true);
403         button->SetClientData(indiProp);
404         Connect(button->GetId(), wxEVT_COMMAND_CHECKBOX_CLICKED,
405                 wxCommandEventHandler(IndiGui::SetCheckboxEvent));
406         gbs->Add(button, POS(pos / 4, pos % 4), SPAN(1, 1), wxALIGN_LEFT | wxALL);
407     }
408 }
409 
CreateSwitchButton(ISwitchVectorProperty * svp,IndiProp * indiProp)410 void IndiGui::CreateSwitchButton(ISwitchVectorProperty *svp, IndiProp *indiProp)
411 {
412     wxPanel *p = indiProp->panel;
413     wxGridBagSizer *gbs = indiProp->gbs;
414     for (int pos = 0; pos < svp->nsp; pos++)
415     {
416         wxString swlbl = wxString::FromAscii(svp->sp[pos].label);
417         if (swlbl.empty())
418             swlbl = wxString::FromAscii(svp->sp[pos].name);
419         wxToggleButton *button = new wxToggleButton(p, wxID_ANY, swlbl);
420         indiProp->ctrl[wxString::FromAscii(svp->sp[pos].name)] = button;
421         if (svp->sp[pos].s == ISS_ON)
422             button->SetValue(true);
423         button->SetClientData(indiProp);
424         Connect(button->GetId(), wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
425                 wxCommandEventHandler(IndiGui::SetToggleButtonEvent));
426         if (!allow_connect_disconnect && strcmp(svp->name,"CONNECTION") == 0)
427         {
428             button->Enable(false);
429         }
430         gbs->Add(button, POS(0, pos), SPAN(1, 1), wxALIGN_LEFT | wxALL);
431     }
432 }
433 
CreateTextWidget(INDI::Property * property,IndiProp * indiProp)434 void IndiGui::CreateTextWidget(INDI::Property *property, IndiProp *indiProp)
435 {
436     ITextVectorProperty *tvp = property->getText();
437     wxPanel *p = indiProp->panel;
438     wxGridBagSizer *gbs = indiProp->gbs;
439 
440     int pos;
441     for (pos = 0; pos < tvp->ntp; pos++)
442     {
443         gbs->Add(new wxStaticText(p, wxID_ANY, wxString::FromAscii(tvp->tp[pos].label)),
444                  POS(pos, 0), SPAN(1, 1), wxALIGN_LEFT | wxALL);
445 
446         wxStaticText *value = new wxStaticText(p, wxID_ANY, wxString::FromAscii(tvp->tp[pos].text));
447         indiProp->ctrl[wxString::FromAscii(tvp->tp[pos].name)] = value;
448         gbs->Add(value, POS(pos, 1), SPAN(1, 1), wxALIGN_LEFT | wxALL);
449         if (tvp->p != IP_RO)
450         {
451             wxTextCtrl *entry = new wxTextCtrl(p, wxID_ANY);
452             indiProp->entry[wxString::FromAscii(tvp->tp[pos].name)] = entry;
453             gbs->Add(entry, POS(pos, 2), SPAN(1, 1), wxALIGN_LEFT | wxEXPAND | wxALL);
454         }
455     }
456     if (tvp->p != IP_RO)
457     {
458         wxButton *button = new wxButton(p, wxID_ANY, _("Set"));
459         button->SetClientData(indiProp);
460         Connect(button->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(IndiGui::SetButtonEvent));
461         gbs->Add(button, POS(0, 3), SPAN(pos, 1), wxALIGN_LEFT | wxALL);
462     }
463 }
464 
CreateNumberWidget(INDI::Property * property,IndiProp * indiProp)465 void IndiGui::CreateNumberWidget(INDI::Property *property, IndiProp *indiProp)
466 {
467     INumberVectorProperty *nvp = property->getNumber();
468     wxPanel *p = indiProp->panel;
469     wxGridBagSizer *gbs = indiProp->gbs;
470 
471     int pos;
472     for (pos = 0; pos < nvp->nnp; pos++)
473     {
474         gbs->Add(new wxStaticText(p, wxID_ANY, wxString::FromAscii(nvp->np[pos].label)),
475                  POS(pos, 0), SPAN(1, 1), wxALIGN_LEFT | wxALL);
476 
477         wxStaticText *value = new wxStaticText(p, wxID_ANY, wxString::Format(_T("%f"), nvp->np[pos].value));
478         indiProp->ctrl[wxString::FromAscii(nvp->np[pos].name)] = value;
479         gbs->Add(value, POS(pos, 1), SPAN(1, 1), wxALIGN_LEFT | wxALL);
480         if (nvp->p != IP_RO)
481         {
482             wxTextCtrl *entry = new wxTextCtrl(p, wxID_ANY);
483             indiProp->entry[wxString::FromAscii(nvp->np[pos].name)] = entry;
484             gbs->Add(entry, POS(pos, 2), SPAN(1, 1), wxALIGN_LEFT | wxEXPAND | wxALL);
485         }
486     }
487     if (nvp->p != IP_RO)
488     {
489         wxButton *button = new wxButton(p, wxID_ANY, _("Set"));
490         button->SetClientData(indiProp);
491         Connect(button->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(IndiGui::SetButtonEvent));
492         gbs->Add(button, POS(0, 3), SPAN(pos, 1), wxALIGN_LEFT | wxALL);
493     }
494 }
495 
CreateLightWidget(INDI::Property * property,IndiProp * indiProp)496 void IndiGui::CreateLightWidget(INDI::Property *property, IndiProp *indiProp)
497 {
498     //printf("IndiGui: Unimplemented CreateLightWidget\n");
499 }
500 
CreateBlobWidget(INDI::Property * property,IndiProp * indiProp)501 void IndiGui::CreateBlobWidget(INDI::Property *property, IndiProp *indiProp)
502 {
503     //printf("IndiGui: Unimplemented CreateBlobWidget\n");
504 }
505 
CreateUnknowWidget(INDI::Property * property,IndiProp * indiProp)506 void IndiGui::CreateUnknowWidget(INDI::Property *property, IndiProp *indiProp)
507 {
508     //printf("IndiGui: Unimplemented CreateUnknowWidget\n");
509 }
510 
OnNewNumberFromThread(wxThreadEvent & event)511 void IndiGui::OnNewNumberFromThread(wxThreadEvent& event)
512 {
513     INumberVectorProperty *nvp = (INumberVectorProperty *) event.GetExtraLong();
514     wxString devname = wxString::FromAscii(nvp->device);
515     wxString propname = wxString::FromAscii(nvp->name);
516     IndiDev *indiDev = (IndiDev *) devlist[devname];
517     IndiProp *indiProp = (IndiProp *) indiDev->properties[propname];
518     for (int i = 0; i < nvp->nnp; i++)
519     {
520         void *st = indiProp->ctrl[wxString::FromAscii(nvp->np[i].name)];
521         wxStaticText *ctrl = (wxStaticText *)st;
522         ctrl->SetLabel(wxString::Format(wxT("%f"), nvp->np[i].value));
523     }
524     indiProp->state->SetState(nvp->s);
525 }
526 
OnNewTextFromThread(wxThreadEvent & event)527 void IndiGui::OnNewTextFromThread(wxThreadEvent& event)
528 {
529     ITextVectorProperty *tvp = (ITextVectorProperty *) event.GetExtraLong();
530     //printf("newtext from thread %s \n",tvp->name);
531     wxString devname =  wxString::FromAscii(tvp->device);
532     wxString propname =  wxString::FromAscii(tvp->name);
533     IndiDev *indiDev = (IndiDev *) devlist[devname];
534     IndiProp *indiProp = (IndiProp *) indiDev->properties[propname];
535     for (int i = 0; i < tvp->ntp; i++)
536     {
537         void *st = indiProp->ctrl[wxString::FromAscii(tvp->tp[i].name)];
538         wxStaticText *ctrl = (wxStaticText *)st;
539         ctrl->SetLabel(wxString::Format(wxT("%s"), tvp->tp[i].text));
540     }
541     indiProp->state->SetState(tvp->s);
542 }
543 
OnNewSwitchFromThread(wxThreadEvent & event)544 void IndiGui::OnNewSwitchFromThread(wxThreadEvent& event)
545 {
546     ISwitchVectorProperty *svp = (ISwitchVectorProperty *) event.GetExtraLong();
547     wxString devname = wxString::FromAscii(svp->device);
548     wxString propname = wxString::FromAscii(svp->name);
549     int swtype = GetSwitchType(svp);
550     IndiDev *indiDev = (IndiDev *) devlist[devname];
551     IndiProp *indiProp = (IndiProp *) indiDev->properties[propname];
552     switch (swtype) {
553     case SWITCH_COMBOBOX: {
554         int idx=0;
555         for (int i = 0; i < svp->nsp; i++)
556         {
557             if (svp->sp[i].s == ISS_ON)
558                 idx = i;
559         }
560         void *st = indiProp->ctrl[wxString::FromAscii(svp->name)];
561         wxChoice *combo = (wxChoice *)st;
562         combo->SetSelection(idx);
563         break;
564     }
565     case SWITCH_CHECKBOX:{
566         for (int i = 0; i < svp->nsp; i++)
567         {
568             void *st = indiProp->ctrl[wxString::FromAscii(svp->sp[i].name)];
569             wxCheckBox *button = (wxCheckBox *) st;
570             button->SetValue(svp->sp[i].s ? true : false);
571         }
572         break;
573     }
574     case SWITCH_BUTTON:{
575         for (int i = 0; i < svp->nsp; i++)
576         {
577             void *st = indiProp->ctrl[wxString::FromAscii(svp->sp[i].name)];
578             wxToggleButton *button = (wxToggleButton *) st;
579             button->SetValue(svp->sp[i].s ? true : false);
580         }
581         break;
582     }
583     }
584 }
585 
OnNewMessageFromThread(wxThreadEvent & event)586 void IndiGui::OnNewMessageFromThread(wxThreadEvent& event)
587 {
588     textbuffer->SetInsertionPoint(0);
589     textbuffer->WriteText(event.GetString());
590     textbuffer->WriteText(_T("\n"));
591 }
592 
SetButtonEvent(wxCommandEvent & event)593 void IndiGui::SetButtonEvent(wxCommandEvent& event)
594 {
595     wxButton *button = (wxButton *)event.GetEventObject();
596     if (!button) return;
597     IndiProp *indiProp = (IndiProp *) button->GetClientData();
598     if (!indiProp) return;
599 
600     switch (indiProp->property->getType()) {
601     case INDI_TEXT: {
602         ITextVectorProperty *tvp = indiProp->property->getText();
603         for (int i = 0; i < tvp->ntp; i++)
604         {
605             if (tvp->p != IP_RO)
606             {
607                 wxTextCtrl *entry = (wxTextCtrl *)(indiProp->entry[wxString::FromAscii(tvp->tp[i].name)]);
608                 sprintf(tvp->tp[i].text, "%s", entry->GetLineText(0).mb_str().data());
609             }
610         }
611         sendNewText(tvp);
612         break;
613     }
614     case INDI_NUMBER:{
615         INumberVectorProperty *nvp = indiProp->property->getNumber();
616         for (int i = 0; i < nvp->nnp; i++)
617         {
618             if (nvp->p != IP_RO)
619             {
620                 wxTextCtrl *entry = (wxTextCtrl *)(indiProp->entry[wxString::FromAscii(nvp->np[i].name)]);
621                 entry->GetLineText(0).ToDouble(&nvp->np[i].value);
622             }
623         }
624         sendNewNumber(nvp);
625         break;
626     }
627     default:
628         break;
629     }
630 }
631 
SetToggleButtonEvent(wxCommandEvent & event)632 void IndiGui::SetToggleButtonEvent(wxCommandEvent& event)
633 {
634     wxToggleButton *button = (wxToggleButton *)event.GetEventObject();
635     if (!button) return;
636     IndiProp *indiProp = (IndiProp *) button->GetClientData();
637     if (!indiProp) return;
638     ISwitchVectorProperty *svp = indiProp->property->getSwitch();
639 
640     if (!allow_connect_disconnect && strcmp(svp->name, "CONNECTION") ==0 )
641     {
642         // Prevent device disconnection from this window.
643         // Use Gear manager instead.
644         return;
645     }
646 
647     wxString b_name;
648     for (auto it = indiProp->ctrl.begin(); it !=indiProp->ctrl.end(); ++it)
649     {
650         wxString key = it->first;
651         wxToggleButton *value = (wxToggleButton *) it->second;
652         if (value == button)
653         {
654             b_name = key;
655             break;
656         }
657     }
658     if (svp->r == ISR_1OFMANY)
659     {
660         for (int i = 0; i < svp->nsp; i++)
661         {
662             if (svp->sp[i].name == b_name)
663                 svp->sp[i].s = ISS_ON;
664             else
665                 svp->sp[i].s = ISS_OFF;
666         }
667     }
668     else
669     {
670         for (int i = 0; i < svp->nsp; i++)
671         {
672             if (svp->sp[i].name == b_name)
673             {
674                 svp->sp[i].s = button->GetValue() ? ISS_ON : ISS_OFF;
675                 break;
676             }
677         }
678     }
679     sendNewSwitch(svp);
680 }
681 
682 
SetComboboxEvent(wxCommandEvent & event)683 void IndiGui::SetComboboxEvent(wxCommandEvent& event)
684 {
685     wxChoice *combo = (wxChoice *)event.GetEventObject();
686     if (!combo) return;
687     IndiProp *indiProp = (IndiProp *) combo->GetClientData();
688     if (!indiProp) return;
689     ISwitchVectorProperty *svp = indiProp->property->getSwitch();
690     int choice = combo->GetSelection();
691     for (int i = 0; i < svp->nsp; i++)
692     {
693         if (i == choice)
694             svp->sp[i].s = ISS_ON;
695         else
696             svp->sp[i].s = ISS_OFF;
697     }
698     sendNewSwitch(svp);
699 }
700 
SetCheckboxEvent(wxCommandEvent & event)701 void IndiGui::SetCheckboxEvent(wxCommandEvent& event)
702 {
703     wxCheckBox *button = (wxCheckBox *)event.GetEventObject();
704     if (!button) return;
705     IndiProp *indiProp = (IndiProp *) button->GetClientData();
706     if (!indiProp) return;
707     ISwitchVectorProperty *svp = indiProp->property->getSwitch();
708 
709     wxString b_name;
710     for (auto it = indiProp->ctrl.begin(); it !=indiProp->ctrl.end(); ++it)
711     {
712         wxString key = it->first;
713         wxCheckBox *value = (wxCheckBox *) it->second;
714         if (value == button)
715         {
716             b_name = key;
717             break;
718         }
719     }
720     for (int i = 0; i < svp->nsp; i++)
721     {
722         if (svp->sp[i].name == b_name)
723         {
724             svp->sp[i].s = button->GetValue() ? ISS_ON : ISS_OFF;
725             break;
726         }
727     }
728     sendNewSwitch(svp);
729 }
730 
OnRemovePropertyFromThread(wxThreadEvent & event)731 void IndiGui::OnRemovePropertyFromThread(wxThreadEvent& event)
732 {
733     IndiProp *indiProp = (IndiProp *)event.GetExtraLong();
734     if (!indiProp) return;
735     IndiDev *indiDev = (IndiDev *) indiProp->idev;
736     if (!indiDev) return;
737     wxString propname = indiProp->PropName;
738 
739     for (int y = 0; y < indiProp->gbs->GetRows(); y++)
740     {
741         for (int x = 0; x < indiProp->gbs->GetCols(); x++)
742         {
743             wxGBSizerItem *item = indiProp->gbs->FindItemAtPosition(POS(y, x));
744             if (item)
745             {
746                 indiProp->gbs->Remove(item->GetId());
747                 item->GetWindow()->Destroy();
748             }
749         }
750     }
751     indiProp->gbs->Layout();
752     if (indiProp->name)
753         indiProp->name->Destroy();
754     if (indiProp->state)
755         indiProp->state->Destroy();
756     if (indiProp->panel)
757         indiProp->panel->Destroy();
758     if (indiProp->page->GetChildren().GetCount() == 0)
759     {
760         for (unsigned int i = 0; i < indiDev->page->GetPageCount(); i++)
761         {
762             if (indiProp->page == indiDev->page->GetPage(i))
763             {
764                 indiDev->groups.erase(indiDev->page->GetPageText(i));
765                 indiDev->page->DeletePage(i);
766                 break;
767             }
768         }
769     }
770     delete indiProp;
771     indiDev->properties.erase(propname);
772     indiDev->page->Layout();
773     indiDev->page->Fit();
774     sizer->Layout();
775     Fit();
776     m_lastUpdate = wxGetUTCTimeMillis();
777 }
778 
ShowIndiGui(IndiGui ** ret,const wxString & host,long port,bool allow_connect_disconnect_,bool modal)779 void IndiGui::ShowIndiGui(IndiGui **ret, const wxString& host, long port, bool allow_connect_disconnect_, bool modal)
780 {
781     IndiGui *gui = new IndiGui();
782     gui->allow_connect_disconnect = allow_connect_disconnect_;
783     gui->ConnectServer(host, port);
784 
785     {
786         wxProgressDialog dlg(_("INDI"), _("Loading INDI properties..."), 0, nullptr, wxPD_APP_MODAL | wxPD_CAN_ABORT);
787 
788         enum { IDLE_TIME_MS = 500 };
789 
790         unsigned int i = 0;
791         while (wxGetUTCTimeMillis().GetValue() - gui->m_lastUpdate.GetValue() < IDLE_TIME_MS)
792         {
793             wxSafeYield();
794             wxMilliSleep(10);
795             if (dlg.WasCancelled())
796             {
797                 gui->Destroy();
798                 *ret = nullptr;
799                 return;
800             }
801             if (++i % 10 == 0)
802                 dlg.Pulse();
803         }
804     }
805 
806     gui->m_holder = ret;
807     *ret = gui;
808 
809     if (modal)
810         gui->ShowModal();
811     else
812         gui->Show();
813 }
814 
IndiGui()815 IndiGui::IndiGui()
816     :
817     wxDialog(wxGetApp().GetTopWindow(), wxID_ANY,
818              _("INDI Options"),
819              wxDefaultPosition, wxSize(640, 400), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
820     m_deleted(false),
821     m_holder(nullptr)
822 {
823     panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_DOUBLE | wxTAB_TRAVERSAL);
824     sizer = new wxBoxSizer(wxVERTICAL);
825     panel->SetSizer(sizer);
826     parent_notebook = new wxNotebook(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
827     sizer->Add(parent_notebook, 0, wxEXPAND | wxALL);
828     textbuffer = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
829     sizer->Add(textbuffer, 1, wxFIXED_MINSIZE | wxEXPAND | wxALL);
830 }
831 
OnQuit(wxCloseEvent & WXUNUSED (event))832 void IndiGui::OnQuit(wxCloseEvent& WXUNUSED(event))
833 {
834     if (isServerConnected())
835         Show(false);
836     else
837         Destroy();
838 }
839 
DestroyIndiGui(IndiGui ** holder)840 void IndiGui::DestroyIndiGui(IndiGui **holder)
841 {
842     IndiGui *gui = *holder;
843 
844     // disconnect gui from owner
845     assert(holder == gui->m_holder);
846     *gui->m_holder = nullptr;
847     gui->m_holder = nullptr;
848 
849     gui->Destroy();
850 }
851 
~IndiGui()852 IndiGui::~IndiGui()
853 {
854     // prevent recursive destruction when DisconnectIndiServer() calls
855     // serverDisconnected() which calls Destroy()
856     m_deleted = true;
857 
858     if (m_holder)
859         *m_holder = nullptr;
860 
861     DisconnectIndiServer();
862 
863     for (auto itdev = devlist.begin(); itdev != devlist.end(); ++itdev)
864     {
865         IndiDev *indiDev = (IndiDev *) itdev->second;
866         if (indiDev)
867         {
868             for (auto itprop = indiDev->properties.begin(); itprop != indiDev->properties.end(); ++itprop)
869             {
870                 IndiProp *indiProp = (IndiProp *) itprop->second;
871                 indiProp->ctrl.clear();
872                 indiProp->entry.clear();
873                 delete indiProp;
874             }
875             indiDev->properties.clear();
876             indiDev->groups.clear();
877             delete indiDev;
878         }
879     }
880 }
881