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