1 // ----------------------------------------------------------------------------
2 //
3 // PSK browser widget
4 //
5 // Copyright (C) 2008-2010
6 //		David Freese, W1HKJ
7 // Copyright (C) 2008-2010
8 //		Stelios Bounanos, M0GLD
9 //
10 // This file is part of fldigi.
11 //
12 // Fldigi is free software: you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation, either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // Fldigi is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
24 // ----------------------------------------------------------------------------
25 
26 #include <FL/Enumerations.H>
27 
28 #include "config.h"
29 
30 #include "psk_browser.h"
31 
32 #include "configuration.h"
33 #include "confdialog.h"
34 #include "status.h"
35 #include "waterfall.h"
36 #include "fl_digi.h"
37 #include "gettext.h"
38 #include "flmisc.h"
39 #include "flinput2.h"
40 #include "flslider2.h"
41 #include "spot.h"
42 #include "icons.h"
43 #include "Viewer.h"
44 
45 #include "audio_alert.h"
46 
47 #include <string>
48 
49 using namespace std;
50 
51 string pskBrowser::hilite_color_1;
52 string pskBrowser::hilite_color_2;
53 string pskBrowser::white;
54 string pskBrowser::bkgnd[2];
55 
56 int pskBrowser::cwidth = 5;
57 int pskBrowser::cheight = 12;
58 int pskBrowser::sbarwidth = 16;
59 
pskBrowser(int x,int y,int w,int h,const char * l)60 pskBrowser::pskBrowser(int x, int y, int w, int h, const char *l)
61 	:Fl_Hold_Browser(x,y,w,h,l)
62 {
63 	fnt = FL_HELVETICA;
64 	siz = 12;
65 	rfc = 0LL;
66 	usb = true;
67 	seek_re = NULL;
68 	cols[0] = 80; cols[1] = 0;
69 	evalcwidth();
70 
71 	HiLite_1 = FL_RED;
72 	HiLite_2 = FL_GREEN;
73 	BkSelect = FL_BLUE;
74 	Backgnd1 = (Fl_Color)55;
75 	Backgnd2 = (Fl_Color)53;
76 	makecolors();
77 	cdistiller = reinterpret_cast<CharsetDistiller*>(operator new(MAXCHANNELS*sizeof(CharsetDistiller)));
78 
79 	string bline;
80 	for (int i = 0; i < MAXCHANNELS; i++) {
81 		alerted[i].regex_alert = alerted[i].mycall_alert = false;
82 		bwsrline[i] = " ";
83 		bwsrfreq[i] = NULLFREQ;
84 		bline = freqformat(i);
85 		if ( i < progdefaults.VIEWERchannels) add(bline.c_str());
86 		linechars[i] = 0;
87 		new(&cdistiller[i]) CharsetDistiller;
88 	}
89 	nchars = (w - cols[0] - (sbarwidth + 2*BWSR_BORDER)) / cwidth;
90 	nchars = nchars < 1 ? 1 : nchars;
91 
92 }
93 
~pskBrowser()94 pskBrowser::~pskBrowser()
95 {
96 	for (int i = MAXCHANNELS-1; i >= 0; i--)
97 		cdistiller[i].~CharsetDistiller();
98 
99 	operator delete(cdistiller);
100 }
101 
evalcwidth()102 void pskBrowser::evalcwidth()
103 {
104 	fl_font(fnt, siz);
105 	textfont(fnt);
106 	textsize(siz);
107 	cwidth = (int)fl_width("8");
108 	if (cwidth <= 0) cwidth = 5;
109 	cheight = fl_height();
110 	labelwidth[VIEWER_LABEL_OFF] = 1;
111 	labelwidth[VIEWER_LABEL_AF] = 5*cwidth;
112 	labelwidth[VIEWER_LABEL_RF] = 10*cwidth;
113 	labelwidth[VIEWER_LABEL_CH] = 3*cwidth;
114 	columns(labelwidth[progdefaults.VIEWERlabeltype]);
115 }
116 
freqformat(int i)117 string pskBrowser::freqformat(int i) // 0 < i < channels
118 {
119 	szLine[0] = 0;
120 	int freq = bwsrfreq[i];
121 	switch (progdefaults.VIEWERlabeltype) {
122 		case VIEWER_LABEL_AF:
123 			if (freq != NULLFREQ)
124 				snprintf(szLine, sizeof(szLine), "%4d", freq);
125 			else
126 				snprintf(szLine, sizeof(szLine), "    ");
127 			break;
128 		case VIEWER_LABEL_RF:
129 			if (freq != NULLFREQ)
130 				snprintf(szLine, sizeof(szLine), "%8.2f", (rfc + (usb ? freq : -freq)) / 1000.0f);
131 			else
132 				snprintf(szLine, sizeof(szLine), "    ");
133 			break;
134 		case VIEWER_LABEL_CH:
135 			snprintf(szLine, sizeof(szLine), "%2d", i + 1);
136 			break;
137 		default:
138 			snprintf(szLine, sizeof(szLine), "    ");
139 			break;
140 	}
141 	fline = white;
142 	fline.append("@r").append(szLine).append("\t").append(bkgnd[i%2]);
143 
144 	return fline;
145 }
146 
swap(int i,int j)147 void pskBrowser::swap(int i, int j)
148 {
149 	string tempstr = bwsrline[j];
150 	bwsrline[j] = bwsrline[i];
151 	bwsrline[i] = tempstr;
152 	int f = bwsrfreq[j];
153 	bwsrfreq[j] = bwsrfreq[i];
154 	bwsrfreq[i] = f;
155 
156 	tempstr = freqformat(i);
157 	tempstr.append(bwsrline[i]);
158 	text(i+1, tempstr.c_str());
159 
160 	tempstr = freqformat(j);
161 	tempstr.append(bwsrline[j]);
162 	text(j+1, tempstr.c_str());
163 
164 	redraw();
165 
166 }
167 
case_find(string & haystack,string & needle)168 static size_t case_find(string &haystack, string &needle)
169 {
170 	string Uhaystack = haystack;
171 	string Uneedle = needle;
172 	for (size_t i = 0; i < Uhaystack.length(); i++ ) Uhaystack[i] = toupper(Uhaystack[i]);
173 	for (size_t i = 0; i < Uneedle.length(); i++ ) Uneedle[i] = toupper(Uneedle[i]);
174 	return Uhaystack.find(Uneedle);
175 }
176 
resize(int x,int y,int w,int h)177 void pskBrowser::resize(int x, int y, int w, int h)
178 {
179 	if (w) {
180 		Fl_Hold_Browser::resize(x,y,w,h);
181 		evalcwidth();
182 		nchars = (w - cols[0] - (sbarwidth + 2*BWSR_BORDER)) / cwidth;
183 		nchars = nchars < 1 ? 1 : nchars;
184 		string bline;
185 		Fl_Hold_Browser::clear();
186 		for (int i = 0, j = 0; i < progdefaults.VIEWERchannels; i++) {
187 			if (progdefaults.VIEWERascend) j = progdefaults.VIEWERchannels - 1 - i;
188 			else j = i;
189 			bwsrline[j].clear();
190 			linechars[j] = 0;
191 			bline = freqformat(j);
192 			if (seek_re  && seek_re->match(bwsrline[j].c_str(), REG_NOTBOL | REG_NOTEOL))
193 				bline.append(hilite_color_1);
194 			else if (	!progdefaults.myCall.empty() &&
195 						case_find (bwsrline[j], progdefaults.myCall ) != string::npos)
196 				bline.append(hilite_color_2);
197 			Fl_Hold_Browser::add(bline.c_str());
198 		}
199 	}
200 }
201 
makecolors()202 void pskBrowser::makecolors()
203 {
204 	char tempstr[20];
205 
206 	snprintf(tempstr, sizeof(tempstr), "@C%u", HiLite_1);
207 	hilite_color_1 = tempstr;
208 
209 	snprintf(tempstr, sizeof(tempstr), "@C%u", HiLite_2);
210 	hilite_color_2 = tempstr;
211 
212 	snprintf(tempstr, sizeof(tempstr), "@C%u", FL_FOREGROUND_COLOR); // foreground
213 	white = tempstr;
214 
215 	selection_color(BkSelect);
216 
217 	snprintf(tempstr, sizeof(tempstr), "@B%u", Backgnd1); // background for odd rows
218 	bkgnd[0] = tempstr;
219 
220 	snprintf(tempstr, sizeof(tempstr), "@B%u", Backgnd2); // background for even rows
221 	bkgnd[1] = tempstr;
222 }
223 
addchr(int ch,int freq,unsigned char c,int md,bool signal_alert)224 void pskBrowser::addchr(int ch, int freq, unsigned char c, int md, bool signal_alert)
225 {
226 	if (ch < 0 || ch >= MAXCHANNELS)
227 		return;
228 
229 	if (c == '\n') c = ' ';
230 	if (c < ' ') return;
231 
232 	nchars = (w() - cols[0] - (sbarwidth + 2*BWSR_BORDER)) / cwidth;
233 	nchars = nchars < 1 ? 1 : nchars;
234 
235 	bwsrfreq[ch] = freq;
236 
237 	if (bwsrline[ch].length() == 1 && bwsrline[ch][0] == ' ') {
238 		bwsrline[ch].clear();
239 		linechars[ch] = 0;
240 	}
241 
242 	cdistiller[ch].rx(c);
243 
244 	if (cdistiller[ch].data_length() > 0) {
245 		bwsrline[ch] += cdistiller[ch].data();
246 		linechars[ch] += cdistiller[ch].num_chars();
247 		cdistiller[ch].clear();
248 	}
249 
250 	if (linechars[ch] > nchars) {
251 		if (progdefaults.VIEWERmarquee) {
252 			while (linechars[ch] > nchars) {
253 				bwsrline[ch].erase(0, fl_utf8len1(bwsrline[ch][0]));
254 				linechars[ch]--;
255 			}
256 		} else {
257 			bwsrline[ch].clear();
258 			linechars[ch] = 0;
259 		}
260 	}
261 
262 	nuline = freqformat(ch);
263 
264 	if (!bwsrline[ch].empty()) {
265 		if (seek_re  && seek_re->match(bwsrline[ch].c_str(), REG_NOTBOL | REG_NOTEOL)) {
266 			if ((trx_state == STATE_RX) &&
267 				(alerted[ch].regex_alert == false) &&
268 				signal_alert &&
269 				progdefaults.ENABLE_BWSR_REGEX_MATCH) {
270 				if (audio_alert) audio_alert->alert(progdefaults.BWSR_REGEX_MATCH);
271 				alerted[ch].regex_alert = true;
272 			}
273 			nuline.append(hilite_color_1);
274 		} else {
275 			alerted[ch].regex_alert = false;
276 		}
277 	} else {
278 		alerted[ch].regex_alert = false;
279 	}
280 	if (!progdefaults.myCall.empty() &&
281 			case_find (bwsrline[ch], progdefaults.myCall ) != string::npos) {
282 		nuline.append(hilite_color_2);
283 		if ((trx_state == STATE_RX) &&
284 			(alerted[ch].mycall_alert == false) &&
285 			signal_alert &&
286 			progdefaults.ENABLE_BWSR_MYCALL_MATCH) {
287 			if (audio_alert) audio_alert->alert(progdefaults.BWSR_MYCALL_MATCH);
288 			alerted[ch].mycall_alert = true;
289 		}
290 	} else
291 		alerted[ch].mycall_alert = false;
292 
293 	nuline.append("@.").append(bwsrline[ch]);
294 
295 	if (progdefaults.VIEWERascend)
296 		text(progdefaults.VIEWERchannels - ch, nuline.c_str());
297 	else
298 		text(ch + 1, nuline.c_str());
299 
300 	redraw();
301 }
302 
set_freq(int i,int freq)303 void pskBrowser::set_freq(int i, int freq) // 0 < i < channels
304 {
305 	string new_line = "";
306 
307 	bwsrfreq[i] = freq;
308 	new_line.append(freqformat(i)).append(bwsrline[i]);
309 	if (progdefaults.VIEWERascend)
310 		replace(progdefaults.VIEWERchannels - i, new_line.c_str());
311 	else
312 		replace(i + 1, new_line.c_str());
313 }
314 
clear()315 void pskBrowser::clear()
316 {
317 	long freq;
318 	Fl_Hold_Browser::clear();
319 	for (int i = 0, j = 0; i < progdefaults.VIEWERchannels; i++) {
320 		if (progdefaults.VIEWERascend) j = progdefaults.VIEWERchannels - 1 - i;
321 		else j = i;
322 		freq = NULLFREQ;
323 		bwsrline[j] = " ";
324 		bwsrfreq[j] = freq;
325 		fline = freqformat(j);
326 		add((fline.append(bwsrline[j])).c_str());
327 	}
328 	deselect();
329 	redraw();
330 }
331 
clearch(int n,int freq)332 void pskBrowser::clearch(int n, int freq) // 0 < n < channels
333 {
334 	bwsrline[n] = " ";
335 	set_freq(n, freq);
336 	redraw();
337 }
338 
freq(int i)339 int pskBrowser::freq(int i) { // 1 < i < progdefaults.VIEWERchannels
340 	if (progdefaults.VIEWERascend)
341 		return (
342 			i < 1 ? 0 :
343 			i > progdefaults.VIEWERchannels ? 0 : bwsrfreq[progdefaults.VIEWERchannels - i]);
344 	else
345 		return (i < 1 ? 0 : i > MAXCHANNELS ? 0 : bwsrfreq[i - 1]);
346 }
347 
set_input_encoding(int encoding_id)348 void pskBrowser::set_input_encoding(int encoding_id)
349 {
350 	for (int i = 0; i < MAXCHANNELS; i++)
351 		cdistiller[i].set_input_encoding(encoding_id);
352 }
353