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