1 // ----------------------------------------------------------------------------
2 // configuration.cxx
3 //
4 // Copyright (C) 2006-2010
5 //		Dave Freese, W1HKJ
6 // Copyright (C) 2007-2008
7 //		Leigh L. Klotz, Jr., WA5ZNU
8 // Copyright (C) 2007-2010
9 //		Stelios Bounanos, M0GLD
10 // Copyright (C) 2013
11 //		Remi Chateauneu, F4ECW
12 //
13 // This file is part of fldigi.
14 //
15 // Fldigi is free software: you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation, either version 3 of the License, or
18 // (at your option) any later version.
19 //
20 // Fldigi is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
27 // ----------------------------------------------------------------------------
28 
29 #include <config.h>
30 
31 #include "configuration.h"
32 #include "confdialog.h"
33 #include "xmlreader.h"
34 #include "soundconf.h"
35 #include "fl_digi.h"
36 #include "main.h"
37 #include "gettext.h"
38 #include "nls.h"
39 #include "icons.h"
40 #include "rigsupport.h"
41 #include "contest.h"
42 
43 #if USE_HAMLIB
44 	#include "hamlib.h"
45 	#include "rigclass.h"
46 #endif
47 
48 #include "rigio.h"
49 #include "rigxml.h"
50 #include "nanoIO.h"
51 #include "debug.h"
52 
53 #include <FL/Fl_Tooltip.H>
54 
55 #include <unistd.h>
56 #include <iostream>
57 #include <fstream>
58 #include <map>
59 #include <sstream>
60 
61 #ifdef __linux__
62 #  include <dirent.h>
63 #  include <limits.h>
64 #  include <errno.h>
65 #  include <glob.h>
66 #endif
67 #ifdef __APPLE__
68 #  include <glob.h>
69 #endif
70 #ifndef __CYGWIN__
71 #  include <sys/stat.h>
72 #else
73 #  include <fcntl.h>
74 #endif
75 
76 // this tests depends on a modified FL/filename.H in the Fltk-1.3.0
77 // change
78 //#  if defined(WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
79 // to
80 //#  if defined(WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) && !defined(__WOE32__)
81 
82 #include <dirent.h>
83 
84 using namespace std;
85 
86 
87 const char *szBaudRates[] = {
88 	"",
89 	"300","600","1200","2400",
90 	"4800","9600","19200","38400",
91 	"57600","115200","230400","460800"};
92 
93 const char *szBands[] = {
94 	"",
95 	"1830", "3580", "7030", "7070", "10138",
96 	"14070", "18100", "21070", "21080", "24920", "28070", "28120", 0};
97 
operator <<(ostream & out,const RGB & rgb)98 ostream& operator<<(ostream& out, const RGB& rgb)
99 {
100 	return out << (int)rgb.R << ' ' << (int)rgb.G << ' ' << (int)rgb.B;
101 }
operator >>(istream & in,RGB & rgb)102 istream& operator>>(istream& in, RGB& rgb)
103 {
104 	int i;
105 	in >> i; rgb.R = i;
106 	in >> i; rgb.G = i;
107 	in >> i; rgb.B = i;
108 	return 	in;
109 
110 }
operator <<(ostream & out,const RGBI & rgbi)111 ostream& operator<<(ostream& out, const RGBI& rgbi)
112 {
113 	return out << (int)rgbi.R << ' ' << (int)rgbi.G << ' ' << (int)rgbi.B;
114 }
operator >>(istream & in,RGBI & rgbi)115 istream& operator>>(istream& in, RGBI& rgbi)
116 {
117 	int i;
118 	in >> i; rgbi.R = i;
119 	in >> i; rgbi.G = i;
120 	in >> i; rgbi.B = i;
121 	return 	in;
122 }
123 
124 // This allows to put tag elements into containers
125 class tag_base
126 {
127 public:
tag_base(const char * t,const char * d="")128 	tag_base(const char* t, const char* d = "") : tag(t), doc(d) { }
129 	virtual void write(ostream& out) const = 0;
130 	virtual void read(const char* data) = 0;
~tag_base()131 	virtual ~tag_base() { }
132 	const char* tag;
133 	const char* doc;
134 };
135 
136 // This will handle every type that has << and >> stream operators
137 template <typename T>
138 class tag_elem : public tag_base
139 {
140 public:
tag_elem(const char * t,const char * d,T & v)141 	tag_elem(const char* t, const char* d, T& v) : tag_base(t, d), var(v) { }
write(ostream & out) const142 	void write(ostream& out) const
143         {
144 		out << "<!-- " << doc << " -->\n"
145 		    << '<' << tag << '>' << var << "</" << tag << ">\n\n";
146 	}
read(const char * data)147 	void read(const char* data)
148 	{
149 		istringstream iss(data);
150 		iss >> var;
151 	}
152 	T& var;
153 };
154 
155 // Instantiate an explicit tag_elem<T> for types that require unusual handling.
156 
157 // Special handling for strings
158 template <>
159 class tag_elem<string> : public tag_base
160 {
161 public:
tag_elem(const char * t,const char * d,string & s)162 	tag_elem(const char* t, const char* d, string& s) : tag_base(t, d), str(s) { }
write(ostream & out) const163 	void write(ostream& out) const
164         {
165 		string s = str;
166 		string s2 = doc;
167 
168 		string::size_type i = s.find('&');
169 		while (i != string::npos) {
170 			s.replace(i, 1, "&amp;");
171 			i = s.find('&', i + 1);
172 		}
173 		while ((i = s.find('<')) != string::npos)
174 			s.replace(i, 1, "&lt;");
175 		while ((i = s.find('>')) != string::npos)
176 			s.replace(i, 1, "&gt;");
177 		while ((i = s.find('"')) != string::npos)
178 			s.replace(i, 1, "&quot;");
179 		while ((i = s.find('\'')) != string::npos)
180 			s.replace(i, 1, "&apos;");
181 
182 		i = s2.find('&');
183 		while (i != string::npos) {
184 			s2.replace(i, 1, "&amp;");
185 			i = s2.find('&', i + 1);
186 		}
187 		while ((i = s2.find('<')) != string::npos)
188 			s2.replace(i, 1, "&lt;");
189 		while ((i = s2.find('>')) != string::npos)
190 			s2.replace(i, 1, "&gt;");
191 		while ((i = s2.find('"')) != string::npos)
192 			s2.replace(i, 1, "&quot;");
193 		while ((i = s2.find('\'')) != string::npos)
194 			s2.replace(i, 1, "&apos;");
195 
196 		out << "<!-- " << s2 << " -->\n"
197 		    << '<' << tag << '>' << s << "</" << tag << ">\n\n";
198 	}
read(const char * data)199 	void read(const char* data) { str = data; }
200 	string& str;
201 };
202 
203 // Special handling for mode bitsets
204 template <>
205 class tag_elem<mode_set_t> : public tag_base
206 {
207 public:
tag_elem(const char * t,const char * d,mode_set_t & m)208 	tag_elem(const char* t, const char* d, mode_set_t& m) : tag_base(t, d), modes(m) { }
write(ostream & out) const209 	void write(ostream& out) const
210         {
211 		out << "<!-- " << doc << " -->\n" << '<' << tag << '>';
212 		for (size_t i = 0; i < modes.size(); i++) {
213 			if (!modes.test(i))
214 				out << mode_info[i].name << ',';
215 		}
216 		out << ",</" << tag << ">\n\n";
217 	}
read(const char * data)218 	void read(const char* data)
219 	{
220 		string sdata = data, smode, tstmode;
221 		modes.set();
222 		size_t p = sdata.find(",");
223 		while ((p != string::npos) && (p != 0)) {
224 			smode = sdata.substr(0, p);
225 
226 			for (size_t i = 0; i < modes.size(); i++) {
227 				tstmode = mode_info[i].name;
228 				if (smode == tstmode) {
229 					modes.set(i,0);
230 					break;
231 				}
232 			}
233 			sdata.erase(0, p+1);
234 			p = sdata.find(",");
235 		}
236 	}
237 	mode_set_t& modes;
238 };
239 
240 // By redefining the ELEM_ macro, we can control what the CONFIG_LIST macro
241 // will expand to, and accomplish several things:
242 // 1) Declare "struct configuration". See ELEM_DECLARE_CONFIGURATION
243 //    in configuration.h.
244 // 2) Define progdefaults, the configuration struct that is initialised with
245 //    fldigi's default options
246 #define ELEM_PROGDEFAULTS(type_, var_, tag_, doc_, ...) __VA_ARGS__,
247 // 3) Define an array of tag element pointers
248 #define ELEM_TAG_ARRAY(type_, var_, tag_, doc_, ...)                                             \
249         (*tag_ ? new tag_elem<type_>(tag_, "type: " #type_ "; default: " #__VA_ARGS__ "\n" doc_, \
250                                      progdefaults.var_) : 0),
251 
252 // First define the default config
253 #undef ELEM_
254 #define ELEM_ ELEM_PROGDEFAULTS
255 configuration progdefaults = { CONFIG_LIST };
256 
257 
writeDefaultsXML()258 void configuration::writeDefaultsXML()
259 {
260 	string deffname(HomeDir);
261 	deffname.append("fldigi_def.xml");
262 
263 	string deffname_backup(deffname);
264 	deffname_backup.append("-old");
265 	remove(deffname_backup.c_str());
266 	rename(deffname.c_str(), deffname_backup.c_str());
267 
268 	ofstream f(deffname.c_str());
269 	if (!f) {
270 		LOG_ERROR("Could not write %s", deffname.c_str());
271 		return;
272 	}
273 
274 	// create an array
275 #undef ELEM_
276 #define ELEM_ ELEM_TAG_ARRAY
277 	tag_base* tag_list[] = { CONFIG_LIST };
278 
279 	// write all variables with non-empty tags to f
280 	f << "<FLDIGI_DEFS>\n\n";
281 	for (size_t i = 0; i < sizeof(tag_list)/sizeof(*tag_list); i++) {
282 		if (tag_list[i]) {
283 			tag_list[i]->write(f);
284 			delete tag_list[i];
285 		}
286 	}
287 	f << "</FLDIGI_DEFS>\n";
288 	f.close();
289 }
290 
log_excluded_modes(void)291 static void log_excluded_modes(void)
292 {
293 return;
294 	struct {
295 		mode_set_t* modes;
296 		const char* msgstr;
297 	} excluded[] = {
298 		{ &progdefaults.rsid_rx_modes, "RSID (rx)" },
299 		{ &progdefaults.rsid_tx_modes, "RSID (tx)" },
300 		{ &progdefaults.cwid_modes, "CWID" },
301 		{ &progdefaults.videoid_modes, "VIDEOID" }
302 	};
303 	string buf;
304 	for (size_t i = 0; i < sizeof(excluded)/sizeof(*excluded); i++) {
305 		size_t n = excluded[i].modes->size();
306 		if (excluded[i].modes->count() == n)
307 			continue;
308 		buf.erase();
309 		for (size_t j = 0; j < n; j++) {
310 			if (!excluded[i].modes->test(j)) {
311 				if (!buf.empty())
312 					buf += ' ';
313 				buf += mode_info[j].sname;
314 			}
315 		}
316 		LOG(debug::QUIET_LEVEL, debug::LOG_OTHER, "%-10s: %s", excluded[i].msgstr, buf.c_str());
317 	}
318 }
319 
readDefaultsXML()320 bool configuration::readDefaultsXML()
321 {
322 	// Decode all RSID modes
323 	rsid_rx_modes.set();
324 	// Don't transmit RSID or VideoID for CW, PSK31, RTTY
325 	rsid_tx_modes.set().reset(MODE_CW).reset(MODE_PSK31).reset(MODE_RTTY);
326 	videoid_modes = rsid_tx_modes;
327 	// Don't transmit CWID for CW
328 	cwid_modes.set().reset(MODE_CW);
329 	// Show all op modes
330 	visible_modes.set();
331 
332 	string deffname = HomeDir;
333 	deffname.append("fldigi_def.xml");
334 	ifstream f(deffname.c_str());
335 	if (!f)
336 		return false;
337 
338 	string xmlbuf;
339 
340 	f.seekg(0, ios::end);
341 	xmlbuf.reserve(f.tellg()); // reserve some space to avoid reallocations
342 	f.seekg(0, ios::beg);
343 
344 	char line[2048];
345 	while (f.getline(line, sizeof(line)))
346 		xmlbuf.append(line).append("\n");
347 	f.close();
348 
349 	IrrXMLReader* xml = createIrrXMLReader(new IIrrXMLStringReader(xmlbuf));
350 	if (!xml)
351 		return false;
352 
353 	// create a TAG_NAME -> ELEMENT map
354 	typedef map<string, tag_base*> tag_map_t;
355 	tag_map_t tag_map;
356 
357 	tag_base* tag_list[] = { CONFIG_LIST };
358 	for (size_t i = 0; i < sizeof(tag_list)/sizeof(*tag_list); i++)
359 		if (tag_list[i])
360 			tag_map[tag_list[i]->tag] = tag_list[i];
361 
362 	// parse the xml buffer
363 	tag_map_t::const_iterator i = tag_map.end();
364 	while(xml->read()) {
365 		switch(xml->getNodeType()) {
366 		case EXN_TEXT:
367 		case EXN_CDATA:
368 			if (i != tag_map.end()) // do we know about this tag?
369 				i->second->read(xml->getNodeData());
370 			break;
371 		case EXN_ELEMENT_END:
372 			i = tag_map.end(); // ignore the next EXN_CDATA
373 			break;
374 		case EXN_ELEMENT:
375 			i = tag_map.find(xml->getNodeName());
376 			break;
377 		case EXN_NONE: case EXN_COMMENT: case EXN_UNKNOWN:
378 			break;
379 		}
380 	}
381 
382 	delete xml;
383 	// delete the tag objects
384 	for (size_t i = 0; i < sizeof(tag_list)/sizeof(*tag_list); i++)
385 		delete tag_list[i];
386 
387 	log_excluded_modes();
388 
389 	return true;
390 }
391 
loadDefaults()392 void configuration::loadDefaults()
393 {
394 // RTTY
395 	selShift->index(rtty_shift);
396 	if (progdefaults.rtty_shift == selShift->lsize() - 1) {
397 		selCustomShift->activate();
398 		selCustomShift->value(rtty_custom_shift);
399 	} else
400 		selCustomShift->deactivate();
401 	selBaud->index(rtty_baud);
402 	selBits->index(rtty_bits);
403 	selParity->index(rtty_parity);
404 //	chkMsbFirst->value(rtty_msbfirst);
405 	selStopBits->index(rtty_stop);
406 	btnCRCRLF->value(rtty_crcrlf);
407 	btnAUTOCRLF->value(rtty_autocrlf);
408 	cntrAUTOCRLF->value(rtty_autocount);
409 	chkPseudoFSK->value(PseudoFSK);
410 	chkUOSrx->value(UOSrx);
411 	chkUOStx->value(UOStx);
412 
413 	i_listbox_rtty_afc_speed->index(rtty_afcspeed);
414 	btnPreferXhairScope->value(PreferXhairScope);
415 
416 // OLIVIA
417 	i_listbox_olivia_tones->index(oliviatones);
418 	i_listbox_olivia_bandwidth->index(oliviabw);
419 	cntOlivia_smargin->value(oliviasmargin);
420 	cntOlivia_sinteg->value(oliviasinteg);
421 	btnOlivia_8bit->value(olivia8bit);
422 
423 // CONTESTIA
424 	i_listbox_contestia_tones->index(contestiatones);
425 	i_listbox_contestia_bandwidth->index(contestiabw);
426 	cntContestia_smargin->value(contestiasmargin);
427 	cntContestia_sinteg->value(contestiasinteg);
428 	btnContestia_8bit->value(contestia8bit);
429 
430 	chkDominoEX_FEC->value(DOMINOEX_FEC);
431 
432 	Fl_Tooltip::enable(tooltips);
433 
434 // contest settings for state qso parties
435 	adjust_for_contest(0);
436 
437 	UI_select();
438 	set_log_colors();
439 	clear_log_fields();
440 	clearQSO();
441 }
442 
saveDefaults()443 void configuration::saveDefaults()
444 {
445 	ENSURE_THREAD(FLMAIN_TID);
446 
447 	memcpy(&cfgpal0, &palette[0], sizeof(cfgpal0));
448 	memcpy(&cfgpal1, &palette[1], sizeof(cfgpal1));
449 	memcpy(&cfgpal2, &palette[2], sizeof(cfgpal2));
450 	memcpy(&cfgpal3, &palette[3], sizeof(cfgpal3));
451 	memcpy(&cfgpal4, &palette[4], sizeof(cfgpal4));
452 	memcpy(&cfgpal5, &palette[5], sizeof(cfgpal5));
453 	memcpy(&cfgpal6, &palette[6], sizeof(cfgpal6));
454 	memcpy(&cfgpal7, &palette[7], sizeof(cfgpal7));
455 	memcpy(&cfgpal8, &palette[8], sizeof(cfgpal8));
456 
457 	RxFontName = Fl::get_font_name(RxFontnbr);
458 	TxFontName = Fl::get_font_name(TxFontnbr);
459 
460 	WaterfallFontName = Fl::get_font_name(WaterfallFontnbr);
461 
462 	ViewerFontName = Fl::get_font_name(ViewerFontnbr);
463 
464 	FreqControlFontName = Fl::get_font_name(FreqControlFontnbr);
465 
466 	MacroEditFontName = Fl::get_font_name(MacroEditFontnbr);
467 	MacroBtnFontName = Fl::get_font_name(MacroBtnFontnbr);
468 
469 	DXC_textname = Fl::get_font_name(DXC_textfont);
470 	DXfontname = Fl::get_font_name(DXfontnbr);
471 
472 	LOGGINGfontname = Fl::get_font_name(LOGGINGtextfont);
473 	LOGBOOKtextname = Fl::get_font_name(LOGBOOKtextfont);
474 
475 
476 #if ENABLE_NLS && defined(__WOE32__)
477 	set_ui_lang(listbox_language->index());
478 #endif
479 
480 	writeDefaultsXML();
481 	changed = false;
482 }
483 
484 #if USE_HAMLIB
fill_hamlib_menu(const char * rigname)485 static int fill_hamlib_menu(const char* rigname)
486 {
487 	cboHamlibRig->add(rigname);
488 	return 1;
489 }
490 #endif
491 
setDefaults()492 int configuration::setDefaults()
493 {
494 	ENSURE_THREAD(FLMAIN_TID);
495 
496 #if USE_HAMLIB
497 	hamlib_get_rigs();
498 	hamlib_get_rig_str(fill_hamlib_menu);
499 	if (HamRigModel == 0 && !HamRigName.empty()) { // compatibility with < 3.04
500 		HamRigModel = hamlib_get_rig_model_compat(HamRigName.c_str());
501 		LOG_VERBOSE("Found rig model %d for \"%s\"", HamRigModel, HamRigName.c_str());
502 	}
503 #endif
504 
505 	inpMyCallsign->value(myCall.c_str());
506 	inpMyName->value(myName.c_str());
507 	inpMyQth->value(myQth.c_str());
508 	inpMyLocator->value(myLocator.c_str());
509 	inpMyAntenna->value(myAntenna.c_str());
510 	UseLeadingZeros = btnUseLeadingZeros->value();
511 	ContestStart = (int)nbrContestStart->value();
512 	ContestDigits = (int)nbrContestDigits->value();
513 
514 	txtSecondary->value(secText.c_str());
515 
516 	txtTHORSecondary->value(THORsecText.c_str());
517 	valTHOR_BW->value(THOR_BW);
518 	valTHOR_FILTER->value(THOR_FILTER);
519 	valTHOR_PATHS->value(THOR_PATHS);
520 	valThorCWI->value(ThorCWI);
521 	valTHOR_PREAMBLE->value(THOR_PREAMBLE);
522 	valTHOR_SOFTSYMBOLS->value(THOR_SOFTSYMBOLS);
523 	valTHOR_SOFTBITS->value(THOR_SOFTBITS);
524 
525 	valDominoEX_BW->value(DOMINOEX_BW);
526 	valDominoEX_FILTER->value(DOMINOEX_FILTER);
527 	chkDominoEX_FEC->value(DOMINOEX_FEC);
528 	valDominoEX_PATHS->value(DOMINOEX_PATHS);
529 	valDomCWI->value(DomCWI);
530 
531 	btnRigCatCMDptt->value(RigCatCMDptt);
532 	btnTTYptt->value(TTYptt);
533 	btnUsePPortPTT->value(progdefaults.UsePPortPTT);
534 	btnUseUHrouterPTT->value(progdefaults.UseUHrouterPTT);
535 
536 	btn_use_cmedia_PTT->value(progdefaults.cmedia_ptt);
537 
538 #if USE_HAMLIB
539 	listbox_sideband->add(_("Rig mode"));
540 	listbox_sideband->add(_("Always LSB"));
541 	listbox_sideband->add(_("Always USB"));
542 	listbox_sideband->index(HamlibSideband);
543     btnHamlibCMDptt->value(HamlibCMDptt);
544     inpRIGdev->show();
545 	listbox_baudrate->show();
546 	cboHamlibRig->show();
547 	cboHamlibRig->value(HamRigName.c_str());
548 #else
549 	tab_tree->remove(tab_tree->find_item(_("Rig Control/Hamlib")));
550 #endif
551 	btnRTSptt->value(RTSptt);
552 	btnDTRptt->value(DTRptt);
553 	btnRTSplusV->value(RTSplus);
554 	btnDTRplusV->value(DTRplus);
555 
556 	inpTTYdev->value(PTTdev.c_str());
557 
558 	chkUSEHAMLIB->value(0);
559 	chkUSERIGCAT->value(0);
560 	if (chkUSEHAMLIBis) chkUSEHAMLIB->value(1);
561 	if (chkUSERIGCATis) chkUSERIGCAT->value(1);
562 
563 	if (!XmlRigFilename.empty()) readRigXML();
564 
565 	inpRIGdev->value(HamRigDevice.c_str());
566 	listbox_baudrate->index(HamRigBaudrate);
567 
568 	inpXmlRigDevice->value(XmlRigDevice.c_str());
569 	listbox_xml_rig_baudrate->index(XmlRigBaudrate);
570 
571 	select_nanoIO_CommPort->value(nanoIO_serial_port_name.c_str());
572 	select_nanoCW_CommPort->value(nanoIO_serial_port_name.c_str());
573 	select_CW_KEYLINE_CommPort->value(CW_KEYLINE_serial_port_name.c_str());
574 
575 	select_USN_FSK_port->value(Nav_FSK_port.c_str());
576 	select_Nav_config_port->value(Nav_config_port.c_str());
577 
578 	valCWsweetspot->value(CWsweetspot);
579 	valRTTYsweetspot->value(RTTYsweetspot);
580 	valPSKsweetspot->value(PSKsweetspot);
581 	btnWaterfallHistoryDefault->value(WaterfallHistoryDefault);
582 	btnWaterfallQSY->value(WaterfallQSY);
583 
584 	inpWaterfallClickText->input_type(FL_MULTILINE_INPUT);
585 	inpWaterfallClickText->value(WaterfallClickText.c_str());
586 	if (!WaterfallClickInsert)
587 		inpWaterfallClickText->deactivate();
588 
589 	for (size_t i = 0;
590 	     i < sizeof(waterfall::wf_wheel_action)/sizeof(*waterfall::wf_wheel_action); i++)
591 		listboxWaterfallWheelAction->add(waterfall::wf_wheel_action[i]);
592 	listboxWaterfallWheelAction->index(WaterfallWheelAction);
593 
594 	btnStartAtSweetSpot->value(StartAtSweetSpot);
595 	btnPSKmailSweetSpot->value(PSKmailSweetSpot);
596 	cntSearchRange->value(SearchRange);
597 	cntServerOffset->value(ServerOffset);
598 	cntACQsn->value(ACQsn);
599 
600 	btnCursorBWcolor->color(
601 		fl_rgb_color(cursorLineRGBI.R, cursorLineRGBI.G, cursorLineRGBI.B) );
602 	btnCursorCenterLineColor->color(
603 		fl_rgb_color(cursorCenterRGBI.R, cursorCenterRGBI.G, cursorCenterRGBI.B) );
604 	btnBwTracksColor->color(
605 		fl_rgb_color(bwTrackRGBI.R, bwTrackRGBI.G, bwTrackRGBI.B) );
606 
607 	sldrCWxmtWPM->value(CWspeed);
608 	cntCWdefWPM->value(defCWspeed);
609 	sldrCWbandwidth->value(CWbandwidth);
610 	btnCWrcvTrack->value(CWtrack);
611 	cntCWrange->value(CWrange);
612 	cntCWlowerlimit->value(CWlowerlimit);
613 	cntCWupperlimit->value(CWupperlimit);
614 	cntCWlowerlimit->maximum(CWupperlimit - 20);
615 	cntCWupperlimit->minimum(CWlowerlimit + 20);
616 	cntCWrisetime->value(CWrisetime);
617 	cntCWdash2dot->value(CWdash2dot);
618 	i_listboxQSKshape->index(QSKshape);
619 	sldrCWxmtWPM->minimum(CWlowerlimit);
620 	sldrCWxmtWPM->maximum(CWupperlimit);
621 	btnQSK->value(QSK);
622 	cntPreTiming->value(CWpre);
623 	cntPostTiming->value(CWpost);
624 	btnCWID->value(CWid);
625 
626 	listboxHellFont->index(feldfontnbr);
627 	btnFeldHellIdle->value(HellXmtIdle);
628 
629 	btnTxRSID->value(TransmitRSid);
630 	btnRSID->value(rsid);
631 	chkRSidWideSearch->value(rsidWideSearch);
632 	chkSlowCpu->value(slowcpu);
633 
634 	Fl_Button* qrzb = btnQRZXMLnotavailable;
635 	Fl_Button* qrzb2 = btnQRZWEBnotavailable;
636 	switch (QRZXML) {
637 	case QRZCD:
638 		qrzb = btnQRZcdrom;
639 		break;
640 	case QRZNET:
641 		qrzb = btnQRZsub;
642 		break;
643 	case HAMCALLNET:
644 		qrzb = btnHamcall;
645 		break;
646 	case CALLOOK:
647 		qrzb = btnCALLOOK;
648 		break;
649 	case HAMQTH:
650 		qrzb = btnHamQTH;
651 		break;
652 	case QRZXMLNONE:
653 	default :
654 		break;
655 	}
656 	switch (QRZWEB) {
657 	case QRZHTML:
658 		qrzb2 = btnQRZonline;
659 		break;
660 	case HAMCALLHTML:
661 		qrzb2 = btnHAMCALLonline;
662 		break;
663 	case HAMQTHHTML:
664 		qrzb2 = btnHamQTHonline;
665 		break;
666 	case QRZWEBNONE:
667 	default :
668 		break;
669 	}
670 
671 	set_qrzxml_buttons(qrzb);
672 	set_qrzweb_buttons(qrzb2);
673 
674 	txtQRZpathname->value(QRZpathname.c_str());
675 
676 	btnsendid->value(sendid);
677 	btnsendvideotext->value(sendtextid);
678 	chkID_SMALL->value(ID_SMALL);
679 
680 	wf->setPrefilter(wfPreFilter);
681 	btnWFaveraging->value(WFaveraging);
682 
683 	memcpy(&palette[0], &cfgpal0, sizeof(palette[0]));
684 	memcpy(&palette[1], &cfgpal1, sizeof(palette[1]));
685 	memcpy(&palette[2], &cfgpal2, sizeof(palette[2]));
686 	memcpy(&palette[3], &cfgpal3, sizeof(palette[3]));
687 	memcpy(&palette[4], &cfgpal4, sizeof(palette[4]));
688 	memcpy(&palette[5], &cfgpal5, sizeof(palette[5]));
689 	memcpy(&palette[6], &cfgpal6, sizeof(palette[6]));
690 	memcpy(&palette[7], &cfgpal7, sizeof(palette[7]));
691 	memcpy(&palette[8], &cfgpal8, sizeof(palette[8]));
692 
693 	wf->setcolors();
694 	setColorButtons();
695 
696 #if !HAVE_UHROUTER
697 	btnUseUHrouterPTT->hide();
698 #endif
699 
700 #if !HAVE_PARPORT
701 	btnUsePPortPTT->hide();
702 #endif
703 
704 #if ENABLE_NLS && defined(__WOE32__)
705 	ostringstream ss;
706 	for (lang_def_t* p = ui_langs; p->lang; p++) {
707 		ss.str("");
708 		ss << p->native_name;
709 		listbox_language->add(ss.str().c_str());
710 	}
711 	listbox_language->index(get_ui_lang());
712 	listbox_language->show();
713 #else
714 	listbox_language->hide();
715 #endif
716 
717 	return 1;
718 }
719 
resetDefaults(void)720 void configuration::resetDefaults(void)
721 {
722 	if (!fl_choice2(_("\
723 Reset all options to their default values?\n\n\
724 Reset options will take effect at the next start\n\
725 Files: fldigi_def.xml and fldigi.prefs will be deleted!\n"), _("OK"), _("Cancel"), NULL) &&
726 			Fl::event_key() != FL_Escape) {
727 		if (!fl_choice2(_("Confirm RESET"), _("Yes"), _("No"), NULL) &&
728 			Fl::event_key() != FL_Escape) {
729 			reset();
730 			atexit(reset);
731 		}
732 	}
733 }
734 
reset(void)735 void configuration::reset(void)
736 {
737 	remove(string(HomeDir).append("fldigi_def.xml").c_str());
738 	remove(string(HomeDir).append("fldigi.prefs").c_str());
739 }
740 
741 #include "rigio.h"
742 
initInterface()743 void configuration::initInterface()
744 {
745 	ENSURE_THREAD(FLMAIN_TID);
746 
747 // close down any possible rig interface threads
748 #if USE_HAMLIB
749 	hamlib_close();
750 //		MilliSleep(100);
751 #endif
752 	rigCAT_close();
753 //		MilliSleep(100);
754 
755 	RigCatCMDptt = btnRigCatCMDptt->value();
756 	TTYptt = btnTTYptt->value();
757 
758 	RTSptt = btnRTSptt->value();
759 	DTRptt = btnDTRptt->value();
760 	RTSplus = btnRTSplusV->value();
761 	DTRplus = btnDTRplusV->value();
762 
763 	PTTdev = inpTTYdev->value();
764 
765 #if USE_HAMLIB
766 	chkUSEHAMLIBis = chkUSEHAMLIB->value();
767      HamlibCMDptt = btnHamlibCMDptt->value();
768 #endif
769 	chkUSERIGCATis = chkUSERIGCAT->value();
770 
771 #if USE_HAMLIB
772 	if (*cboHamlibRig->value() == '\0') // no selection at start up
773 		cboHamlibRig->index(hamlib_get_index(HamRigModel));
774 	else
775 		HamRigModel = hamlib_get_rig_model(cboHamlibRig->index());
776 	HamRigDevice = inpRIGdev->value();
777 	HamRigBaudrate = listbox_baudrate->index();
778 #else
779 	cboHamlibRig->hide();
780 	inpRIGdev->hide();
781 	listbox_baudrate->hide();
782 #endif
783 
784 	if (connected_to_flrig) {
785 		LOG_INFO("%s", "using flrig xcvr control");
786 		wf->setQSY(1);
787 	} else if (chkUSERIGCATis) { // start the rigCAT thread
788 		if (rigCAT_init()) {
789 			LOG_INFO("%s", "using rigCAT xcvr control");
790 			wf->USB(true);
791 			wf->setQSY(1);
792 			rigCAT_get_pwrlevel();
793 		} else {
794 			LOG_INFO("%s", "defaulting to no xcvr control");
795 			noCAT_init();
796 			wf->USB(true);
797 			wf->setQSY(0);
798 			chkUSERIGCATis = false;
799 		}
800 #if USE_HAMLIB
801 	} else if (chkUSEHAMLIBis) { // start the hamlib thread
802 		if (hamlib_init(HamlibCMDptt)) {
803 			LOG_INFO("%s", "using HAMLIB xcvr control");
804 			btnInitHAMLIB->deactivate();
805 			wf->USB(true);
806 			wf->setQSY(1);
807 		} else {
808 			LOG_INFO("%s", "defaulting to no xcvr control");
809 			noCAT_init();
810 			wf->USB(true);
811 			wf->setQSY(0);
812 		}
813 #endif
814 	} else {
815 		LOG_INFO("%s", "No xcvr control selected");
816 		noCAT_init();
817 		wf->USB(true);
818 		wf->setQSY(0);
819 	}
820 	build_frequencies2_list();
821 
822 	if (HamlibCMDptt && chkUSEHAMLIBis)
823 		push2talk->reset(PTT::PTT_HAMLIB);
824 	else if ((RigCatCMDptt || RigCatRTSptt || RigCatDTRptt) && chkUSERIGCATis)
825 		push2talk->reset(PTT::PTT_RIGCAT);
826 	else if (TTYptt)
827 		push2talk->reset(PTT::PTT_TTY);
828 	else if (UsePPortPTT)
829 		push2talk->reset(PTT::PTT_PARPORT);
830 	else if (UseUHrouterPTT)
831 		push2talk->reset(PTT::PTT_UHROUTER);
832 	else if (cmedia_ptt)
833 		push2talk->reset(PTT::PTT_CMEDIA);
834 	else if (gpio_ptt)
835 		push2talk->reset(PTT::PTT_GPIO);
836 	else
837 		push2talk->reset(PTT::PTT_NONE);
838 
839 	wf->setRefLevel();
840 	wf->setAmpSpan();
841 	cntLowFreqCutoff->value(LowFreqCutoff);
842 }
843 
strBaudRate()844 const char* configuration::strBaudRate()
845 {
846 	return (szBaudRates[HamRigBaudrate + 1]);
847 }
848 
nBaudRate(const char * szBR)849 int configuration::nBaudRate(const char *szBR)
850 {
851     for (size_t i = 1; i < sizeof(szBaudRates); i++)
852         if (strcmp(szBaudRates[i], szBR) == 0)
853             return i - 1;
854     return 0;
855 }
856 
BaudRate(size_t n)857 int configuration::BaudRate(size_t n)
858 {
859 	if (n > sizeof(szBaudRates) + 1) return 1200;
860 	return (atoi(szBaudRates[n + 1]));
861 }
862 
863 #ifdef __WOE32__
open_serial(const char * dev)864 static bool open_serial(const char* dev)
865 {
866 	bool ret = false;
867 #ifdef __CYGWIN__
868 	int fd = fl_open(dev, O_RDWR | O_NOCTTY | O_NDELAY | O_CLOEXEC);
869 	if (fd != -1) {
870 		close(fd);
871 		ret = true;
872 	}
873 #elif defined(__MINGW32__)
874 	HANDLE fd = CreateFile(dev, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
875 	if (fd != INVALID_HANDLE_VALUE) {
876 		CloseHandle(fd);
877 		ret = true;
878 	}
879 #endif
880 	return ret;
881 }
882 #endif // __WOE32__
883 
testCommPorts()884 void configuration::testCommPorts()
885 {
886 	inpTTYdev->clear();
887 	inpRIGdev->clear();
888 	inpXmlRigDevice->clear();
889 #ifndef PATH_MAX
890 #  define PATH_MAX 1024
891 #endif
892 #ifndef __WOE32__
893 	struct stat st;
894 #endif
895 
896 #ifndef __APPLE__
897 	char ttyname[PATH_MAX + 1];
898 #endif
899 
900 	const char* tty_fmt[] = {
901 #if defined(__linux__)
902 		"/dev/ttyS%u",
903 		"/dev/ttyUSB%u",
904 		"/dev/usb/ttyUSB%u",
905 		"/dev/ttyACM%u",
906 		"/dev/usb/ttyACM%u",
907 		"/dev/rfcomm%u",
908 		"/opt/vttyS%u"
909 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
910 		"/dev/tty%2.2u"
911 #elif defined(__CYGWIN__)
912 		"/dev/ttyS%u"
913 #elif defined(__MINGW32__)
914 		"//./COM%u"
915 #elif defined(__APPLE__)
916 		"/dev/cu.*",
917 		"/dev/tty.*"
918 #endif
919 	};
920 
921 #if defined(__WOE32__)
922 #  define TTY_MAX 255
923 #elif defined(__OpenBSD__) || defined(__NetBSD__)
924 #  define TTY_MAX 4
925 #else
926 #  define TTY_MAX 8
927 #endif
928 
929 #ifdef __linux__
930 	glob_t gbuf;
931 	glob("/dev/serial/by-id/*", 0, NULL, &gbuf);
932 	for (size_t j = 0; j < gbuf.gl_pathc; j++) {
933 		if ( !(stat(gbuf.gl_pathv[j], &st) == 0 && S_ISCHR(st.st_mode)) ||
934 		     strstr(gbuf.gl_pathv[j], "modem") )
935 			continue;
936 		LOG_INFO("Found serial port %s", gbuf.gl_pathv[j]);
937 		inpTTYdev->add(gbuf.gl_pathv[j]);
938 #  if USE_HAMLIB
939 		inpRIGdev->add(gbuf.gl_pathv[j]);
940 #  endif
941 		inpXmlRigDevice->add(gbuf.gl_pathv[j]);
942 
943 		select_nanoIO_CommPort->add(gbuf.gl_pathv[j]);
944 		select_nanoCW_CommPort->add(gbuf.gl_pathv[j]);
945 		select_CW_KEYLINE_CommPort->add(gbuf.gl_pathv[j]);
946 
947 		select_USN_FSK_port->add(gbuf.gl_pathv[j]);
948 		select_Nav_config_port->add(gbuf.gl_pathv[j]);
949 
950 		select_WK_CommPort->add(gbuf.gl_pathv[j]);
951 		select_WKFSK_CommPort->add(gbuf.gl_pathv[j]);
952 	}
953 	globfree(&gbuf);
954 #endif
955 
956 	for (size_t i = 0; i < sizeof(tty_fmt)/sizeof(*tty_fmt); i++) {
957 #ifndef __APPLE__
958 		for (unsigned j = 0; j < TTY_MAX; j++) {
959 			snprintf(ttyname, sizeof(ttyname), tty_fmt[i], j);
960 #  ifndef __WOE32__
961 			if ( !(stat(ttyname, &st) == 0 && S_ISCHR(st.st_mode)) )
962 				continue;
963 #  else // __WOE32__
964 			LOG_DEBUG("Testing serial port %s", ttyname);
965 			if (!open_serial(ttyname))
966 				continue;
967 #    ifdef __CYGWIN__
968 			snprintf(ttyname, sizeof(ttyname), "COM%u", j+1);
969 #    else
970 			snprintf(ttyname, sizeof(ttyname), "COM%u", j);
971 #    endif
972 #  endif // __WOE32__
973 
974 			LOG_VERBOSE("Found serial port %s", ttyname);
975 			inpTTYdev->add(ttyname);
976 #  if USE_HAMLIB
977 			inpRIGdev->add(ttyname);
978 #  endif
979 			inpXmlRigDevice->add(ttyname);
980 
981 			select_nanoIO_CommPort->add(ttyname);
982 			select_nanoCW_CommPort->add(ttyname);
983 			select_CW_KEYLINE_CommPort->add(ttyname);
984 
985 			select_USN_FSK_port->add(ttyname);
986 			select_Nav_config_port->add(ttyname);
987 
988 			select_WK_CommPort->add(ttyname);
989 			select_WKFSK_CommPort->add(ttyname);
990 		}
991 #else // __APPLE__
992 		glob_t gbuf;
993 		glob(tty_fmt[i], 0, NULL, &gbuf);
994 		for (size_t j = 0; j < gbuf.gl_pathc; j++) {
995 			int ret1 = !stat(gbuf.gl_pathv[j], &st);
996 			int ret2 = S_ISCHR(st.st_mode);
997 			if (ret1) {
998 				LOG_INFO("Serial port %s", gbuf.gl_pathv[j]);
999 				LOG_INFO("  device mode:     %X", st.st_mode);
1000 				LOG_INFO("  char device?     %s", ret2 ? "Y" : "N");
1001 			} else
1002 				LOG_INFO("%s does not return stat query", gbuf.gl_pathv[j]);
1003 
1004 			if ( (ret1 && ret2 ) || strstr(gbuf.gl_pathv[j], "modem") )
1005 				inpTTYdev->add(gbuf.gl_pathv[j]);
1006 			else
1007 				continue;
1008 #  if USE_HAMLIB
1009 			inpRIGdev->add(gbuf.gl_pathv[j]);
1010 #  endif
1011 			inpXmlRigDevice->add(gbuf.gl_pathv[j]);
1012 
1013 			select_nanoIO_CommPort->add(gbuf.gl_pathv[j]);
1014 			select_nanoCW_CommPort->add(gbuf.gl_pathv[j]);
1015 			select_CW_KEYLINE_CommPort->add(gbuf.gl_pathv[j]);
1016 
1017 			select_USN_FSK_port->add(gbuf.gl_pathv[j]);
1018 			select_Nav_config_port->add(gbuf.gl_pathv[j]);
1019 
1020 			select_WK_CommPort->add(gbuf.gl_pathv[j]);
1021 			select_WKFSK_CommPort->add(gbuf.gl_pathv[j]);
1022 		}
1023 		globfree(&gbuf);
1024 #endif // __APPLE__
1025 	}
1026 
1027 #if HAVE_UHROUTER
1028 	if (stat(UHROUTER_FIFO_PREFIX "Read", &st) != -1 && S_ISFIFO(st.st_mode) &&
1029 	    stat(UHROUTER_FIFO_PREFIX "Write", &st) != -1 && S_ISFIFO(st.st_mode))
1030 		inpTTYdev->add(UHROUTER_FIFO_PREFIX);
1031 #endif // HAVE_UHROUTER
1032 }
1033 
font_number(const char * name)1034 Fl_Font font_number(const char* name)
1035 {
1036     int n = (int)Fl::set_fonts(0);
1037     for (int i = 0; i < n; i++) {
1038         if (strcmp(Fl::get_font_name((Fl_Font)i), name) == 0)
1039             return (Fl_Font)i;
1040     }
1041 	return FL_HELVETICA;
1042 }
1043 
initFonts(void)1044 void configuration::initFonts(void)
1045 {
1046 	RxFontnbr =
1047 	TxFontnbr =
1048 	WaterfallFontnbr =
1049 	ViewerFontnbr =
1050 	FreqControlFontnbr =
1051 	MacroBtnFontnbr =
1052 	MacroEditFontnbr  =
1053 
1054 	DXC_textfont =
1055 	DXfontnbr =
1056 
1057 	LOGGINGtextfont =
1058 	LOGBOOKtextfont =
1059 
1060 	FL_HELVETICA;
1061 
1062 	if (!RxFontName.empty())
1063 		RxFontnbr = font_number(RxFontName.c_str());
1064 	if (!TxFontName.empty())
1065 		TxFontnbr = font_number(TxFontName.c_str());
1066 	if (!WaterfallFontName.empty())
1067 		WaterfallFontnbr = font_number(WaterfallFontName.c_str());
1068 	if (!ViewerFontName.empty())
1069 		ViewerFontnbr = font_number(ViewerFontName.c_str());
1070 	if (!FreqControlFontName.empty())
1071 		FreqControlFontnbr = font_number(FreqControlFontName.c_str());
1072 
1073 	if (!MacroEditFontName.empty())
1074 		MacroEditFontnbr = font_number(MacroEditFontName.c_str());
1075 	if (!MacroBtnFontName.empty())
1076 		MacroBtnFontnbr = font_number(MacroBtnFontName.c_str());
1077 
1078 	if (!DXC_textname.empty())
1079 		DXC_textfont = font_number(DXC_textname.c_str());
1080 	if (!DXfontname.empty())
1081 		DXfontnbr = font_number(DXfontname.c_str());
1082 
1083 	if (!LOGGINGfontname.empty())
1084 		LOGGINGtextfont = font_number(LOGGINGfontname.c_str());
1085 	if (!LOGBOOKtextname.empty())
1086 		LOGBOOKtextfont = font_number(LOGBOOKtextname.c_str());
1087 }
1088