1 // ----------------------------------------------------------------------------
2 // FTextRXTX.cxx
3 //
4 // Copyright (C) 2007-2010
5 // Stelios Bounanos, M0GLD
6 //
7 // Copyright (C) 2008-2010
8 // Dave Freese, W1HKJ
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 this program. If not, see <http://www.gnu.org/licenses/>.
24 // ----------------------------------------------------------------------------
25
26 #include <config.h>
27
28 #include <string>
29 #include <cstring>
30 #include <cstdlib>
31 #include <cstdio>
32 #include <sys/stat.h>
33 #include <map>
34 #include <iostream>
35 #include <fstream>
36 #include <sstream>
37 #include <algorithm>
38 #include <iomanip>
39
40 #include <FL/Fl_Tooltip.H>
41
42 #include "FTextView.h"
43 #include "main.h"
44 #include "trx.h"
45 #include "macros.h"
46 #include "main.h"
47 #include "fl_digi.h"
48
49 #include "cw.h"
50
51 #include "fileselect.h"
52 #include "font_browser.h"
53
54 #include "ascii.h"
55 #include "configuration.h"
56
57 #include "qrunner.h"
58
59 #include "mfsk.h"
60 #include "icons.h"
61 #include "globals.h"
62 #include "re.h"
63 #include "strutil.h"
64 #include "dxcc.h"
65 #include "locator.h"
66 #include "logsupport.h"
67 #include "status.h"
68 #include "gettext.h"
69 #include "arq_io.h"
70 #include "fl_digi.h"
71 #include "strutil.h"
72
73 #include "debug.h"
74
75 #include "contest.h"
76 #include "counties.h"
77
78 using namespace std;
79
80
81 // Fl_Scrollbar wrapper to draw marks on the slider background.
82 // Currently only implemented for a vertical scrollbar.
83 class MVScrollbar : public Fl_Scrollbar
84 {
85 struct mark_t {
86 double pos;
87 Fl_Color color;
mark_tMVScrollbar::mark_t88 mark_t(double pos_, Fl_Color color_) : pos(pos_), color(color_) { }
89 };
90
91 public:
MVScrollbar(int X,int Y,int W,int H,const char * l=0)92 MVScrollbar(int X, int Y, int W, int H, const char* l = 0)
93 : Fl_Scrollbar(X, Y, W, H, l), draw_marks(false) { }
94
95 void draw(void);
mark(Fl_Color c)96 void mark(Fl_Color c) { marks.push_back(mark_t(maximum() - 1.0, c)); redraw(); }
has_marks(void)97 bool has_marks(void) { return !marks.empty(); }
show_marks(bool b)98 void show_marks(bool b) { draw_marks = b; redraw(); }
clear(void)99 void clear(void) { marks.clear(); redraw(); }
100
101 private:
102 vector<mark_t> marks;
103 bool draw_marks;
104 };
105
106 /*
107 RX_MENU_QRZ_THIS, RX_MENU_CALL, RX_MENU_NAME, RX_MENU_QTH,
108 RX_MENU_STATE, RX_MENU_COUNTY, RX_MENU_PROVINCE,
109 RX_MENU_COUNTRY, RX_MENU_LOC,
110 RX_MENU_RST_IN, RX_MENU_RST_OUT,
111 RX_MENU_XCHG, RX_MENU_SERIAL,
112 RX_MENU_CLASS, RX_MENU_SECTION,
113
114 RX_MENU_SS_SER, RX_MENU_SS_PRE, RX_MENU_SS_CHK, RX_MENU_SS_SEC,
115
116 RX_MENU_CQZONE, RX_MENU_CQSTATE,
117 RX_MENU_1010_NR,
118 RX_MENU_AGE,
119
120 RX_MENU_CHECK,
121 RX_MENU_NAQP,
122 RX_MENU_SCOUT,
123 RX_MENU_TROOP,
124 RX_MENU_POWER,
125
126 RX_MENU_QSOP_STATE,
127 RX_MENU_QSOP_COUNTY,
128 RX_MENU_QSOP_SERNO,
129 RX_MENU_QSOP_NAME,
130 RX_MENU_QSOP_XCHG,
131 RX_MENU_QSOP_CAT,
132
133 RX_MENU_DIV,
134
135 RX_MENU_COPY,
136 RX_MENU_CLEAR,
137 RX_MENU_SELECT_ALL,
138 RX_MENU_SAVE,
139 RX_MENU_WRAP,
140
141 RX_MENU_ALL_ENTRY,
142
143 RX_MENU_SCROLL_HINTS,
144
145 RX_MENU_NUM_ITEMS
146 */
147
148 Fl_Menu_Item FTextRX::menu[] = {
149 { icons::make_icon_label(_("Look up call"), net_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
150 { icons::make_icon_label(_("Call"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
151 { icons::make_icon_label(_("Name"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
152 { icons::make_icon_label(_("QTH"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
153 { icons::make_icon_label(_("State"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
154 { icons::make_icon_label(_("County"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
155 { icons::make_icon_label(_("Province"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
156 { icons::make_icon_label(_("Country"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
157 { icons::make_icon_label(_("Locator"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
158 { icons::make_icon_label(_("RST(r)"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
159 { icons::make_icon_label(_("RST(s)"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
160 { icons::make_icon_label(_("Exchange In"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
161 { icons::make_icon_label(_("Rx Serial #"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
162 { icons::make_icon_label(_("Class"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
163 { icons::make_icon_label(_("Section"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
164
165 { icons::make_icon_label(_("SS ser #"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
166 { icons::make_icon_label(_("SS prec"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
167 { icons::make_icon_label(_("SS check"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
168 { icons::make_icon_label(_("SS section"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
169
170 { icons::make_icon_label(_("CQ zone"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
171 { icons::make_icon_label(_("CQ STATE"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
172
173 { icons::make_icon_label(_("1010 Nr"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
174
175 { icons::make_icon_label(_("Kid's Age"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
176
177 { icons::make_icon_label(_("Round Up Chk"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
178 { icons::make_icon_label(_("NAQP xchg"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
179 { icons::make_icon_label(_("JOTA scout"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
180 { icons::make_icon_label(_("JOTA troop"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
181 { icons::make_icon_label(_("POWER(r)"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
182
183 { icons::make_icon_label(_("QSOp state"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
184 { icons::make_icon_label(_("QSOp county"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
185 { icons::make_icon_label(_("QSOp serno"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
186 { icons::make_icon_label(_("QSOp name"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
187 { icons::make_icon_label(_("QSOp xchg"), enter_key_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
188 { icons::make_icon_label(_("QSOp category"), enter_key_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
189
190 { icons::make_icon_label(_("Insert marker"), insert_link_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
191
192 { 0 }, // VIEW_MENU_COPY
193 { 0 }, // VIEW_MENU_CLEAR
194 { 0 }, // VIEW_MENU_SELECT_ALL
195 { 0 }, // VIEW_MENU_SAVE
196 { 0 }, // VIEW_MENU_WRAP
197
198 { _("All entries"), 0, 0, 0, FL_MENU_TOGGLE, FL_NORMAL_LABEL },
199
200 { _("Scroll hints"), 0, 0, 0, FL_MENU_TOGGLE, FL_NORMAL_LABEL },
201 { 0 }
202 };
203
204 /// FTextRX constructor.
205 /// We remove \c Fl_Text_Display_mod::buffer_modified_cb from the list of callbacks
206 /// because we want to scroll depending on the visibility of the last line; @see
207 /// changed_cb.
208 /// @param x
209 /// @param y
210 /// @param w
211 /// @param h
212 /// @param l
FTextRX(int x,int y,int w,int h,const char * l)213 FTextRX::FTextRX(int x, int y, int w, int h, const char *l)
214 : FTextView(x, y, w, h, l)
215 {
216 memcpy(menu + RX_MENU_COPY, FTextView::menu, (FTextView::menu->size() - 1) * sizeof(*FTextView::menu));
217 context_menu = menu;
218 init_context_menu();
219 menu[RX_MENU_ALL_ENTRY].clear();
220 menu[RX_MENU_SCROLL_HINTS].clear();
221 menu[RX_MENU_WRAP].hide();
222 // Replace the scrollbar widget
223 MVScrollbar* mvsb = new MVScrollbar(mVScrollBar->x(), mVScrollBar->y(),
224 mVScrollBar->w(), mVScrollBar->h(), NULL);
225 mvsb->show_marks(false);
226 mvsb->callback(mVScrollBar->callback(), mVScrollBar->user_data());
227 remove(mVScrollBar);
228 delete mVScrollBar;
229 Fl_Group::add(mVScrollBar = mvsb);
230 mFastDisplay = 1;
231 num_words = 1;
232 }
233
~FTextRX()234 FTextRX::~FTextRX()
235 {
236 }
237
238 /// Handles fltk events for this widget.
239
240 /// We only care about mouse presses (to display the popup menu and prevent
241 /// pasting) and keyboard events (to make sure no text can be inserted).
242 /// Everything else is passed to the base class handle().
243 ///
244 /// @param event
245 ///
246 /// @return
247 ///
handle(int event)248 int FTextRX::handle(int event)
249 {
250 static Fl_Cursor cursor;
251
252 switch (event) {
253 case FL_DRAG:
254 if (Fl::event_button() != FL_LEFT_MOUSE)
255 return 1;
256 break;
257 case FL_PUSH:
258 if (!Fl::event_inside(this))
259 break;
260 switch (Fl::event_button()) {
261 case FL_LEFT_MOUSE:
262 if (progdefaults.rxtext_clicks_qso_data) {
263 if (handle_clickable(Fl::event_x() - x(), Fl::event_y() - y()))
264 return 1;
265 if (handle_qso_data(Fl::event_x() - x(), Fl::event_y() - y()))
266 return 1;
267 }
268 goto out;
269 case FL_MIDDLE_MOUSE:
270 if (cursor != FL_CURSOR_HAND) {
271 if (handle_qso_data(Fl::event_x() - x(), Fl::event_y() - y())) {
272 return 1;
273 }
274 }
275 goto out;
276 case FL_RIGHT_MOUSE:
277 handle_context_menu();
278 return 1;
279 default:
280 goto out;
281 }
282 break;
283 case FL_RELEASE:
284 break;
285 case FL_MOVE: {
286 int p = xy_to_position(Fl::event_x(), Fl::event_y(), Fl_Text_Display_mod::CURSOR_POS);
287 if ((unsigned char)sbuf->byte_at(p) >= CLICK_START + FTEXT_DEF) {
288 if (cursor != FL_CURSOR_HAND)
289 window()->cursor(cursor = FL_CURSOR_HAND);
290 return 1;
291 }
292 else
293 cursor = FL_CURSOR_INSERT;
294 break;
295 }
296 // catch some text-modifying events that are not handled by kf_* functions
297 case FL_KEYBOARD:
298 break;
299 case FL_PASTE:
300 return 0;
301 case FL_ENTER:
302 if (!progdefaults.rxtext_tooltips || Fl_Tooltip::delay() == 0.0f)
303 break;
304 tooltips.enabled = Fl_Tooltip::enabled();
305 tooltips.delay = Fl_Tooltip::delay();
306 Fl_Tooltip::enable(1);
307 Fl_Tooltip::delay(0.0f);
308 Fl::add_timeout(tooltips.delay / 2.0, dxcc_tooltip, this);
309 break;
310 case FL_LEAVE:
311 window()->cursor(FL_CURSOR_DEFAULT);
312 if (!progdefaults.rxtext_tooltips || Fl_Tooltip::delay() != 0.0f)
313 break;
314 Fl_Tooltip::enable(tooltips.enabled);
315 Fl_Tooltip::delay(tooltips.delay);
316 Fl::remove_timeout(dxcc_tooltip, this);
317 break;
318 }
319
320 out:
321 return FTextView::handle(event);
322 }
323
324 /// Adds a char to the buffer
325 ///
326 /// @param c The character
327 /// @param attr The attribute (@see enum text_attr_e); RECV if omitted.
328 ///
329
add(unsigned int c,int attr)330 void FTextRX::add(unsigned int c, int attr)
331 {
332 if (c == '\r')
333 return;
334
335 char s[] = { '\0', '\0', char( FTEXT_DEF + attr ), '\0' };
336 const char *cp = &s[0];
337
338 // The user may have moved the cursor by selecting text or
339 // scrolling. Place it at the end of the buffer.
340 if (mCursorPos != tbuf->length())
341 insert_position(tbuf->length());
342
343 switch (c) {
344 case '\b':
345 // we don't call kf_backspace because it kills selected text
346 if (s_text.length()) {
347 int character_start = tbuf->utf8_align(tbuf->length() - 1);
348 int character_length = fl_utf8len1(tbuf->byte_at(character_start));
349
350 tbuf->remove(character_start, tbuf->length());
351 sbuf->remove(character_start, sbuf->length());
352 s_text.resize(s_text.length() - character_length);
353 s_style.resize(s_style.length() - character_length);
354 }
355 break;
356 case '\n':
357 // maintain the scrollback limit, if we have one
358 if (max_lines > 0 && tbuf->count_lines(0, tbuf->length()) >= max_lines) {
359 int le = tbuf->line_end(0) + 1; // plus 1 for the newline
360 tbuf->remove(0, le);
361 sbuf->remove(0, le);
362 }
363 s_text.clear();
364 s_style.clear();
365 insert("\n");
366 sbuf->append(s + 2);
367 break;
368 default:
369 if ((c < ' ' || c == 127) && attr != CTRL) // look it up
370 cp = ascii[(unsigned char)c];
371 else // insert verbatim
372 s[0] = c;
373
374 for (int i = 0; cp[i]; ++i) {
375 s_text += cp[i];
376 s_style += s[2];
377 }
378
379 fl_font( textfont(), textsize() );
380 int lwidth = (int)fl_width( s_text.c_str(), s_text.length());
381 bool wrapped = false;
382 if ( lwidth >= (text_area.w - mVScrollBar->w() - LEFT_MARGIN - RIGHT_MARGIN)) {
383 if (c != ' ') {
384 size_t p = s_text.rfind(' ');
385 if (p != string::npos) {
386 s_text.erase(0, p+1);
387 s_style.erase(0, p+1);
388 if (s_text.length() < 10) { // wrap and delete trailing space
389 tbuf->remove(tbuf->length() - s_text.length(), tbuf->length());
390 sbuf->remove(sbuf->length() - s_style.length(), sbuf->length());
391 insert("\n"); // always insert new line
392 sbuf->append(s + 2);
393 insert(s_text.c_str());
394 sbuf->append(s_style.c_str());
395 wrapped = true;
396 }
397 }
398 }
399 if (!wrapped) { // add a new line if not wrapped
400 insert("\n");
401 sbuf->append(s + 2);
402 s_text.clear();
403 s_style.clear();
404 if (c != ' ') { // add character if not a space (no leading spaces)
405 for (int i = 0; cp[i]; ++i) {
406 sbuf->append(s + 2);
407 s_style.append(s + 2);
408 }
409 s_text.append(cp);
410 insert(cp);
411 }
412 }
413 } else {
414 for (int i = 0; cp[i]; ++i)
415 sbuf->append(s + 2);
416 insert(cp);
417 }
418 break;
419 }
420
421 // test for bottom of text visibility
422 if (// !mFastDisplay &&
423 (mVScrollBar->value() >= mNBufferLines - mNVisibleLines + mVScrollBar->linesize() - 1))
424 show_insert_position();
425 }
426
set_all_entry(bool b)427 void FTextRX::set_all_entry(bool b)
428 {
429 if (b)
430 menu[RX_MENU_ALL_ENTRY].set();
431 else
432 menu[RX_MENU_ALL_ENTRY].clear();
433 }
434
set_scroll_hints(bool b)435 void FTextRX::set_scroll_hints(bool b)
436 {
437 if (b)
438 menu[RX_MENU_SCROLL_HINTS].set();
439 else
440 menu[RX_MENU_SCROLL_HINTS].clear();
441 static_cast<MVScrollbar*>(mVScrollBar)->show_marks(b);
442 }
443
mark(FTextBase::TEXT_ATTR attr)444 void FTextRX::mark(FTextBase::TEXT_ATTR attr)
445 {
446 if (attr == NATTR)
447 attr = CLICK_START;
448 static_cast<MVScrollbar*>(mVScrollBar)->mark(styles[attr].color);
449 }
450
clear(void)451 void FTextRX::clear(void)
452 {
453 FTextBase::clear();
454 s_text.clear();
455 s_style.clear();
456 static_cast<MVScrollbar*>(mVScrollBar)->clear();
457 }
458
setFont(Fl_Font f,int attr)459 void FTextRX::setFont(Fl_Font f, int attr)
460 {
461 FTextBase::setFont(f, attr);
462 }
463
handle_clickable(int x,int y)464 int FTextRX::handle_clickable(int x, int y)
465 {
466 int pos;
467 unsigned int style;
468
469 pos = xy_to_position(x + this->x(), y + this->y(), CURSOR_POS);
470 // return unless clickable style
471 if ((style = (unsigned char)sbuf->byte_at(pos)) < CLICK_START + FTEXT_DEF)
472 return 0;
473
474 int start, end;
475 for (start = pos-1; start >= 0; start--)
476 if ((unsigned char)sbuf->byte_at(start) != style)
477 break;
478 start++;
479 int len = sbuf->length();
480 for (end = pos+1; end < len; end++)
481 if ((unsigned char)sbuf->byte_at(end) != style)
482 break;
483
484 switch (style - FTEXT_DEF) {
485 case QSY:
486 handle_qsy(start, end);
487 return 1;
488 break;
489 // ...
490 default:
491 break;
492 }
493 return 0;
494 }
495
handle_qsy(int start,int end)496 void FTextRX::handle_qsy(int start, int end)
497 {
498 char* text = tbuf->text_range(start, end);
499
500 extern map<string, qrg_mode_t> qrg_marks;
501 map<string, qrg_mode_t>::const_iterator i;
502 if ((i = qrg_marks.find(text)) != qrg_marks.end()) {
503 const qrg_mode_t& m = i->second;
504 if (active_modem->get_mode() != m.mode)
505 init_modem_sync(m.mode);
506 qsy(m.rfcarrier, m.carrier);
507 }
508
509 free(text);
510 }
511
512 static fre_t rst("^[1-5][123456789nN]{2}$", REG_EXTENDED | REG_NOSUB);
513 static fre_t loc("[a-r]{2}[[:digit:]]{2}([a-x]{2})?", REG_EXTENDED | REG_ICASE);
514 static fre_t call("([[:alnum:]]?[[:alpha:]/]+[[:digit:]]+[[:alnum:]/]+)", REG_EXTENDED);
515
set_cbo_county(string str)516 void set_cbo_county(string str)
517 {
518 inpCounty->value(str.c_str());
519 inpSQSO_county1->value(str.c_str());
520 inpSQSO_county2->value(str.c_str());
521
522 Cstates st;
523 if (inpState->value()[0])
524 cboCountyQSO->value(
525 string(st.state_short(inpState->value())).append(" ").
526 append(st.county(inpState->value(), inpCounty->value())).c_str());
527 else
528 cboCountyQSO->clear_entry();
529 cboCountyQSO->redraw();
530 }
531
set_QSO_call(const char * s)532 void set_QSO_call(const char *s)
533 {
534 if (progdefaults.clear_fields)
535 clearQSO();
536 std::string call = ucasestr(s);
537
538 inpCall1->value(call.c_str());
539 inpCall2->value(call.c_str());
540 inpCall3->value(call.c_str());
541 inpCall4->value(call.c_str());
542
543 if (progStatus.timer && (Fl::event() != FL_HIDE))
544 stopMacroTimer();
545
546 sDate_on = sDate_off = zdate();
547 sTime_on = sTime_off = ztime();
548
549 inpTimeOn->value(inpTimeOff->value(), inpTimeOff->size());
550 inpTimeOn1->value(inpTimeOff->value(), inpTimeOff->size());
551 inpTimeOn2->value(inpTimeOff->value(), inpTimeOff->size());
552 inpTimeOn3->value(inpTimeOff->value(), inpTimeOff->size());
553 inpTimeOn4->value(inpTimeOff->value(), inpTimeOff->size());
554 inpTimeOn5->value(inpTimeOff->value(), inpTimeOff->size());
555
556 updateOutSerNo();
557 }
558
set_cbo_Country(std::string c)559 void set_cbo_Country(std::string c)
560 {
561 cboCountryQSO->value(c.c_str());
562 cboCountryAICW2->value(c.c_str());
563 cboCountryAIDX2->value(c.c_str());
564 cboCountryCQDX2->value(c.c_str());
565 cboCountryCQ2->value(c.c_str());
566 cboCountryIARI2->value(c.c_str());
567 cboCountryRTU2->value(c.c_str());
568 // cboCountryWAE2->value(c.c_str());
569
570 if (progdefaults.logging == LOG_JOTA)
571 inp_JOTA_spc->value(c.c_str());
572 if (progdefaults.logging == LOG_ARR)
573 inpXchgIn->value(c.c_str());
574
575 }
576
set_zone(std::string z)577 void set_zone(std::string z)
578 {
579 inp_CQDXzone1->value(z.c_str());
580 inp_CQDXzone2->value(z.c_str());
581 inp_CQzone1->value(z.c_str());
582 inp_CQzone2->value(z.c_str());
583 }
584
set_name(std::string nm)585 void set_name(std::string nm)
586 {
587 inpName->value(nm.c_str());
588 inpName1->value(nm.c_str());
589 inpName2->value(nm.c_str());
590 inp_1010_name2->value(nm.c_str());
591 inp_ARR_Name2->value(nm.c_str());
592 inpNAQPname2->value(nm.c_str());
593 inp_ASCR_name2->value(nm.c_str());
594 }
595
set_rst_in(std::string rst)596 void set_rst_in(std::string rst)
597 {
598 for (size_t n = 0; n < rst.length(); n++)
599 if (rst[n] == 'N' || rst[n] == 'n') rst[n] = '9';
600 inpRstIn->value(rst.c_str());
601 inpRTU_RSTin2->value(rst.c_str());
602 inpRstIn1->value(rst.c_str());
603 inpRstIn2->value(rst.c_str());
604 inpRstIn3->value(rst.c_str());
605 inpRstIn4->value(rst.c_str());
606 inpRstIn_AICW2->value(rst.c_str());
607 inpRstIn_SQSO2->value(rst.c_str());
608 inpRstIn_WPX2->value(rst.c_str());
609 inp_IARI_RSTin2->value(rst.c_str());
610 // inpRstIn_WAE2->value(rst.c_str());
611 }
612
set_rst_out(std::string rst)613 void set_rst_out(std::string rst)
614 {
615 for (size_t n = 0; n < rst.length(); n++)
616 if (rst[n] == 'N' || rst[n] == 'n') rst[n] = '9';
617 inpRstOut->value(rst.c_str());
618 inpRstOut1->value(rst.c_str());
619 inpRstOut2->value(rst.c_str());
620 inpRstOut3->value(rst.c_str());
621 inpRstOut4->value(rst.c_str());
622 inpRstOut_AICW2->value(rst.c_str());
623 inpRstOut_SQSO2->value(rst.c_str());
624 inpRstOut_WPX2->value(rst.c_str());
625 inp_IARI_RSTout2->value(rst.c_str());
626 // inpRstOut_WAE2->value(rst.c_str());
627 }
628
set_rst(std::string rst)629 void set_rst(std::string rst)
630 {
631 if (inpRstIn->value()[0] == 0)
632 set_rst_in(rst);
633 else
634 set_rst_out(rst);
635 }
636
set_state(std::string s)637 void set_state(std::string s)
638 {
639 s = ucasestr(s);
640 inpState->value(s.c_str());
641 inpState1->value(s.c_str());
642 inp_CQstate1->value(s.c_str());
643 inp_CQstate2->value(s.c_str());
644 inp_KD_state1->value(s.c_str());
645 inp_KD_state2->value(s.c_str());
646 inpSQSO_state1->value(s.c_str());
647 inpSQSO_state2->value(s.c_str());
648 }
649
set_province(std::string pr)650 void set_province(std::string pr)
651 {
652 pr = ucasestr(pr);
653 inpVEprov->value(pr.c_str());
654 inp_KD_VEprov1->value(pr.c_str());
655 inp_KD_VEprov2->value(pr.c_str());
656 }
657
set_serno_in(std::string s)658 void set_serno_in(std::string s)
659 {
660 inpSerNo->value(s.c_str());
661 inpSerNo1->value(s.c_str());
662 inpSerNo2->value(s.c_str());
663 inpSerNo3->value(s.c_str());
664 inpSerNo4->value(s.c_str());
665 inpSerNo_WPX1->value(s.c_str());
666 inpSerNo_WPX2->value(s.c_str());
667 inpRTU_serno1->value(s.c_str());
668 inpRTU_serno2->value(s.c_str());
669 inpSQSO_serno1->value(s.c_str());
670 inpSQSO_serno2->value(s.c_str());
671 inp_IARI_SerNo1->value(s.c_str());
672 inp_IARI_SerNo2->value(s.c_str());
673 // inpSerNo_WAE1->value(s.c_str());
674 // inpSerNo_WAE2->value(s.c_str());
675 }
676
parseSQSO(std::string str)677 void parseSQSO(std::string str)
678 {
679 if (std::string(QSOparties.qso_parties[progdefaults.SQSOcontest].state) == "7QP" &&
680 str.length() == 5 && !inpState->value()[0]) {
681 set_state(str.substr(0,2).c_str());
682 set_cbo_county(str);
683 return;
684 }
685
686 if (std::string(QSOparties.qso_parties[progdefaults.SQSOcontest].state) == "6NE" &&
687 str.length() == 5 && !inpState->value()[0]) {
688 set_state(str.substr(str.length() - 2, 2).c_str());
689 set_cbo_county(str);
690 return;
691 }
692
693 if (progdefaults.SQSOinstate) {
694 if (state_test(str)) {
695 set_state(str);
696 return;
697 }
698 }
699
700 std::string st = inpState->value();
701 std::string inState = QSOparties.qso_parties[progdefaults.SQSOcontest].state;
702
703 if (st == "6NE" || st == "7QP") {
704 st.clear();
705 } else if (st.empty())
706 st = inState;
707
708 bool chkC = check_field(str, cCNTY, st);
709 bool chkP = check_field(str, cDIST, st);
710 bool chkCin = check_field(str, cCNTY, inState);
711 bool chkPin = check_field(str, cDIST, inState);
712
713 if ( QSOparties.qso_parties[progdefaults.SQSOcontest].st &&
714 !st.empty() &&
715 progdefaults.SQSOlogcounty &&
716 (chkC || chkP) ) {
717 if (progdefaults.SQSOinstate && !inpState->value()[0])
718 set_state(st);
719 set_cbo_county(states.cnty_short(st, str));
720 return;
721 }
722
723 if ((chkCin || chkPin) && inpCounty->value()[0] == 0) {
724 set_state(st.c_str());
725 set_cbo_county(states.cnty_short(st, str));
726 return;
727 }
728
729 if (progdefaults.SQSOlogstate &&
730 check_field(str, cSTATE) && !inpState->value()[0]) {
731 set_state(str);
732 return;
733 }
734
735 if (progdefaults.SQSOlogstate &&
736 check_field(str, cVE_PROV) && !inpState->value()[0]) {
737 set_state(str);
738 return;
739 }
740
741 if (section_test(str) && !inpState->value()[0]) {
742 set_state(str);
743 return;
744 }
745
746 if (check_field(str, cCOUNTRY)) {
747 cboCountry->value(country_match.c_str());
748 return;
749 }
750
751 if (progdefaults.SQSOlogserno && check_field(str, cNUMERIC) && !inpSerNo->value()[0]) {
752 set_serno_in(str);
753 return;
754 }
755
756 {
757 bool bCAT = (QSOparties.qso_parties[progdefaults.SQSOcontest].cat[0]);
758 string category = ucasestr(str);
759 if (bCAT &&
760 (category == "CLB" || category == "MOB" || category == "QRP" || category == "STD")) {
761 inpSQSO_category->value(category.c_str());
762 return;
763 }
764 }
765
766 if (!inpName->value()[0] && !isdigit(str[0])
767 && !chkC && !chkP && !chkCin && !chkPin) {
768 set_name(str);
769 return;
770 }
771
772 if (check_field(str, cRST) ) {
773 set_rst(str);
774 }
775
776 }
777
778 // capture 1, 2, or 3 sequential words from RX text
779 // 1 - left click on word
780 // 2 - shift-left click on first word
781 // 3 - ctrl-left click on first word
782 // 4 - shift-ctrl-left click on first word
783
handle_qso_data(int start,int end)784 int FTextRX::handle_qso_data(int start, int end)
785 {
786 if (start < 0 || end < 0) return 0;
787
788 num_words = 1;
789
790 if (Fl::event_state() & FL_SHIFT) {
791 num_words = 2;
792 }
793 if (Fl::event_state() & FL_CTRL) {
794 num_words = 3;
795 if (Fl::event_state() & FL_SHIFT) {
796 num_words = 4;
797 }
798 }
799
800 char *sz = get_word(start, end, progdefaults.nonwordchars.c_str(), num_words);
801
802 if (!sz)
803 return 0;
804
805 std::string sz_str = sz;
806 free(sz);
807
808 if (sz_str.empty())
809 return 0;
810
811 while (sz_str[sz_str.length() -1] <= ' ') sz_str.erase(sz_str.length() - 1);
812
813 // remove leading substrings such as 'loc:' 'qth:' 'op:' etc
814 size_t sp = std::string::npos;
815 if ((sp = sz_str.find(":")) != std::string::npos)
816 sz_str.erase(0, sp+1);
817
818 if (sz_str.empty())
819 return 0;
820
821 char* s = (char *)sz_str.c_str();
822 char* p = (char *)sz_str.c_str();
823
824 if (progdefaults.logging != LOG_QSO) {
825 if (loc.match(s)) { // force maidenhead match to exchange
826 // or it will overwrite the call
827 inpXchgIn->position(inpXchgIn->size());
828 if (inpXchgIn->size()) inpXchgIn->insert(" ", 1);
829 if (progdefaults.logging == LOG_VHF) {
830 inpLoc->value(s);
831 DupCheck();
832 } else {
833 inpXchgIn->insert(s);
834 log_callback(inpXchgIn);
835 DupCheck();
836 }
837 } else if (call.match(s)) { // point p to substring
838 const regmatch_t& offsets = call.suboff()[1];
839 p = s + offsets.rm_so;
840 *(s + offsets.rm_eo) = '\0';
841
842 set_QSO_call(p);
843
844 Fl::copy(p, strlen(p), 1); // copy to clipboard
845
846 if (std::string(QSOparties.qso_parties[progdefaults.SQSOcontest].state) == "6NE") {
847 set_state("");
848 }
849
850 const dxcc *e = dxcc_lookup(p);
851 if (e) {
852 std::ostringstream zone;
853 zone << e->cq_zone;
854 set_zone(zone.str());
855
856 std::string cntry = e->country;
857 if (cntry.find("United States") != std::string::npos)
858 cntry = "USA";
859 set_cbo_Country(cntry);
860 }
861 DupCheck();
862
863 } else {
864 std::string str = ucasestr(s);
865 if (cut_numeric_test(str)) str = cut_to_numeric(str);
866
867 switch (progdefaults.logging) {
868 case LOG_FD:
869 if (check_field(str, cFD_SECTION) && !inpSection->value()[0]) {
870 inpSection->value(str.c_str());
871 break;
872 }
873 if (check_field(str, cFD_CLASS) && !inpClass->value()[0]) {
874 inpClass->value(str.c_str());
875 break;
876 }
877 if (check_field(str, cRST)) {
878 set_rst(str);
879 break;
880 }
881 break;
882 case LOG_WFD:
883 if (check_field(str, cFD_SECTION) && !inpSection->value()[0]) {
884 inpSection->value(str.c_str());
885 break;
886 }
887 if (check_field(str, cWFD_CLASS) && !inpClass->value()[0]) {
888 inpClass->value(str.c_str());
889 break;
890 }
891 if (check_field(str, cRST)) {
892 set_rst(str);
893 break;
894 }
895 break;
896 case LOG_CQWW_DX:
897 if (check_field(str, cCOUNTRY)) {
898 set_cbo_Country(country_match);
899 break;
900 }
901 if (check_field(str, cNUMERIC) && !inp_CQzone->value()[0]) {
902 set_zone(str);
903 break;
904 }
905 if (check_field(str, cRST)) {
906 if (!inpRstIn->value()[0])
907 set_rst_in(str);
908 else if (!inpRstOut->value()[0])
909 set_rst_out(str);
910 }
911 break;
912 case LOG_CQWW_RTTY :
913 if ( (check_field(str, cSTATE) || check_field(str, cVE_PROV)) &&
914 !inp_CQstate->value()[0] ) {
915 inp_CQstate->value(str.c_str());
916 break;
917 }
918 if (check_field(str, cCOUNTRY)) {
919 set_cbo_Country(country_match);
920 break;
921 }
922 if (check_field(str, cNUMERIC) && !inp_CQzone->value()[0]) {
923 set_zone(str);
924 break;
925 }
926 if (check_field(str, cRST)) {
927 set_rst(str);
928 break;
929 }
930 break;
931 case LOG_KD:
932 if (!check_field(str, cNUMERIC) && inpName->value()[0] == 0) {
933 set_name(s);
934 break;
935 }
936 if (!check_field(str, cRST) &&
937 check_field(str, cNUMERIC) &&
938 inp_KD_age->value()[0] == 0) {
939 inp_KD_age->value(str.c_str());
940 break;
941 }
942 if (check_field(str, cSTATE) && !inpState->value()[0]) {
943 set_state(str);
944 break;
945 }
946 if (check_field(str, cVE_PROV) && !inpVEprov->value()[0]) {
947 set_province(str);
948 break;
949 }
950 if (check_field(str, cRST)) {
951 set_rst(str);
952 break;
953 }
954 if (!inpXchgIn->value()[0]) {
955 inpXchgIn->position(inpXchgIn->size());
956 if (inpXchgIn->size())
957 inpXchgIn->insert(" ", 1);
958 inpXchgIn->insert(str.c_str());
959 }
960 break;
961 case LOG_ASCR:
962 if (check_field(str, cASCR_CLASS) && !inpClass->value()[0]) {
963 inpClass->value(str.c_str());
964 break;
965 }
966 if (check_field(str, cSTATE) && !inpXchgIn->value()[0]) {
967 inpXchgIn->value(str.c_str());
968 break;
969 }
970 if (check_field(str, cVE_PROV) && !inpXchgIn->value()[0]) {
971 inpXchgIn->value(str.c_str());
972 break;
973 }
974 if (check_field(str, cRST)) {
975 set_rst(str);
976 break;
977 }
978 if (!inpName->value()[0]) {
979 set_name(s);
980 break;
981 }
982 inpXchgIn->value(s);
983 break;
984 case LOG_ARR: // rookie roundup
985 if (check_field(str, cRST)) {
986 set_rst(str);
987 break;
988 }
989 if (check_field(s, cROOKIE) && !inp_ARR_check->value()[0]) {
990 if (strlen(s) > 2)
991 inp_ARR_check->value(s + 2);
992 else
993 inp_ARR_check->value(s);
994 break;
995 }
996 if (!inpName->value()[0]) {
997 set_name(s);
998 break;
999 }
1000 if (check_field(str, cCHECK) && !inpXchgIn->value()[0]) {
1001 inpXchgIn->value(str.c_str());
1002 break;
1003 }
1004 if (!inpXchgIn->value()[0]) {
1005 inpXchgIn->value(s);
1006 break;
1007 }
1008 break;
1009 case LOG_AICW:
1010 if (check_field(str, cCOUNTRY)) {
1011 set_cbo_Country(country_match);
1012 break;
1013 }
1014 if (check_field(str, cNUMERIC) && !inpSPCnum->value()[0]) {
1015 inpSPCnum->value(str.c_str());
1016 break;
1017 }
1018 if (check_field(str, cRST)) {
1019 set_rst(str);
1020 break;
1021 }
1022 break;
1023 case LOG_1010:
1024 if (check_field(str, c1010) && !inp_1010_nr->value()[0]) {
1025 inp_1010_nr->value(str.c_str());
1026 break;
1027 }
1028 if (check_field(str, cRST)) {
1029 set_rst(str);
1030 break;
1031 }
1032 if (check_field(str, cSTATE) && !inpXchgIn->value()[0]) {
1033 inpXchgIn->value(str.c_str());
1034 break;
1035 }
1036 if (check_field(str, cVE_PROV) && !inpXchgIn->value()[0]) {
1037 inpXchgIn->value(str.c_str());
1038 break;
1039 }
1040 if (!inpName->value()[0]) {
1041 set_name(s);
1042 break;
1043 }
1044 inpXchgIn->value(s);
1045 break;
1046 case LOG_NAQP:
1047 if (!inpName->value()[0]) {
1048 set_name(s);
1049 break;
1050 } else if (!inpSPCnum->value()[0]) {
1051 inpSPCnum_NAQP1->value(s);
1052 inpSPCnum_NAQP2->value(s);
1053 inpXchgIn1->value(s);
1054 inpXchgIn2->value(s);
1055 break;
1056 }
1057 if (check_field(str, cRST)) {
1058 set_rst(str);
1059 break;
1060 }
1061 break;
1062 case LOG_CWSS:
1063 if (check_field(str, cSS_SEC) && !inp_SS_Section->value()[0]) {
1064 inp_SS_Section->value(str.c_str());
1065 break;
1066 }
1067 if (cut_numeric_test(str) && !inpSerNo->value()[0]) {
1068 set_serno_in(str);
1069 break;
1070 }
1071 if (check_field(str, cSS_PREC) && !inp_SS_Precedence->value()[0]) {
1072 inp_SS_Precedence->value(s);
1073 break;
1074 }
1075 if (check_field(str, cSS_CHK) && !inp_SS_Check->value()[0]) {
1076 inp_SS_Check->value(str.c_str());
1077 break;
1078 }
1079 if (check_field(str, cRST)) {
1080 set_rst(str);
1081 break;
1082 }
1083 break;
1084 case LOG_CQ_WPX:
1085 if (cut_numeric_test(str) && !inpSerNo->value()[0]) {
1086 set_serno_in(str);
1087 break;
1088 }
1089 if (check_field(str, cCOUNTRY)) {
1090 set_cbo_Country(country_match);
1091 break;
1092 }
1093 if (check_field(str, cRST)) {
1094 set_rst(str);
1095 break;
1096 }
1097 break;
1098 case LOG_RTTY: // ARRL RTTY Round Up
1099 if (check_field(str, cSTATE) || check_field(str, cVE_PROV)) {
1100 set_state(str);
1101 break;
1102 }
1103 if (check_field(str, cCOUNTRY)) {
1104 set_cbo_Country(country_match);
1105 break;
1106 }
1107 if (check_field(str, cRST)) {
1108 set_rst(str);
1109 break;
1110 }
1111 if (cut_numeric_test(str) && !inpSerNo->value()[0]) {
1112 set_serno_in(str);
1113 break;
1114 }
1115 break;
1116 case LOG_IARI:
1117 if (check_field(str, cITALIAN)) {
1118 inp_IARI_PR1->value(ucasestr(str).c_str());
1119 inp_IARI_PR2->value(ucasestr(str).c_str());
1120 break;
1121 }
1122 if (check_field(str, cCOUNTRY)) {
1123 set_cbo_Country(country_match);
1124 break;
1125 }
1126 if (check_field(str, cRST)) {
1127 set_rst(str);
1128 break;
1129 }
1130 if (cut_numeric_test(str) && !inpSerNo->value()[0]) {
1131 set_serno_in(str);
1132 break;
1133 }
1134 break;
1135 case LOG_NAS:
1136 if (cut_numeric_test(str) && !inpSerNo->value()[0]) {
1137 set_serno_in(str);
1138 break;
1139 }
1140 if (check_field(str, cSTATE) && !inpXchgIn->value()[0]) {
1141 inpXchgIn->value(str.c_str());
1142 break;
1143 }
1144 if (check_field(str, cVE_PROV) && !inpXchgIn->value()[0]) {
1145 inpXchgIn->value(str.c_str());
1146 break;
1147 }
1148 if (check_field(str, cCOUNTRY) && !inpXchgIn->value()[0]) {
1149 set_cbo_Country(str);
1150 inpXchgIn->value(str.c_str());
1151 break;
1152 }
1153 if (inpName->value()[0] == 0 && !inpName->value()[0]) {
1154 set_name(str);
1155 break;
1156 }
1157 if (check_field(s, cRST)) {
1158 set_rst(s);
1159 break;
1160 }
1161 break;
1162 case LOG_AIDX:
1163 if (check_field(str, cNUMERIC) && !inpSerNo->value()[0]) {
1164 set_serno_in(str);
1165 break;
1166 }
1167 if (check_field(str, cCOUNTRY) && !inpXchgIn->value()[0]) {
1168 set_cbo_Country(str);
1169 inpXchgIn->value(str.c_str());
1170 break;
1171 }
1172 if (check_field(str, cRST)) {
1173 set_rst(str);
1174 break;
1175 }
1176 break;
1177 case LOG_JOTA:
1178 if (check_field(str, cRST)) {
1179 set_rst(str);
1180 break;
1181 }
1182 if (cut_numeric_test(str) && !inp_JOTA_troop->value()[0]) {
1183 inp_JOTA_troop->value(str.c_str());
1184 break;
1185 }
1186 if (check_field(str, cSTATE)) {
1187 set_state(str);
1188 break;
1189 }
1190 if (check_field(str, cVE_PROV)) {
1191 set_province(str);
1192 break;
1193 }
1194 if (check_field(str, cCOUNTRY)) {
1195 set_cbo_Country(str);
1196 inpXchgIn->value(str.c_str());
1197 break;
1198 }
1199 inp_JOTA_scout->value(str.c_str());
1200 break;
1201 // case LOG_WAE:
1202 // if (!inpSerNo->value()[0] && check_field(str, cNUMERIC)) {
1203 // set_serno_in(str);
1204 // break;
1205 // }
1206 // if (check_field(str, cCOUNTRY)) {
1207 // cboCountryCQ1->value(country_match.c_str());
1208 // cboCountryCQ2->value(country_match.c_str());
1209 // cboCountry->value(country_match.c_str());
1210 // }
1211 // if (check_field(s, cRST)) {
1212 // set_rst(s);
1213 // break;
1214 // }
1215 // break;
1216 case LOG_VHF:
1217 if (check_field(str, cRST))
1218 set_rst(str);
1219 break;
1220 case LOG_SQSO:
1221 parseSQSO(str);
1222 break;
1223 case LOG_BART:
1224 // CALL, NAME, SERIAL, EXCHANGE
1225 if (!cut_numeric_test(s) && !inpName->value()[0]) {
1226 set_name(p);
1227 break;
1228 } else if (cut_numeric_test(str) && !inpSerNo->value()[0]) {
1229 set_serno_in(str);
1230 break;
1231 }
1232 case LOG_GENERIC:
1233 default:
1234 // EXCHANGE
1235 inpXchgIn->position(inpXchgIn->size());
1236 if (inpXchgIn->size()) inpXchgIn->insert(" ", 1);
1237 if (cut_numeric_test(str))
1238 inpXchgIn->insert(str.c_str());
1239 else
1240 inpXchgIn->insert(s);
1241 log_callback(inpXchgIn);
1242 }
1243 }
1244 DupCheck();
1245 restoreFocus(91);
1246 return 1;
1247 } else {
1248 if (loc.match(s) && inpCall->value()[0]) {
1249 inpLoc->value(p);
1250 log_callback(inpLoc);
1251 restoreFocus();
1252 DupCheck();
1253 return 1;
1254 } else if (call.match(s)) { // point p to substring
1255 const regmatch_t& offsets = call.suboff()[1];
1256 p = s + offsets.rm_so;
1257 *(s + offsets.rm_eo) = '\0';
1258 Fl::copy(p, strlen(p), 1); // copy to clipboard
1259 set_QSO_call(p);
1260 log_callback(inpCall);
1261 restoreFocus();
1262 return 1;
1263 } else if (rst.match(s)) {
1264 set_rst(s);
1265 restoreFocus();
1266 return 1;
1267 } else if (!inpName->value()[0]) {
1268 set_name(p);
1269 restoreFocus();
1270 return 1;
1271 } else if (!inpQTH->value()[0]) {
1272 inpQTH->value(p);
1273 log_callback(inpQTH);
1274 restoreFocus();
1275 return 1;
1276 } else if (!inpState->value()[0]) {
1277 set_state(p);
1278 log_callback(inpState);
1279 restoreFocus();
1280 DupCheck();
1281 return 1;
1282 }
1283 }
1284 return 0;
1285 }
1286
handle_context_menu(void)1287 void FTextRX::handle_context_menu(void)
1288 {
1289 bool contest_ui = (progdefaults.logging != LOG_QSO);
1290
1291 num_words = 1;
1292
1293 if (Fl::event_state() & FL_SHIFT) {
1294 num_words = 2;
1295 }
1296 if (Fl::event_state() & FL_CTRL) {
1297 num_words = 3;
1298 if (Fl::event_state() & FL_SHIFT) {
1299 num_words = 4;
1300 }
1301 }
1302
1303 unsigned shown[RX_MENU_NUM_ITEMS];
1304 for (int i = 0; i < RX_MENU_NUM_ITEMS; shown[i++] = 0); // all hidden
1305
1306 #define show_item(x_) (shown[x_] = 1)
1307 #define hide_item(x_) (shown[x_] = 0)
1308 #define test_item(x_) (shown[x_] == 1)
1309
1310 show_item(RX_MENU_CALL);
1311
1312 if (contest_ui) {
1313 switch (progdefaults.logging) {
1314 case LOG_FD:
1315 case LOG_WFD:
1316 show_item(RX_MENU_CLASS);
1317 show_item(RX_MENU_SECTION);
1318 break;
1319 case LOG_CQ_WPX:
1320 show_item(RX_MENU_RST_IN);
1321 show_item(RX_MENU_RST_OUT);
1322 show_item(RX_MENU_CQZONE);
1323 show_item(RX_MENU_COUNTRY);
1324 show_item(RX_MENU_SERIAL);
1325 break;
1326 case LOG_CQWW_DX:
1327 show_item(RX_MENU_RST_IN);
1328 show_item(RX_MENU_RST_OUT);
1329 show_item(RX_MENU_CQZONE);
1330 show_item(RX_MENU_COUNTRY);
1331 break;
1332 case LOG_CQWW_RTTY:
1333 show_item(RX_MENU_RST_IN);
1334 show_item(RX_MENU_RST_OUT);
1335 show_item(RX_MENU_CQZONE);
1336 show_item(RX_MENU_CQSTATE);
1337 show_item(RX_MENU_COUNTRY);
1338 break;
1339 case LOG_ASCR:
1340 show_item(RX_MENU_NAME);
1341 show_item(RX_MENU_CLASS);
1342 show_item(RX_MENU_RST_IN);
1343 show_item(RX_MENU_RST_OUT);
1344 show_item(RX_MENU_XCHG);
1345 break;
1346 case LOG_VHF:
1347 show_item(RX_MENU_RST_IN);
1348 show_item(RX_MENU_RST_OUT);
1349 show_item(RX_MENU_LOC);
1350 break;
1351 case LOG_CWSS:
1352 show_item(RX_MENU_SS_SER);
1353 show_item(RX_MENU_SS_PRE);
1354 show_item(RX_MENU_SS_CHK);
1355 show_item(RX_MENU_SS_SEC);
1356 show_item(RX_MENU_RST_IN);
1357 break;
1358 case LOG_1010:
1359 show_item(RX_MENU_NAME);
1360 show_item(RX_MENU_1010_NR);
1361 show_item(RX_MENU_XCHG);
1362 break;
1363 case LOG_ARR:
1364 show_item(RX_MENU_NAME);
1365 show_item(RX_MENU_CHECK);
1366 show_item(RX_MENU_XCHG);
1367 break;
1368 case LOG_AICW:
1369 show_item(RX_MENU_RST_IN);
1370 show_item(RX_MENU_RST_OUT);
1371 show_item(RX_MENU_POWER);
1372 show_item(RX_MENU_COUNTRY);
1373 break;
1374 case LOG_KD:
1375 show_item(RX_MENU_RST_IN);
1376 show_item(RX_MENU_RST_OUT);
1377 show_item(RX_MENU_NAME);
1378 show_item(RX_MENU_AGE);
1379 show_item(RX_MENU_STATE);
1380 show_item(RX_MENU_PROVINCE);
1381 show_item(RX_MENU_XCHG);
1382 break;
1383 case LOG_NAS:
1384 show_item(RX_MENU_NAME);
1385 show_item(RX_MENU_SERIAL);
1386 show_item(RX_MENU_STATE);
1387 show_item(RX_MENU_PROVINCE);
1388 show_item(RX_MENU_COUNTRY);
1389 break;
1390 case LOG_AIDX:
1391 show_item(RX_MENU_SERIAL);
1392 show_item(RX_MENU_RST_IN);
1393 show_item(RX_MENU_RST_OUT);
1394 show_item(RX_MENU_COUNTRY);
1395 break;
1396 case LOG_NAQP:
1397 show_item(RX_MENU_NAME);
1398 show_item(RX_MENU_NAQP);
1399 break;
1400 case LOG_RTTY:
1401 show_item(RX_MENU_RST_IN);
1402 show_item(RX_MENU_RST_OUT);
1403 show_item(RX_MENU_STATE);
1404 show_item(RX_MENU_COUNTRY);
1405 show_item(RX_MENU_SERIAL);
1406 break;
1407 case LOG_IARI:
1408 show_item(RX_MENU_RST_IN);
1409 show_item(RX_MENU_RST_OUT);
1410 show_item(RX_MENU_COUNTRY);
1411 show_item(RX_MENU_PROVINCE);
1412 show_item(RX_MENU_SERIAL);
1413 break;
1414 case LOG_JOTA:
1415 show_item(RX_MENU_RST_IN);
1416 show_item(RX_MENU_RST_OUT);
1417 show_item(RX_MENU_SCOUT);
1418 show_item(RX_MENU_TROOP);
1419 show_item(RX_MENU_STATE);
1420 show_item(RX_MENU_PROVINCE);
1421 show_item(RX_MENU_COUNTRY);
1422 break;
1423 case LOG_SQSO:
1424 show_item(RX_MENU_RST_IN);
1425 show_item(RX_MENU_RST_OUT);
1426 show_item(RX_MENU_QSOP_STATE);
1427 show_item(RX_MENU_QSOP_COUNTY);
1428 show_item(RX_MENU_QSOP_SERNO);
1429 show_item(RX_MENU_QSOP_NAME);
1430 show_item(RX_MENU_QSOP_CAT);
1431 break;
1432 // case LOG_WAE:
1433 // show_item(RX_MENU_RST_IN);
1434 // show_item(RX_MENU_RST_OUT);
1435 // show_item(RX_MENU_SERIAL);
1436 // show_item(RX_MENU_COUNTRY);
1437 // break;
1438 case LOG_BART:
1439 show_item(RX_MENU_NAME);
1440 show_item(RX_MENU_SERIAL);
1441 show_item(RX_MENU_XCHG);
1442 show_item(RX_MENU_RST_IN);
1443 break;
1444 case LOG_GENERIC:
1445 default:
1446 show_item(RX_MENU_SERIAL);
1447 show_item(RX_MENU_XCHG);
1448 show_item(RX_MENU_RST_IN);
1449 break;
1450 }
1451 }
1452 else {
1453 show_item(RX_MENU_NAME);
1454 show_item(RX_MENU_QTH);
1455 show_item(RX_MENU_RST_IN);
1456 show_item(RX_MENU_RST_OUT);
1457 // "Look up call" shown only in non-contest mode
1458 if (progdefaults.QRZWEB != QRZWEBNONE || progdefaults.QRZXML != QRZXMLNONE)
1459 show_item(RX_MENU_QRZ_THIS);
1460 menu[RX_MENU_CALL].flags |= FL_MENU_DIVIDER;
1461 }
1462
1463 if (menu[RX_MENU_ALL_ENTRY].value()) {
1464 for (size_t i = RX_MENU_NAME; i <= RX_MENU_RST_OUT; i++)
1465 show_item(i);
1466 menu[RX_MENU_CALL].flags &= ~FL_MENU_DIVIDER;
1467 }
1468
1469
1470 if (static_cast<MVScrollbar*>(mVScrollBar)->has_marks())
1471 menu[RX_MENU_SCROLL_HINTS].show();
1472 else
1473 menu[RX_MENU_SCROLL_HINTS].hide();
1474
1475 for (size_t i = RX_MENU_QRZ_THIS; i < RX_MENU_DIV; i++) {
1476 if (test_item(i))
1477 menu[i].show();
1478 else
1479 menu[i].hide();
1480 }
1481
1482 #undef show_item
1483 #undef hide_item
1484 #undef test_item
1485
1486 // availability of editing items depend on buffer state
1487 icons::set_active(&menu[RX_MENU_COPY], tbuf->selected());
1488 icons::set_active(&menu[RX_MENU_CLEAR], tbuf->length());
1489 icons::set_active(&menu[RX_MENU_SELECT_ALL], tbuf->length());
1490 icons::set_active(&menu[RX_MENU_SAVE], tbuf->length());
1491
1492 if (wrap)
1493 menu[RX_MENU_WRAP].set();
1494 else
1495 menu[RX_MENU_WRAP].clear();
1496
1497 show_context_menu();
1498 }
1499
1500 /// The context menu handler
1501 ///
1502 /// @param val
1503 ///
menu_cb(size_t item)1504 void FTextRX::menu_cb(size_t item)
1505 {
1506 Fl_Input2* input = 0;
1507
1508 std::string s;
1509 char* str = get_word(popx, popy, "", 1, false);
1510
1511 if (str) {
1512 s = str;
1513 free(str);
1514 }
1515
1516 // remove leading substrings such as 'loc:' 'qth:' 'op:' etc
1517 size_t sp = std::string::npos;
1518 if ((sp = s.find(":")) != std::string::npos)
1519 s.erase(0, sp+1);
1520
1521 if (!s.empty())
1522 while (s[s.length() -1] <= ' ') s.erase(s.length() - 1);
1523
1524 if (s.empty()) {
1525 switch (item) {
1526 case RX_MENU_CLEAR:
1527 clear();
1528 break;
1529 case RX_MENU_SELECT_ALL:
1530 tbuf->select(0, tbuf->length());
1531 break;
1532 case RX_MENU_SAVE:
1533 saveFile();
1534 break;
1535 case RX_MENU_ALL_ENTRY:
1536 menu[item].flags ^= FL_MENU_VALUE;
1537 if (menu[item].value())
1538 handle_context_menu();
1539 break;
1540 case RX_MENU_WRAP:
1541 set_word_wrap(!wrap, true);
1542 break;
1543 case RX_MENU_DIV:
1544 note_qrg(false, "\n", "\n");
1545 break;
1546 case RX_MENU_SCROLL_HINTS:
1547 menu[item].flags ^= FL_MENU_VALUE;
1548 static_cast<MVScrollbar*>(mVScrollBar)->show_marks(menu[item].value());
1549 break;
1550 default: ;
1551 }
1552 return;
1553 }
1554
1555 switch (item) {
1556 case RX_MENU_QRZ_THIS:
1557 menu_cb(RX_MENU_CALL);
1558 extern void CALLSIGNquery();
1559 CALLSIGNquery();
1560 break;
1561 case RX_MENU_CALL:
1562 input = inpCall;
1563 break;
1564 case RX_MENU_NAME:
1565 input = inpName;
1566 break;
1567 case RX_MENU_QTH:
1568 input = inpQTH;
1569 break;
1570 case RX_MENU_STATE:
1571 if (progdefaults.logging == LOG_NAS)
1572 input = inpXchgIn;
1573 else if (progdefaults.logging == LOG_JOTA)
1574 input = inp_JOTA_spc;
1575 else
1576 input = inpState;
1577 break;
1578 case RX_MENU_LOC:
1579 input = inpLoc;
1580 break;
1581 case RX_MENU_RST_IN:
1582 input = inpRstIn;
1583 break;
1584 case RX_MENU_RST_OUT:
1585 input = inpRstOut;
1586 break;
1587 case RX_MENU_SERIAL:
1588 if (progdefaults.logging == LOG_IARI)
1589 input = inpXchgIn;
1590 else
1591 input = inpSerNo;
1592 break;
1593 case RX_MENU_XCHG:
1594 input = inpXchgIn;
1595 break;
1596 case RX_MENU_POWER:
1597 input = inpSPCnum;
1598 break;
1599 case RX_MENU_CLASS:
1600 input = inpClass;
1601 break;
1602 case RX_MENU_SECTION:
1603 input = inpSection;
1604 break;
1605 case RX_MENU_SS_SER:
1606 input = inp_SS_SerialNoR;
1607 break;
1608 case RX_MENU_SS_PRE:
1609 input = inp_SS_Precedence;
1610 break;
1611 case RX_MENU_SS_CHK:
1612 input = inp_SS_Check;
1613 break;
1614 case RX_MENU_SS_SEC:
1615 input = inp_SS_Section;
1616 break;
1617 case RX_MENU_CQZONE:
1618 input = inp_CQzone;
1619 break;
1620 case RX_MENU_CQSTATE:
1621 input = inp_CQstate;
1622 break;
1623 case RX_MENU_1010_NR:
1624 input = inp_1010_nr;
1625 break;
1626 case RX_MENU_AGE:
1627 input = inp_KD_age;
1628 break;
1629 case RX_MENU_CHECK:
1630 input = inp_ARR_check;
1631 break;
1632 case RX_MENU_NAQP:
1633 input = inpSPCnum;
1634 break;
1635 case RX_MENU_SCOUT:
1636 input = inp_JOTA_scout;
1637 break;
1638 case RX_MENU_TROOP:
1639 input = inp_JOTA_troop;
1640 break;
1641 case RX_MENU_QSOP_STATE:
1642 input = inpState;
1643 break;
1644 case RX_MENU_QSOP_COUNTY:
1645 input = inpCounty;
1646 break;
1647 case RX_MENU_QSOP_SERNO:
1648 input = inpSerNo;
1649 break;
1650 case RX_MENU_QSOP_NAME:
1651 input = inpName;
1652 break;
1653 case RX_MENU_QSOP_XCHG:
1654 input = inpXchgIn;
1655 break;
1656 case RX_MENU_QSOP_CAT:
1657 input = inpXchgIn;
1658 break;
1659 case RX_MENU_DIV:
1660 note_qrg(false, "\n", "\n");
1661 break;
1662 case RX_MENU_COPY:
1663 kf_copy(Fl::event_key(), this);
1664 break;
1665 case RX_MENU_CLEAR:
1666 clear();
1667 break;
1668 case RX_MENU_SELECT_ALL:
1669 tbuf->select(0, tbuf->length());
1670 break;
1671 case RX_MENU_SAVE:
1672 saveFile();
1673 break;
1674 case RX_MENU_ALL_ENTRY:
1675 menu[item].flags ^= FL_MENU_VALUE;
1676 if (menu[item].value())
1677 handle_context_menu();
1678 break;
1679 case RX_MENU_WRAP:
1680 set_word_wrap(!wrap, true);
1681 break;
1682 case RX_MENU_SCROLL_HINTS:
1683 menu[item].flags ^= FL_MENU_VALUE;
1684 static_cast<MVScrollbar*>(mVScrollBar)->show_marks(menu[item].value());
1685 break;
1686 case RX_MENU_COUNTRY:
1687 if (progdefaults.logging == LOG_NAS)
1688 inpXchgIn->value(s.c_str());
1689 else if (progdefaults.logging == LOG_JOTA)
1690 inp_JOTA_spc->value(s.c_str());
1691 else
1692 cboCountry->value(s.c_str());
1693 return;
1694 case RX_MENU_PROVINCE:
1695 if (progdefaults.logging == LOG_NAS)
1696 inpXchgIn->value(s.c_str());
1697 else if (progdefaults.logging == LOG_JOTA)
1698 inp_JOTA_spc->value(s.c_str());
1699 else if (progdefaults.logging == LOG_IARI)
1700 inpXchgIn->value(s.c_str());
1701 else
1702 set_province(s);
1703 return;
1704 case RX_MENU_COUNTY:
1705 set_cbo_county(s.c_str());
1706 return;
1707 default:
1708 return;
1709 }
1710
1711 restoreFocus(92);
1712
1713 if (!input)
1714 return;
1715
1716 if (item == RX_MENU_XCHG) { // append
1717 input->position(input->size());
1718 if (input->size())
1719 input->insert(" ", 1);
1720 input->insert(s.c_str());
1721 }
1722 else if (item == RX_MENU_SECTION) {
1723 if (check_field(ucasestr(s).c_str(), cFD_SECTION))
1724 input->value(ucasestr(s).c_str());
1725 } else if (item == RX_MENU_CLASS) {
1726 if (check_field(ucasestr(s).c_str(), cFD_CLASS))
1727 input->value(ucasestr(s).c_str());
1728 } else if (item == RX_MENU_RST_IN) {
1729 if (check_field(s, cRST)) {
1730 set_rst_in(s);
1731 }
1732 } else if (item == RX_MENU_RST_OUT) {
1733 if (check_field(s, cRST))
1734 set_rst_out(s);
1735 } else {
1736 if (input == inpCounty)
1737 set_cbo_county(s.c_str());
1738 else {
1739 input->value(s.c_str());
1740 log_callback(input);
1741 }
1742 }
1743 }
1744
dxcc_lookup_call(int x,int y)1745 const char* FTextRX::dxcc_lookup_call(int x, int y)
1746 {
1747 char* s = get_word(x - this->x(), y - this->y(), progdefaults.nonwordchars.c_str());
1748 char* mem = s;
1749 if (!(s && *s && call.match(s))) {
1750 free(s);
1751 return 0;
1752 }
1753
1754 double lon1, lat1, lon2 = 360.0, lat2 = 360.0, distance, azimuth;
1755 static string tip;
1756 ostringstream stip;
1757 const dxcc* e = 0;
1758 cQsoRec* qso = 0;
1759 unsigned char qsl;
1760
1761 // prevent locator-only lookup if Ctrl is held
1762 if (!(Fl::event_state() & FL_CTRL) && loc.match(s)) {
1763 const vector<regmatch_t>& v = loc.suboff();
1764 s += v[0].rm_so;
1765 *(s + v[0].rm_eo) = '\0';
1766 if (QRB::locator2longlat(&lon2, &lat2, s) != QRB::QRB_OK)
1767 goto ret;
1768 e = 0; qsl = 0; qso = 0;
1769 }
1770 else {
1771 e = dxcc_lookup(s);
1772 qsl = qsl_lookup(s);
1773 qso = SearchLog(s);
1774 }
1775
1776 if (qso && QRB::locator2longlat(&lon2, &lat2, qso->getField(GRIDSQUARE)) != QRB::QRB_OK)
1777 lon2 = lat2 = 360.0;
1778
1779 if (e) {
1780 // use dxcc data if we didn't have a good locator string in the log file
1781 if (lon2 == 360.0)
1782 lon2 = -e->longitude;
1783 if (lat2 == 360.0)
1784 lat2 = e->latitude;
1785 stip << e->country << " (" << e->continent
1786 << " GMT" << fixed << showpos << setprecision(1) << -e->gmt_offset << noshowpos
1787 << ") CQ-" << e->cq_zone << " ITU-" << e->itu_zone << '\n';
1788 }
1789
1790 if (QRB::locator2longlat(&lon1, &lat1, progdefaults.myLocator.c_str()) == QRB::QRB_OK &&
1791 QRB::qrb(lon1, lat1, lon2, lat2, &distance, &azimuth) == QRB::QRB_OK) {
1792 if (progdefaults.us_units) {
1793 stip << "QTE " << fixed << setprecision(0) << azimuth << '\260' << " ("
1794 << QRB::azimuth_long_path(azimuth) << '\260' << ") QRB "
1795 << distance * 0.62168188 << "mi"<< " (" <<
1796 QRB::distance_long_path(distance) * 0.62168188 <<
1797 "mi)\n";
1798 }
1799 else {
1800 stip << "QTE " << fixed << setprecision(0) << azimuth << '\260' << " ("
1801 << QRB::azimuth_long_path(azimuth) << '\260' << ") QRB "
1802 << distance << "km(" <<
1803 QRB::distance_long_path(distance) << "km)\n";
1804 }
1805 }
1806
1807 if (qso) {
1808 const char* info[] = {
1809 qso->getField(NAME), qso->getField(QTH), qso->getField(QSO_DATE),
1810 qso->getField(BAND), qso->getField(ADIF_MODE)
1811 };
1812 // name & qth
1813 if (*info[0])
1814 join(stip << "* ", info, 2, _(" in "), true) << '\n';
1815 // other info
1816 join(stip << "* " << _("Last QSO") << ": ", info+2, 3, ", ", true) << '\n';
1817 }
1818 if (qsl) {
1819 stip << "* QSL: ";
1820 for (unsigned char i = 0; i < QSL_END; i++)
1821 if (qsl & (1 << i))
1822 stip << qsl_names[i] << ' ';
1823 stip << '\n';
1824 }
1825
1826 ret:
1827 free(mem);
1828 tip = stip.str();
1829 return tip.empty() ? 0 : tip.c_str();
1830 }
1831
dxcc_tooltip(void * obj)1832 void FTextRX::dxcc_tooltip(void* obj)
1833 {
1834 struct point {
1835 int x, y;
1836 bool operator==(const point& p) { return x == p.x && y == p.y; }
1837 bool operator!=(const point& p) { return !(*this == p); }
1838 };
1839 static point p[3] = { {0, 0}, {0, 0}, {0, 0} };
1840
1841 memmove(p, p+1, 2 * sizeof(point));
1842 p[2].x = Fl::event_x(); p[2].y = Fl::event_y();
1843
1844 static const char* tip = 0;
1845 FTextRX* v = reinterpret_cast<FTextRX*>(obj);
1846 // look up word under cursor if we have been called twice with the cursor
1847 // at the same position, and if the cursor was previously somewhere else
1848 if (p[2] == p[1] && p[2] != p[0] && ((tip = v->dxcc_lookup_call(p[2].x, p[2].y))))
1849 Fl_Tooltip::enter_area(v, p[2].x, p[2].y, 100, 100, tip);
1850 else if (p[2] != p[1])
1851 Fl_Tooltip::exit(v);
1852
1853 Fl::repeat_timeout(tip ? Fl_Tooltip::hoverdelay() : v->tooltips.delay / 2.0, dxcc_tooltip, obj);
1854 }
1855
1856
1857 // ----------------------------------------------------------------------------
1858
1859
1860 Fl_Menu_Item FTextTX::menu[] = {
1861 { icons::make_icon_label(_("Transmit"), tx_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
1862 { icons::make_icon_label(_("Receive"), rx_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
1863 { icons::make_icon_label(_("Abort"), process_stop_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
1864 { icons::make_icon_label(_("Send image..."), image_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
1865
1866 { 0 }, // EDIT_MENU_CUT
1867 { 0 }, // EDIT_MENU_COPY
1868 { 0 }, // EDIT_MENU_PASTE
1869 { 0 }, // EDIT_MENU_CLEAR
1870 { 0 }, // EDIT_MENU_READ
1871 { 0 }, // EDIT_MENU_WRAP
1872
1873 { _("Spec Char"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1874 { "¢ - cent", 0, 0, 0, 0, FL_NORMAL_LABEL },
1875 { "£ - pound", 0, 0, 0, 0, FL_NORMAL_LABEL },
1876 { "µ - micro", 0, 0, 0, 0, FL_NORMAL_LABEL },
1877 { "° - degree", 0, 0, 0, 0, FL_NORMAL_LABEL },
1878 { "¿ - iques", 0, 0, 0, 0, FL_NORMAL_LABEL },
1879 { "× - times", 0, 0, 0, 0, FL_NORMAL_LABEL },
1880 { "÷ - divide", 0, 0, 0, 0, FL_NORMAL_LABEL },
1881 { _("A"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1882 { "À - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1883 { "à - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1884 { "Á - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1885 { "á - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1886 { "Â - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1887 { "â - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1888 { "Ã - tilde", 0, 0, 0, 0, FL_NORMAL_LABEL },
1889 { "ã - tilde", 0, 0, 0, 0, FL_NORMAL_LABEL },
1890 { "Ä - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1891 { "ä - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1892 { "Å - ring", 0, 0, 0, 0, FL_NORMAL_LABEL },
1893 { "å - ring", 0, 0, 0, 0, FL_NORMAL_LABEL },
1894 { "Æ - aelig", 0, 0, 0, 0, FL_NORMAL_LABEL },
1895 { "æ - aelig", 0, 0, 0, 0, FL_NORMAL_LABEL },
1896 {0,0,0,0,0,0,0,0,0},
1897 { _("E"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1898 { "È - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1899 { "è - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1900 { "É - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1901 { "é - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1902 { "Ê - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1903 { "Ê - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1904 { "Ë - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1905 { "ë - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1906 {0,0,0,0,0,0,0,0,0},
1907 { _("I"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1908 { "Ì - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1909 { "ì - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1910 { "Í - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1911 { "í - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1912 { "Î - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1913 { "î - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1914 { "Ï - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1915 { "ï - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1916 {0,0,0,0,0,0,0,0,0},
1917 { _("N"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1918 { "Ñ - tilde", 0, 0, 0, 0, FL_NORMAL_LABEL },
1919 { "ñ - tilde", 0, 0, 0, 0, FL_NORMAL_LABEL },
1920 {0,0,0,0,0,0,0,0,0},
1921 { _("O"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1922 { "Ò - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1923 { "ò - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1924 { "Ó - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1925 { "ó - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1926 { "Ô - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1927 { "ô - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1928 { "Õ - tilde", 0, 0, 0, 0, FL_NORMAL_LABEL },
1929 { "õ - tilde", 0, 0, 0, 0, FL_NORMAL_LABEL },
1930 { "Ö - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1931 { "ö - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1932 { "Ø - slash", 0, 0, 0, 0, FL_NORMAL_LABEL },
1933 { "ø - slash", 0, 0, 0, 0, FL_NORMAL_LABEL },
1934 {0,0,0,0,0,0,0,0,0},
1935 { _("U"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1936 { "Ù - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1937 { "ù - grave", 0, 0, 0, 0, FL_NORMAL_LABEL },
1938 { "Ú - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1939 { "ú - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1940 { "Û - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1941 { "û - circ", 0, 0, 0, 0, FL_NORMAL_LABEL },
1942 { "Ü - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1943 { "ü - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1944 {0,0,0,0,0,0,0,0,0},
1945 { _("Y"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1946 { "Ý - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1947 { "ý - acute", 0, 0, 0, 0, FL_NORMAL_LABEL },
1948 { "ÿ - umlaut", 0, 0, 0, 0, FL_NORMAL_LABEL },
1949 {0,0,0,0,0,0,0,0,0},
1950 { _("Other"), 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL },
1951 { "ß - szlig", 0, 0, 0, 0, FL_NORMAL_LABEL },
1952 { "Ç - cedil", 0, 0, 0, 0, FL_NORMAL_LABEL },
1953 { "ç - cedil", 0, 0, 0, 0, FL_NORMAL_LABEL },
1954 { "Ð - eth", 0, 0, 0, 0, FL_NORMAL_LABEL },
1955 { "ð - eth", 0, 0, 0, 0, FL_NORMAL_LABEL },
1956 { "Þ - thorn", 0, 0, 0, 0, FL_NORMAL_LABEL },
1957 {0,0,0,0,0,0,0,0,0},
1958 {0,0,0,0,0,0,0,0,0},
1959 { 0 }
1960 };
1961
1962 // needed by our static kf functions, which may restrict editing depending on
1963 // the transmit cursor position
1964 int *FTextTX::ptxpos;
1965
FTextTX(int x,int y,int w,int h,const char * l)1966 FTextTX::FTextTX(int x, int y, int w, int h, const char *l)
1967 : FTextEdit(x, y, w, h, l),
1968 PauseBreak(false), txpos(0), bkspaces(0)
1969 {
1970 ptxpos = &txpos;
1971
1972 change_keybindings();
1973
1974 memcpy(menu + TX_MENU_CUT, FTextEdit::menu, (FTextEdit::menu->size() - 1) * sizeof(*FTextEdit::menu));
1975 context_menu = menu;
1976 init_context_menu();
1977 utf8_txpos = txpos = 0;
1978 }
1979
1980 /// Handles fltk events for this widget.
1981 /// We pass keyboard events to handle_key() and handle mouse3 presses to show
1982 /// the popup menu. We also disallow mouse2 events in the transmitted text area.
1983 /// Everything else is passed to the base class handle().
1984 ///
1985 /// @param event
1986 ///
1987 /// @return
1988 ///
handle(int event)1989 int FTextTX::handle(int event)
1990 {
1991 if ( !(Fl::event_inside(this) || (event == FL_KEYBOARD && Fl::focus() == this)) )
1992 return FTextEdit::handle(event);
1993
1994 switch (event) {
1995 case FL_KEYBOARD:
1996 if (active_modem->get_mode() == MODE_FSQ) {
1997 if (Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter) {
1998 fsq_transmit(0);
1999 return 1;
2000 }
2001 }
2002 return handle_key(Fl::event_key()) ? 1 : FTextEdit::handle(event);
2003 case FL_PUSH:
2004 if (Fl::event_button() == FL_MIDDLE_MOUSE &&
2005 xy_to_position(Fl::event_x(), Fl::event_y(), CHARACTER_POS) < txpos)
2006 return 1; // ignore mouse2 text pastes inside the transmitted text
2007 }
2008
2009 return FTextEdit::handle(event);
2010 }
2011
2012 /// Clears the buffer.
2013 /// Also resets the transmit position, stored backspaces and tx pause flag.
2014 ///
clear(void)2015 void FTextTX::clear(void)
2016 {
2017 FTextEdit::clear();
2018 txpos = 0;
2019 utf8_txpos = 0;
2020 bkspaces = 0;
2021 PauseBreak = false;
2022 }
2023
2024 /// Clears the sent text.
2025 /// Also resets the transmit position, stored backspaces and tx pause flag.
2026 ///
clear_sent(void)2027 void FTextTX::clear_sent(void)
2028 {
2029 tbuf->remove(0, utf8_txpos);
2030 sbuf->remove(0, utf8_txpos);
2031 txpos = 0;
2032 utf8_txpos = 0;
2033 bkspaces = 0;
2034 PauseBreak = false;
2035 set_word_wrap(restore_wrap);
2036 }
2037
2038 /// Returns boolean <eot> end of text
2039 ///
2040 /// true if empty buffer
2041 /// false if characters remain
2042 ///
eot(void)2043 bool FTextTX::eot(void)
2044 {
2045 return (insert_position() == txpos);
2046 }
2047
2048 /// Returns the next character to be transmitted.
2049 ///
2050 /// @return The next character, or ETX if the transmission has been paused, or
2051 /// NUL if no text should be transmitted.
2052 ///
nextChar(void)2053 int FTextTX::nextChar(void)
2054 {
2055 int c;
2056
2057 if (bkspaces) {
2058 --bkspaces;
2059 c = '\b';
2060 }
2061 else if (PauseBreak) {
2062 PauseBreak = false;
2063 c = GET_TX_CHAR_ETX;//0x03;
2064 } else if (insert_position() <= utf8_txpos) { // empty buffer or cursor inside transmitted text
2065 c = -1;
2066 } else {
2067 if ((c = tbuf->char_at(utf8_txpos)) > 0) {
2068 int n = fl_utf8bytes(c);
2069
2070 REQ(FTextTX::changed_cb, utf8_txpos, 0, 0, -1, static_cast<const char *>(0), this);
2071 REQ(FTextTX::changed_cb, utf8_txpos+1, 0, 0, -1, static_cast<const char *>(0), this);
2072 ++txpos;
2073 utf8_txpos += n;
2074 } else
2075 c = -1;
2076 }
2077 return c;
2078 }
2079
2080 // called by xmlrpc thread
2081 // called by macro execution
add_text(string s)2082 void FTextTX::add_text(string s)
2083 {
2084 for (size_t n = 0; n < s.length(); n++) {
2085 if (s[n] == '\b') {
2086 int ipos = insert_position();
2087 if (tbuf->length()) {
2088 if (ipos > 0 && txpos == ipos) {
2089 bkspaces++;
2090 txpos--;
2091 int nn;
2092 tbuf->get_char_at(utf8_txpos, nn);
2093 utf8_txpos -= nn;
2094 }
2095 tbuf->remove(tbuf->length() - 1, tbuf->length());
2096 sbuf->remove(sbuf->length() - 1, sbuf->length());
2097 redraw();
2098 }
2099 } else {
2100 //LOG_DEBUG("%04x ", s[n] & 0x00FF);
2101 add(s[n] & 0xFF, RECV);
2102 }
2103 }
2104 }
2105
setFont(Fl_Font f,int attr)2106 void FTextTX::setFont(Fl_Font f, int attr)
2107 {
2108 FTextBase::setFont(f, attr);
2109 }
2110
2111 /// Handles keyboard shorcuts
2112 ///
2113 /// @param key
2114 // pressed key
2115 ///
2116 /// @return
2117 // 1 if shortcut is handled, otherwise 0.
2118 ///
handle_key_shortcuts(int key)2119 int FTextTX::handle_key_shortcuts(int key)
2120 {
2121 std::string etag = "";
2122
2123 switch (key) {
2124 case 'c': // add <CALL> for SC-c
2125 case 'm': // add <MYCALL> for SC-m
2126 case 'n': // add <NAME> for SC-n
2127 case 'r': // add <RST>rcvd for SC-r
2128 case 's': // add <RST>sent for SC-s
2129 case 'l': // add <MYLOC> for SC-l
2130 case 'h': // add <MYQTH> for SC-h
2131 case 'a': // add <ANTENNA> for SC-a
2132 case 'g': // add <BEL> 0x07
2133 if ((Fl::event_state() & FL_CTRL) && (Fl::event_state() & FL_SHIFT))
2134 // if ((Fl::event_state() & (FL_CTRL | FL_SHIFT))) // investigate why this doesn't work...
2135 {
2136 switch (key)
2137 {
2138 case 'c':
2139 etag = inpCall->value();
2140 break;
2141 case 'm':
2142 etag = progdefaults.myCall;
2143 break;
2144 case 'n':
2145 etag = inpName->value();
2146 break;
2147 case 'r':
2148 {
2149 std::string s;
2150 etag = (s = inpRstIn->value()).length() ? s : std::string("599");
2151 }
2152 break;
2153 case 's':
2154 {
2155 std::string s;
2156 etag = (s = inpRstOut->value()).length() ? s : std::string("599");
2157 }
2158 break;
2159 case 'l':
2160 etag = progdefaults.myLocator;
2161 break;
2162 case 'h':
2163 etag = progdefaults.myQth;
2164 break;
2165 case 'a':
2166 etag = progdefaults.myAntenna;
2167 break;
2168 case 'g':
2169 etag = "\007";
2170 break;
2171 default:
2172 break;
2173 }
2174
2175 // Add text + space if length is > 0
2176 if (etag.length()) {
2177 add_text(etag);
2178 if (etag != "\007")
2179 add_text(" ");
2180 return 1;
2181 }
2182 }
2183 break;
2184
2185 default:
2186 break;
2187 }
2188
2189 return 0;
2190 }
2191
2192 /// Handles keyboard events to override Fl_Text_Editor_mod's handling of some
2193 /// keystrokes.
2194 ///
2195 /// @param key
2196 ///
2197 /// @return
2198 ///
handle_key(int key)2199 int FTextTX::handle_key(int key)
2200 {
2201
2202 if (handle_key_shortcuts(key))
2203 return 1;
2204
2205 switch (key) {
2206 case FL_Escape: // set stop flag and clear
2207 {
2208 static time_t t[2] = { 0, 0 };
2209 static unsigned char i = 0;
2210 if (t[i] == time(&t[!i])) { // two presses in a second: abort transmission
2211 if (trx_state == STATE_TX)
2212 menu_cb(TX_MENU_ABORT);
2213 t[i = !i] = 0;
2214 return 1;
2215 }
2216 i = !i;
2217 }
2218
2219 if (trx_state == STATE_TX && active_modem->get_stopflag() == false) {
2220 kf_select_all(0, this);
2221 kf_copy(0, this);
2222 clear();
2223 if (arq_text_available)
2224 AbortARQ();
2225 active_modem->set_stopflag(true);
2226 }
2227
2228 if (trx_state == STATE_TUNE)
2229 abort_tx();
2230
2231 stopMacroTimer();
2232 return 1;
2233
2234 case 't': // transmit for C-t
2235 if (trx_state == STATE_RX && Fl::event_state() & FL_CTRL) {
2236 menu_cb(TX_MENU_TX);
2237 return 1;
2238 }
2239 break;
2240 case 'r':// receive for C-r
2241 if (Fl::event_state() & FL_CTRL) {
2242 menu_cb(TX_MENU_RX);
2243 return 1;
2244 }
2245 else if (!(Fl::event_state() & (FL_META | FL_ALT)))
2246 break;
2247 // fall through to (un)pause for M-r or A-r
2248
2249 case FL_Pause:
2250 if (trx_state != STATE_TX) {
2251 start_tx();
2252 }
2253 else
2254 PauseBreak = true;
2255 return 1;
2256 case (FL_KP + '+'):
2257 if (active_modem == cw_modem)
2258 active_modem->incWPM();
2259 return 1;
2260 case (FL_KP + '-'):
2261 if (active_modem == cw_modem)
2262 active_modem->decWPM();
2263 return 1;
2264 case (FL_KP + '*'):
2265 if (active_modem == cw_modem)
2266 active_modem->toggleWPM();
2267 return 1;
2268 case FL_Tab:
2269 if (active_modem == fsq_modem) return 1;
2270 // In non-CW modes: Tab and Ctrl-tab both pause until user moves the
2271 // cursor to let some more text through. Another (ctrl-)tab goes back to
2272 // the end of the buffer and resumes sending.
2273
2274 // In CW mode: Tab pauses, skips rest of buffer, applies the
2275 // SKIP style, then resumes sending when new text is entered.
2276 // Ctrl-tab does the same thing as for all other modes.
2277 if (utf8_txpos != insert_position())
2278 insert_position(utf8_txpos);
2279 else
2280 insert_position(tbuf->length());
2281 if (!(Fl::event_state() & FL_CTRL) && active_modem == cw_modem) {
2282 int n = tbuf->length() - utf8_txpos;
2283 char s[n + 1];
2284 memset(s, FTEXT_DEF + SKIP, n);
2285 s[n] = 0;
2286 sbuf->replace(utf8_txpos, sbuf->length(), s);
2287 insert_position(tbuf->length());
2288 redisplay_range(utf8_txpos, insert_position());
2289 utf8_txpos = insert_position();
2290 }
2291 return 1;
2292 // Move cursor, or search up/down with the Meta/Alt modifiers
2293 case FL_Left:
2294 if (Fl::event_state() & (FL_META | FL_ALT)) {
2295 if (active_modem == fsq_modem) return 1;
2296 active_modem->searchDown();
2297 return 1;
2298 }
2299 return 0;
2300 case FL_Right:
2301 if (Fl::event_state() & (FL_META | FL_ALT)) {
2302 if (active_modem == fsq_modem) return 1;
2303 active_modem->searchUp();
2304 return 1;
2305 }
2306 return 0;
2307 // queue a BS and decr. the txpos, unless the cursor is in the tx text
2308 case FL_BackSpace:
2309 {
2310 int ipos = insert_position();
2311 if (utf8_txpos > 0 && utf8_txpos == ipos) {
2312 bkspaces++;
2313 utf8_txpos = tbuf->prev_char(ipos);
2314 txpos--;
2315 }
2316 return 0;
2317 }
2318 // alt - 1 / 2 changes macro sets
2319 case '1':
2320 case '2':
2321 case '3':
2322 case '4':
2323 if (Fl::event_state() & FL_ALT) {
2324 if (active_modem == fsq_modem) return 1;
2325 static char lbl[2] = "1";
2326 altMacros = key - '1';
2327 if (progdefaults.mbar_scheme > MACRO_SINGLE_BAR_MAX) {
2328 if (!altMacros) altMacros = 1;
2329 for (int i = 0; i < NUMMACKEYS; i++) {
2330 btnMacro[NUMMACKEYS + i]->label(
2331 macros.name[(altMacros * NUMMACKEYS) + i].c_str());
2332 btnMacro[NUMMACKEYS + i]->redraw_label();
2333 }
2334 lbl[0] = key;
2335 btnAltMacros2->label(lbl);
2336 btnAltMacros2->redraw_label();
2337 } else {
2338 for (int i = 0; i < NUMMACKEYS; i++) {
2339 btnMacro[i]->label(
2340 macros.name[(altMacros * NUMMACKEYS) + i].c_str());
2341 btnMacro[i]->redraw_label();
2342 }
2343 lbl[0] = key;
2344 btnAltMacros1->label(lbl);
2345 btnAltMacros1->redraw_label();
2346 }
2347 return 1;
2348 }
2349 break;
2350 default:
2351 break;
2352 }
2353
2354 if (insert_position() < txpos)
2355 return 1;
2356
2357 // insert a macro
2358 if (key >= FL_F && key <= FL_F_Last) {
2359 return handle_key_macro(key);
2360 }
2361 // read ctl-ddd, where d is a digit, as ascii characters (in base 10)
2362 // and insert verbatim; e.g. ctl-001 inserts a <soh>
2363 if (Fl::event_state() & FL_CTRL && (key >= FL_KP) && (key <= FL_KP + '9'))
2364 return handle_key_ascii(key);
2365
2366 // restart the numeric keypad entries.
2367 ascii_cnt = 0;
2368 ascii_chr = 0;
2369
2370 return 0;
2371 }
2372
2373 /// Inserts the macro for function key \c key.
2374 ///
2375 /// @param key An integer in the range [FL_F, FL_F_Last]
2376 ///
2377 /// @return 1
2378 ///
handle_key_macro(int key)2379 int FTextTX::handle_key_macro(int key)
2380 {
2381 key -= FL_F + 1;
2382
2383 if (active_modem == fsq_modem) {
2384 if (key == 0) fsq_repeat_last_heard();
2385 if (key == 1) fsq_repeat_last_command();
2386 return 1;
2387 }
2388 if (key > 11)
2389 return 0;
2390
2391 if (progdefaults.mbar_scheme > MACRO_SINGLE_BAR_MAX) {
2392 if (Fl::event_state(FL_SHIFT))
2393 key += altMacros * NUMMACKEYS;
2394 } else {
2395 key += altMacros * NUMMACKEYS;
2396 }
2397 if (!(macros.text[key]).empty())
2398 macros.execute(key);
2399
2400 return 1;
2401 }
2402
handle_dnd_drag(int pos)2403 int FTextTX::handle_dnd_drag(int pos)
2404 {
2405 if (pos >= txpos) {
2406 return FTextEdit::handle_dnd_drag(pos);
2407 }
2408 else // refuse drop inside transmitted text
2409 return 0;
2410 }
2411
2412 /// Handles mouse-3 clicks by displaying the context menu
2413 ///
2414 /// @param val
2415 ///
handle_context_menu(void)2416 void FTextTX::handle_context_menu(void)
2417 {
2418 // adjust Abort/Transmit/Receive menu items
2419 switch (trx_state) {
2420 case STATE_TX:
2421 menu[TX_MENU_TX].hide();
2422 menu[TX_MENU_RX].show();
2423 menu[TX_MENU_ABORT].show();
2424 break;
2425 case STATE_TUNE:
2426 menu[TX_MENU_TX].hide();
2427 menu[TX_MENU_RX].show();
2428 menu[TX_MENU_ABORT].hide();
2429 break;
2430 default:
2431 menu[TX_MENU_TX].show();
2432 menu[TX_MENU_RX].hide();
2433 menu[TX_MENU_ABORT].hide();
2434 break;
2435 }
2436
2437 bool modify_text_ok = insert_position() >= txpos;
2438 bool selected = tbuf->selected();
2439 icons::set_active(&menu[TX_MENU_MFSK16_IMG], active_modem->get_cap() & modem::CAP_IMG);
2440 icons::set_active(&menu[TX_MENU_CLEAR], tbuf->length());
2441 icons::set_active(&menu[TX_MENU_CUT], selected && modify_text_ok);
2442 icons::set_active(&menu[TX_MENU_COPY], selected);
2443 icons::set_active(&menu[TX_MENU_PASTE], modify_text_ok);
2444 icons::set_active(&menu[TX_MENU_READ], modify_text_ok);
2445
2446 if (wrap)
2447 menu[TX_MENU_WRAP].set();
2448 else
2449 menu[TX_MENU_WRAP].clear();
2450
2451 show_context_menu();
2452 }
2453
2454 /// The context menu handler
2455 ///
2456 /// @param val
2457 ///
menu_cb(size_t item)2458 void FTextTX::menu_cb(size_t item)
2459 {
2460 switch (item) {
2461 case TX_MENU_TX:
2462 active_modem->set_stopflag(false);
2463 start_tx();
2464 break;
2465 case TX_MENU_ABORT:
2466 char panic[200];
2467 snprintf(panic, sizeof(panic), "*** Don't panic *** %s", progdefaults.myName.c_str());
2468 put_status(panic, 5.0);
2469 abort_tx();
2470 break;
2471 case TX_MENU_RX:
2472 if (trx_state == STATE_TX) {
2473 insert_position(tbuf->length());
2474 add("^r", CTRL);
2475 }
2476 else
2477 abort_tx();
2478 break;
2479 case TX_MENU_MFSK16_IMG:
2480 {
2481 trx_mode md = active_modem->get_mode();
2482 if (md == MODE_IFKP)
2483 ifkp_showTxViewer();
2484 else if (md >= MODE_THOR_FIRST && md <= MODE_THOR_LAST)
2485 thor_showTxViewer();
2486 else
2487 showTxViewer(0, 0);
2488 break;
2489 }
2490 case TX_MENU_CLEAR:
2491 clear();
2492 break;
2493 case TX_MENU_CUT:
2494 kf_cut(0, this);
2495 break;
2496 case TX_MENU_COPY:
2497 kf_copy(0, this);
2498 break;
2499 case TX_MENU_PASTE:
2500 kf_paste(0, this);
2501 break;
2502 case TX_MENU_READ: {
2503 restore_wrap = wrap;
2504 set_word_wrap(false);
2505 readFile();
2506 break;
2507 }
2508 case TX_MENU_WRAP:
2509 set_word_wrap(!wrap, true);
2510 break;
2511 default:
2512 if (FTextTX::menu[item].flags == 0) { // not an FL_SUB_MENU
2513 add(FTextTX::menu[item].text[0]);
2514 add(FTextTX::menu[item].text[1]);
2515 }
2516 }
2517 }
2518
2519 /// Overrides some useful Fl_Text_Edit keybindings that we want to keep working,
2520 /// provided that they don't try to change chunks of transmitted text.
2521 ///
change_keybindings(void)2522 void FTextTX::change_keybindings(void)
2523 {
2524 struct {
2525 Fl_Text_Editor_mod::Key_Func function, override;
2526 } fbind[] = {
2527 { Fl_Text_Editor_mod::kf_default, FTextTX::kf_default },
2528 { Fl_Text_Editor_mod::kf_enter, FTextTX::kf_enter },
2529 { Fl_Text_Editor_mod::kf_delete, FTextTX::kf_delete },
2530 { Fl_Text_Editor_mod::kf_cut, FTextTX::kf_cut },
2531 { Fl_Text_Editor_mod::kf_paste, FTextTX::kf_paste }
2532 };
2533 int n = sizeof(fbind) / sizeof(fbind[0]);
2534
2535 // walk the keybindings linked list and replace items containing
2536 // functions for which we have an override in fbind
2537 for (Fl_Text_Editor_mod::Key_Binding *k = key_bindings; k; k = k->next) {
2538 for (int i = 0; i < n; i++)
2539 if (fbind[i].function == k->function)
2540 k->function = fbind[i].override;
2541 }
2542 }
2543
2544 // The kf_* functions below call the corresponding Fl_Text_Editor_mod routines, but
2545 // may make adjustments so that no transmitted text is modified.
2546
kf_default(int c,Fl_Text_Editor_mod * e)2547 int FTextTX::kf_default(int c, Fl_Text_Editor_mod* e)
2548 {
2549 return e->insert_position() < *ptxpos ? 1 : Fl_Text_Editor_mod::kf_default(c, e);
2550 }
2551
kf_enter(int c,Fl_Text_Editor_mod * e)2552 int FTextTX::kf_enter(int c, Fl_Text_Editor_mod* e)
2553 {
2554 return e->insert_position() < *ptxpos ? 1 : Fl_Text_Editor_mod::kf_enter(c, e);
2555 }
2556
kf_delete(int c,Fl_Text_Editor_mod * e)2557 int FTextTX::kf_delete(int c, Fl_Text_Editor_mod* e)
2558 {
2559 // single character
2560 if (!e->buffer()->selected()) {
2561 if (e->insert_position() >= *ptxpos &&
2562 e->insert_position() != e->buffer()->length())
2563 return Fl_Text_Editor_mod::kf_delete(c, e);
2564 else
2565 return 1;
2566 }
2567
2568 // region: delete as much as we can
2569 int start, end;
2570 e->buffer()->selection_position(&start, &end);
2571 if (*ptxpos >= end)
2572 return 1;
2573 if (*ptxpos > start)
2574 e->buffer()->select(*ptxpos, end);
2575
2576 return Fl_Text_Editor_mod::kf_delete(c, e);
2577 }
2578
kf_cut(int c,Fl_Text_Editor_mod * e)2579 int FTextTX::kf_cut(int c, Fl_Text_Editor_mod* e)
2580 {
2581 if (e->buffer()->selected()) {
2582 int start, end;
2583 e->buffer()->selection_position(&start, &end);
2584 if (*ptxpos >= end)
2585 return 1;
2586 if (*ptxpos > start)
2587 e->buffer()->select(*ptxpos, end);
2588 }
2589
2590 return Fl_Text_Editor_mod::kf_cut(c, e);
2591 }
2592
kf_paste(int c,Fl_Text_Editor_mod * e)2593 int FTextTX::kf_paste(int c, Fl_Text_Editor_mod* e)
2594 {
2595 return e->insert_position() < *ptxpos ? 1 : Fl_Text_Editor_mod::kf_paste(c, e);
2596 }
2597
2598 // ----------------------------------------------------------------------------
2599
2600
draw(void)2601 void MVScrollbar::draw(void)
2602 {
2603 Fl_Scrollbar::draw();
2604
2605 if (marks.empty() || !draw_marks)
2606 return;
2607
2608 assert((type() & FL_HOR_SLIDER) == 0);
2609
2610 // Calculate the slider knob position and height. For a vertical scrollbar,
2611 // the scroll buttons' height is the scrollbar width and the minimum knob
2612 // height is half that.
2613 int H = h() - Fl::box_dh(box()) - 2 * w(); // internal height (minus buttons)
2614 int slider_h = (int)(slider_size() * H + 0.5);
2615 int min_h = (w() - Fl::box_dw(box())) / 2 + 1;
2616 if (slider_h < min_h)
2617 slider_h = min_h;
2618 double val = (Fl_Slider::value() - minimum()) / (maximum() - minimum());
2619 int slider_y = (int)(val * (H - slider_h) + 0.5) + w(); // relative to y()
2620 // This would draw a green rectangle around the slider knob:
2621 // fl_color(FL_GREEN);
2622 // fl_rect(x(), y() + slider_y, w() - Fl::box_dw(box()), slider_h);
2623
2624 int x1 = x() + Fl::box_dx(box()), x2 = x1 + w() - Fl::box_dw(box()) - 1, ypos;
2625 // Convert stored scrollbar values to vertical positions and draw
2626 // lines inside the widget if they don't overlap with the knob area.
2627 for (vector<mark_t>::const_iterator i = marks.begin(); i != marks.end(); ++i) {
2628 ypos = static_cast<int>(w() + H * i->pos / maximum());
2629 // Don't draw over slider knob
2630 if ((ypos > slider_y && ypos < slider_y + slider_h) ||
2631 (ypos < slider_y + slider_h && ypos > slider_y))
2632 continue;
2633 ypos += y();
2634 fl_color(i->color);
2635 fl_line(x1, ypos, x2, ypos);
2636 }
2637 }
2638