1 /***************************************************************************
2 stationdial.cpp - description
3 -------------------
4 begin : Mon Nov 11 2002
5 copyright : (C) 2002 by ARRL
6 author : Jon Bloom
7 email : jbloom@arrl.org
8 revision : $Id: stationdial.cpp,v 1.6 2013/03/01 13:00:59 k1mu Exp $
9 ***************************************************************************/
10
11 #define TQSL_ID_LOW 6000
12
13 #include "stationdial.h"
14 #include <wx/listctrl.h>
15 #include <algorithm>
16 #include <iostream>
17 #include <map>
18 #ifdef HAVE_CONFIG_H
19 #include "sysconfig.h"
20 #endif
21 #include "tqslwiz.h"
22 #include "tqslexcept.h"
23 #include "tqsltrace.h"
24 #include "tqsllib.h"
25 #include "wxutil.h"
26
27 using std::map;
28
29 #define GS_NAMELIST TQSL_ID_LOW
30 #define GS_OKBUT TQSL_ID_LOW+1
31 #define GS_CANCELBUT TQSL_ID_LOW+2
32 #define GS_NAMEENTRY TQSL_ID_LOW+3
33 #define GS_DELETEBUT TQSL_ID_LOW+4
34 #define GS_NEWBUT TQSL_ID_LOW+5
35 #define GS_MODIFYBUT TQSL_ID_LOW+6
36 #define GS_CMD_PROPERTIES TQSL_ID_LOW+7
37 #define GS_HELPBUT TQSL_ID_LOW+8
38
39 class TQSLStationListBox : public wxListBox {
40 public:
TQSLStationListBox(wxWindow * parent,wxWindowID id,const wxPoint & pos=wxDefaultPosition,const wxSize & size=wxDefaultSize,int n=0,const wxString choices[]=NULL,long style=0)41 TQSLStationListBox(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
42 const wxSize& size = wxDefaultSize, int n = 0, const wxString choices[] = NULL,
43 long style = 0) : wxListBox(parent, id, pos, size, n, choices, style) {}
44 void OnRightDown(wxMouseEvent& event);
45
46 DECLARE_EVENT_TABLE()
47 };
48
BEGIN_EVENT_TABLE(TQSLStationListBox,wxListBox)49 BEGIN_EVENT_TABLE(TQSLStationListBox, wxListBox)
50 EVT_RIGHT_DOWN(TQSLStationListBox::OnRightDown)
51 END_EVENT_TABLE()
52
53 void
54 TQSLStationListBox::OnRightDown(wxMouseEvent& event) {
55 wxMenu menu;
56 menu.Append(GS_CMD_PROPERTIES, _("&Properties"));
57 PopupMenu(&menu, event.GetPosition());
58 }
59
60 class PropList : public wxDialog {
61 public:
62 explicit PropList(wxWindow *parent);
63 wxListCtrl *list;
64 };
65
PropList(wxWindow * parent)66 PropList::PropList(wxWindow *parent) : wxDialog(parent, -1, _("Properties"), wxDefaultPosition,
67 wxSize(400, 300)) {
68 tqslTrace("PropList::PropList", "parent=0x%lx", reinterpret_cast<void *>(parent));
69 list = new wxListCtrl(this, -1, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_SINGLE_SEL);
70 list->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 100);
71 list->InsertColumn(1, _("Value"), wxLIST_FORMAT_LEFT, 300);
72 wxLayoutConstraints *c = new wxLayoutConstraints;
73 c->top.SameAs(this, wxTop);
74 c->left.SameAs(this, wxLeft);
75 c->right.SameAs(this, wxRight);
76 c->height.PercentOf(this, wxHeight, 66);
77 list->SetConstraints(c);
78 CenterOnParent();
79 }
80
81 static void
check_tqsl_error(int rval)82 check_tqsl_error(int rval) {
83 if (rval == 0)
84 return;
85 wxString serr = getLocalizedErrorString();
86 wxLogError(serr);
87 }
88
89 static bool
itemLess(const item & s1,const item & s2)90 itemLess(const item& s1, const item& s2) {
91 int i = s1.call.CmpNoCase(s2.call);
92 if (i == 0)
93 i = s1.name.CmpNoCase(s2.name);
94 return (i < 0);
95 }
96
BEGIN_EVENT_TABLE(TQSLGetStationNameDialog,wxDialog)97 BEGIN_EVENT_TABLE(TQSLGetStationNameDialog, wxDialog)
98 EVT_BUTTON(GS_OKBUT, TQSLGetStationNameDialog::OnOk)
99 EVT_BUTTON(GS_CANCELBUT, TQSLGetStationNameDialog::OnCancel)
100 EVT_BUTTON(GS_DELETEBUT, TQSLGetStationNameDialog::OnDelete)
101 EVT_BUTTON(GS_HELPBUT, TQSLGetStationNameDialog::OnHelp)
102 EVT_BUTTON(GS_NEWBUT, TQSLGetStationNameDialog::OnNew)
103 EVT_BUTTON(GS_MODIFYBUT, TQSLGetStationNameDialog::OnModify)
104 EVT_LISTBOX(GS_NAMELIST, TQSLGetStationNameDialog::OnNamelist)
105 EVT_LISTBOX_DCLICK(GS_NAMELIST, TQSLGetStationNameDialog::OnDblClick)
106 EVT_TEXT(GS_NAMEENTRY, TQSLGetStationNameDialog::OnNameChange)
107 EVT_MENU(GS_CMD_PROPERTIES, TQSLGetStationNameDialog::DisplayProperties)
108 EVT_SET_FOCUS(TQSLGetStationNameDialog::OnSetFocus)
109 END_EVENT_TABLE()
110
111 void
112 TQSLGetStationNameDialog::OnSetFocus(wxFocusEvent& event) {
113 if (!firstFocused) {
114 firstFocused = true;
115 if (issave) {
116 if (name_entry)
117 name_entry->SetFocus();
118 } else if (namelist) {
119 namelist->SetFocus();
120 }
121 }
122 }
123
124 void
OnOk(wxCommandEvent &)125 TQSLGetStationNameDialog::OnOk(wxCommandEvent&) {
126 wxString s = name_entry->GetValue().Trim().Trim(false);
127 tqslTrace("TQSLGetStationNameDialog::OnOk", "selected = %s", S(s));
128 if (editonly) {
129 EndModal(wxID_CANCEL);
130 } else if (s != wxT("")) {
131 _selected = s;
132 EndModal(wxID_OK);
133 }
134 }
135
136 void
OnCancel(wxCommandEvent &)137 TQSLGetStationNameDialog::OnCancel(wxCommandEvent&) {
138 tqslTrace("TQSLGetStationNameDialog::OnCancel", NULL);
139 EndModal(wxID_CANCEL);
140 }
141
142 // Reset the list of Station Locations in the listbox
143 void
RefreshList()144 TQSLGetStationNameDialog::RefreshList() {
145 tqslTrace("TQSLGetStationNameDialog::RefreshList", NULL);
146 namelist->Clear();
147 item_data.clear();
148 int n;
149 tQSL_Location loc;
150 check_tqsl_error(tqsl_initStationLocationCapture(&loc));
151 check_tqsl_error(tqsl_getNumStationLocations(loc, &n));
152 for (int i = 0; i < n && i < 2000; i++) {
153 item it;
154 char buf[256];
155 check_tqsl_error(tqsl_getStationLocationName(loc, i, buf, sizeof buf));
156 it.name = wxString::FromUTF8(buf);
157 char cbuf[256];
158 check_tqsl_error(tqsl_getStationLocationCallSign(loc, i, cbuf, sizeof cbuf));
159 it.call = wxString::FromUTF8(cbuf);
160 it.label = it.call + wxT(" - ") + it.name;
161 item_data.push_back(it);
162 }
163 sort(item_data.begin(), item_data.end(), itemLess);
164 for (int i = 0; i < static_cast<int>(item_data.size()); i++)
165 namelist->Append(item_data[i].label, &(item_data[i].name));
166 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
167 }
168
TQSLGetStationNameDialog(wxWindow * parent,wxHtmlHelpController * help,const wxPoint & pos,bool i_issave,const wxString & title,const wxString & okLabel,bool i_editonly)169 TQSLGetStationNameDialog::TQSLGetStationNameDialog(wxWindow *parent, wxHtmlHelpController *help, const wxPoint& pos,
170 bool i_issave, const wxString& title, const wxString& okLabel, bool i_editonly)
171 : wxDialog(parent, -1, _("Select Station Data"), pos), issave(i_issave), editonly(i_editonly),
172 newbut(0), modbut(0), updating(false), firstFocused(false), _help(help) {
173 tqslTrace("TQSLGetStationNameDialog::TQSLGetStationNameDialog", "parent=0x%lx, i_issave=%d, title=%s, okLabel=%s, i_editonly=%d", parent, i_issave, S(title), S(okLabel), i_editonly);
174 wxBoxSizer *topsizer = new wxBoxSizer(wxHORIZONTAL);
175 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
176 wxSize text_size = getTextSize(this);
177 int control_width = text_size.GetWidth()*30;
178
179 if (title != wxT(""))
180 SetTitle(title);
181 else if (issave)
182 SetTitle(_("Save Station Data"));
183 // List
184 namelist = new TQSLStationListBox(this, GS_NAMELIST, wxDefaultPosition,
185 wxSize(control_width, text_size.GetHeight()*10),
186 0, 0, wxLB_MULTIPLE|wxLB_HSCROLL|wxLB_ALWAYS_SB);
187 sizer->Add(namelist, 1, wxALL|wxEXPAND, 10);
188 RefreshList();
189 sizer->Add(new wxStaticText(this, -1,
190 issave ? _("Enter a name for this Station Location") : _("Selected Station Location")),
191 0, wxLEFT|wxRIGHT|wxTOP|wxEXPAND, 10);
192 name_entry = new wxTextCtrl(this, GS_NAMEENTRY, wxT(""), wxDefaultPosition,
193 wxSize(control_width, -1));
194 if (!issave)
195 name_entry->Enable(false);
196 sizer->Add(name_entry, 0, wxALL|wxEXPAND, 10);
197 topsizer->Add(sizer, 1, 0, 0);
198 wxBoxSizer *button_sizer = new wxBoxSizer(wxVERTICAL);
199 if (!issave) {
200 newbut = new wxButton(this, GS_NEWBUT, _("New..."));
201 newbut->Enable(TRUE);
202 button_sizer->Add(newbut, 0, wxALL|wxALIGN_TOP, 3);
203 modbut = new wxButton(this, GS_MODIFYBUT, _("Edit..."));
204 modbut->Enable(FALSE);
205 button_sizer->Add(modbut, 0, wxALL|wxALIGN_TOP, 3);
206 }
207 delbut = new wxButton(this, GS_DELETEBUT, _("Delete"));
208 delbut->Enable(FALSE);
209 button_sizer->Add(delbut, 0, wxALL|wxALIGN_TOP, 3);
210 button_sizer->Add(new wxStaticText(this, -1, wxT("")), 1, wxEXPAND);
211 if (_help)
212 button_sizer->Add(new wxButton(this, GS_HELPBUT, _("Help") ), 0, wxALL|wxALIGN_BOTTOM, 3);
213 if (!editonly)
214 button_sizer->Add(new wxButton(this, GS_CANCELBUT, _("Cancel") ), 0, wxALL|wxALIGN_BOTTOM, 3);
215 okbut = new wxButton(this, GS_OKBUT, okLabel);
216 button_sizer->Add(okbut, 0, wxALL|wxALIGN_BOTTOM, 3);
217 topsizer->Add(button_sizer, 0, wxTOP|wxBOTTOM|wxRIGHT|wxEXPAND, 7);
218 hack = (namelist->GetCount() > 0) ? true : false;
219
220 UpdateControls();
221 SetAutoLayout(TRUE);
222 SetSizer(topsizer);
223
224 topsizer->Fit(this);
225 topsizer->SetSizeHints(this);
226 CentreOnParent();
227 }
228
229 void
UpdateButtons()230 TQSLGetStationNameDialog::UpdateButtons() {
231 tqslTrace("TQSLGetStationNameDialog::UpdateButtons", NULL);
232 wxArrayInt newsels;
233 namelist->GetSelections(newsels);
234 delbut->Enable(newsels.GetCount() > 0);
235 if (modbut)
236 modbut->Enable(newsels.GetCount() > 0);
237 wxArrayInt sels;
238 namelist->GetSelections(sels);
239 if (!editonly)
240 okbut->Enable(issave ? (name_entry->GetValue().Trim() != wxT("")) : (sels.GetCount() > 0));
241 }
242
243 void
UpdateControls()244 TQSLGetStationNameDialog::UpdateControls() {
245 tqslTrace("TQSLGetStationNameDialog::UpdateControls", NULL);
246 if (updating) // Sentinel to prevent recursion
247 return;
248 updating = true;
249 wxArrayInt newsels;
250 namelist->GetSelections(newsels);
251 int newsel = -1;
252 for (int i = 0; i < static_cast<int>(newsels.GetCount()); i++) {
253 if (sels.Index(newsels[i]) == wxNOT_FOUND) {
254 newsel = newsels[i];
255 break;
256 }
257 }
258 //cout << "newsel: " << newsel << endl;
259 if (newsel > -1) {
260 for (int i = 0; i < static_cast<int>(newsels.GetCount()); i++) {
261 if (newsels[i] != newsel)
262 namelist->Deselect(newsels[i]);
263 }
264 }
265 namelist->GetSelections(sels);
266 int idx = (sels.GetCount() > 0) ? sels[0] : -1;
267 //cout << "UpdateControls selection: " << idx << endl;
268 if (idx >= 0)
269 name_entry->SetValue((idx < 0) ? wxT("") : *reinterpret_cast<wxString *>(namelist->GetClientData(idx)));
270 UpdateButtons();
271 //cout << "UpdateControls selection(1): " << idx << endl;
272 updating = false;
273 }
274
275 void
OnDelete(wxCommandEvent &)276 TQSLGetStationNameDialog::OnDelete(wxCommandEvent&) {
277 tqslTrace("TQSLGetStationNameDialog::OnDelete", NULL);
278 wxArrayInt newsels;
279 namelist->GetSelections(newsels);
280 int idx = (newsels.GetCount() > 0) ? newsels[0] : -1;
281 if (idx < 0)
282 return;
283 wxString name = *reinterpret_cast<wxString *>(namelist->GetClientData(idx));
284 if (name == wxT(""))
285 return;
286 if (wxMessageBox(wxString(_("Delete \"")) + name + wxT("\"?"), _("TQSL Confirm"), wxYES_NO | wxICON_QUESTION | wxCENTRE, this) == wxYES) {
287 check_tqsl_error(tqsl_deleteStationLocation(name.ToUTF8()));
288 if (!issave)
289 name_entry->Clear();
290 RefreshList();
291 UpdateControls();
292 }
293 }
294
295 void
OnNamelist(wxCommandEvent & event)296 TQSLGetStationNameDialog::OnNamelist(wxCommandEvent& event) {
297 tqslTrace("TQSLGetStationNameDialog::OnNamelist", NULL);
298 /* if (hack) { // We seem to get an extraneous start-up event (GTK only?)
299 cout << "OnNamelist hack" << endl;
300 hack = false;
301 if (want_selected < 0)
302 namelist->Deselect(0);
303 else
304 namelist->SetSelection(want_selected);
305 }
306 */
307 //cout << "OnNamelist" << endl;
308 UpdateControls();
309 }
310
311 void
OnDblClick(wxCommandEvent & event)312 TQSLGetStationNameDialog::OnDblClick(wxCommandEvent& event) {
313 tqslTrace("TQSLGetStationNameDialog::OnDblClick", NULL);
314 if(editonly) {
315 OnNamelist(event);
316 EndModal(wxID_MORE);
317 } else {
318 UpdateControls();
319 OnOk(event);
320 } //updatecontrols sets up the text field that ok checks
321 }
322
323 void
OnNew(wxCommandEvent &)324 TQSLGetStationNameDialog::OnNew(wxCommandEvent&) {
325 tqslTrace("TQSLGetStationNameDialog::OnNew", NULL);
326 EndModal(wxID_APPLY);
327 }
328
329 void
OnModify(wxCommandEvent &)330 TQSLGetStationNameDialog::OnModify(wxCommandEvent&) {
331 tqslTrace("TQSLGetStationNameDialog::OnModify", NULL);
332 EndModal(wxID_MORE);
333 }
334
335 void
OnHelp(wxCommandEvent &)336 TQSLGetStationNameDialog::OnHelp(wxCommandEvent&) {
337 tqslTrace("TQSLGetStationNameDialog::OnHelp", NULL);
338 if (_help)
339 _help->Display(wxT("stnloc.htm"));
340 }
341
342 void
OnNameChange(wxCommandEvent &)343 TQSLGetStationNameDialog::OnNameChange(wxCommandEvent&) {
344 tqslTrace("TQSLGetStationNameDialog::OnNameChange", NULL);
345 UpdateButtons();
346 }
347
348 // Location fields, here for translation purposes
349 #ifdef tqsltranslate
350 static const char* labels[] = {
351 __("State"),
352 __("Call Sign"),
353 __("Province"),
354 __("Continent"),
355 __("CQ Zone"),
356 __("DXCC Entity"),
357 __("Grid Square"),
358 __("IOTA ID"),
359 __("ITU Zone"),
360 __("Oblast"),
361 __("County"),
362 __("State"),
363 __("WPX Prefix")
364 }
365 #endif
366
367 void
368 TQSLGetStationNameDialog::DisplayProperties(wxCommandEvent&) {
369 tqslTrace("TQSLGetStationNameDialog::DisplayProperties", NULL);
370 wxArrayInt newsels;
371 namelist->GetSelections(newsels);
372 int idx = (newsels.GetCount() > 0) ? newsels[0] : -1;
373 if (idx < 0)
374 return;
375 wxString name = *reinterpret_cast<wxString *>(namelist->GetClientData(idx));
376 if (name == wxT(""))
377 return;
378 tQSL_Location loc;
379 try {
380 map<wxString, wxString> props;
381 check_tqsl_error(tqsl_getStationLocation(&loc, name.ToUTF8()));
382 do {
383 int nfield;
384 check_tqsl_error(tqsl_getNumLocationField(loc, &nfield));
385 for (int i = 0; i < nfield; i++) {
386 char buf[256];
387 check_tqsl_error(tqsl_getLocationFieldDataLabel(loc, i, buf, sizeof buf));
388 wxString key = wxGetTranslation(wxString::FromUTF8(buf));
389 int type;
390 check_tqsl_error(tqsl_getLocationFieldDataType(loc, i, &type));
391 if (type == TQSL_LOCATION_FIELD_DDLIST || type == TQSL_LOCATION_FIELD_LIST) {
392 int sel;
393 check_tqsl_error(tqsl_getLocationFieldIndex(loc, i, &sel));
394 check_tqsl_error(tqsl_getLocationFieldListItem(loc, i, sel, buf, sizeof buf));
395 } else {
396 check_tqsl_error(tqsl_getLocationFieldCharData(loc, i, buf, sizeof buf));
397 }
398 props[key] = wxString::FromUTF8(buf);
399 }
400 int rval;
401 if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval)
402 break;
403 check_tqsl_error(tqsl_nextStationLocationCapture(loc));
404 } while (1);
405 check_tqsl_error(tqsl_endStationLocationCapture(&loc));
406 PropList plist(this);
407 int i = 0;
408 for (map<wxString, wxString>::iterator it = props.begin(); it != props.end(); it++) {
409 plist.list->InsertItem(i, it->first);
410 plist.list->SetItem(i, 1, it->second);
411 i++;
412 //cout << idx << ", " << it->first << " => " << it->second << endl;
413 }
414 plist.ShowModal();
415 }
416 catch(TQSLException& x) {
417 wxLogError(wxT("%hs"), x.what());
418 }
419 }
420
421 void
SelectName(const wxString & name)422 TQSLGetStationNameDialog::SelectName(const wxString& name) {
423 tqslTrace("TQSLGetStationNameDialog::SelectName", "name=%s", S(name));
424 wxArrayInt sels;
425 namelist->GetSelections(sels);
426 for (int i = 0; i < static_cast<int>(sels.GetCount()); i++)
427 namelist->Deselect(sels[i]);
428 for (int i = 0; i < static_cast<int>(namelist->GetCount()); i++) {
429 if (name == *reinterpret_cast<wxString *>(namelist->GetClientData(i))) {
430 namelist->SetSelection(i, TRUE);
431 break;
432 }
433 }
434 namelist->GetSelections(sels);
435 UpdateControls();
436 }
437
438 int
ShowModal()439 TQSLGetStationNameDialog::ShowModal() {
440 tqslTrace("TQSLGetStationNameDialog::ShowModal", NULL);
441 if (namelist->GetCount() == 0 && !issave) {
442 wxString msg = _("You have no Station Locations defined.");
443 msg += wxT("\n\n");
444 msg += _("You must define at least one Station Location to use for signing.");
445 msg += wxT("\n");
446 msg += _("Use the \"New\" Button of the dialog you're about to see to define a Station Location.");
447 wxMessageBox(msg, _("TQSL Warning"), wxOK | wxICON_WARNING, this);
448 }
449 return wxDialog::ShowModal();
450 }
451