Add RX text and waterfall links

A double click on the waterfall inserts a text anchor than can be
clicked to return to that signal. Similarly, an Alt-click on the
waterfall scrolls the RX text to the most recent text anchor that
describes a similar modem and frequency combination.

If the "insert text on click" waterfall option is switched on,
text anchors will be inserted only if the click text box contains
the string "<FREQ>".

This implementation is based on an idea by Leigh, WA5ZNU.
pull/2/head
Stelios Bounanos 2008-10-29 01:20:26 +00:00
rodzic a0af30f208
commit 5eda8b6077
9 zmienionych plików z 231 dodań i 25 usunięć

Wyświetl plik

@ -315,6 +315,17 @@ static void cb_inpWaterfallClickText(Fl_Input* o, void*) {
progdefaults.changed = true;
}
Fl_Check_Button *btnWaterfallClickInsert=(Fl_Check_Button *)0;
static void cb_btnWaterfallClickInsert(Fl_Check_Button* o, void*) {
progdefaults.WaterfallClickInsert = o->value();
if (progdefaults.WaterfallClickInsert)
inpWaterfallClickText->show();
else
inpWaterfallClickText->hide();
progdefaults.changed = true;
}
Fl_Choice *mnuWaterfallWheelAction=(Fl_Choice *)0;
static void cb_mnuWaterfallWheelAction(Fl_Choice* o, void*) {
@ -1974,10 +1985,15 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
btnWaterfallQSY->down_box(FL_DOWN_BOX);
btnWaterfallQSY->callback((Fl_Callback*)cb_btnWaterfallQSY);
} // Fl_Check_Button* btnWaterfallQSY
{ inpWaterfallClickText = new Fl_Input(15, 126, 150, 40, "Insert text\non left click");
{ inpWaterfallClickText = new Fl_Input(206, 120, 150, 50);
inpWaterfallClickText->callback((Fl_Callback*)cb_inpWaterfallClickText);
inpWaterfallClickText->align(FL_ALIGN_RIGHT);
} // Fl_Input* inpWaterfallClickText
{ btnWaterfallClickInsert = new Fl_Check_Button(15, 134, 172, 20, "Insert text on left click");
btnWaterfallClickInsert->down_box(FL_DOWN_BOX);
btnWaterfallClickInsert->callback((Fl_Callback*)cb_btnWaterfallClickInsert);
btnWaterfallClickInsert->value(progdefaults.WaterfallClickInsert);
} // Fl_Check_Button* btnWaterfallClickInsert
o->end();
} // Fl_Group* o
{ mnuWaterfallWheelAction = new Fl_Choice(15, 176, 150, 22, "Wheel action");

Wyświetl plik

@ -386,11 +386,20 @@ progdefaults.changed = true;}
xywh {15 96 225 20} down_box DOWN_BOX
}
Fl_Input inpWaterfallClickText {
label {Insert text
on left click}
callback {progdefaults.WaterfallClickText = o->value();
progdefaults.changed = true;}
xywh {15 126 150 40} align 8
xywh {206 120 150 50} align 8
}
Fl_Check_Button btnWaterfallClickInsert {
label {Insert text on left click}
callback {progdefaults.WaterfallClickInsert = o->value();
if (progdefaults.WaterfallClickInsert)
inpWaterfallClickText->show();
else
inpWaterfallClickText->hide();
progdefaults.changed = true;}
xywh {15 134 172 20} down_box DOWN_BOX
code0 {btnWaterfallClickInsert->value(progdefaults.WaterfallClickInsert);}
}
}
Fl_Choice mnuWaterfallWheelAction {

Wyświetl plik

@ -2840,6 +2840,10 @@ void qsy(long long rfc, long long fmid)
{
if (fmid < 0LL)
fmid = (long long)active_modem->get_freq();
if (rfc == 0LL || rfc == wf->rfcarrier()) {
active_modem->set_freq(fmid);
return;
}
if (progdefaults.chkUSERIGCATis)
REQ(rigCAT_set_qsy, rfc, fmid);
@ -2853,4 +2857,6 @@ void qsy(long long rfc, long long fmid)
else if (progdefaults.chkUSEXMLRPCis)
REQ(xmlrpc_set_qsy, rfc, fmid);
#endif
else
active_modem->set_freq(fmid);
}

Wyświetl plik

@ -39,7 +39,11 @@
class FTextBase : public Fl_Text_Editor_mod
{
public:
enum TEXT_ATTR { RECV, XMIT, CTRL, SKIP, ALTR, NATTR };
// CLICK_START: same as first clickable style
// NATTR: number of styles (last style + 1)
enum TEXT_ATTR { RECV, XMIT, CTRL, SKIP, ALTR,
CLICK_START, QSY = CLICK_START, /* FOO, BAR, ..., */
NATTR };
FTextBase(int x, int y, int w, int h, const char *l = 0);
virtual ~FTextBase() { delete tbuf; delete sbuf; }
@ -107,6 +111,7 @@ public:
while (*s)
add(*s++, attr);
}
void clear(void);
protected:
enum { RX_MENU_QRZ_THIS, RX_MENU_CALL, RX_MENU_NAME, RX_MENU_QTH,
@ -117,6 +122,8 @@ protected:
#endif
RX_MENU_SAVE, RX_MENU_WRAP };
void handle_clickable(int x, int y);
void handle_qsy(int start, int end);
virtual void menu_cb(int val);
static void changed_cb(int pos, int nins, int ndel, int nsty,
const char *dtext, void *arg);

Wyświetl plik

@ -49,6 +49,7 @@ extern Fl_Check_Button *chkShowAudioScale;
extern Fl_Check_Button *btnWaterfallHistoryDefault;
extern Fl_Check_Button *btnWaterfallQSY;
extern Fl_Input *inpWaterfallClickText;
extern Fl_Check_Button *btnWaterfallClickInsert;
#include <FL/Fl_Choice.H>
extern Fl_Choice *mnuWaterfallWheelAction;
extern Fl_Group *tabVideo;

Wyświetl plik

@ -47,7 +47,8 @@
ELEM_(bool, StartAtSweetSpot, "STARTATSWEETSPOT", false) \
ELEM_(bool, WaterfallHistoryDefault, "WATERFALLHISTORYDEFAULT", false) \
ELEM_(bool, WaterfallQSY, "WATERFALLQSY", false) \
ELEM_(std::string, WaterfallClickText, "WATERFALLCLICKTEXT", "") \
ELEM_(bool, WaterfallClickInsert, "WATERFALLCLICKINSERT", false) \
ELEM_(std::string, WaterfallClickText, "WATERFALLCLICKTEXT", "\n<FREQ>\n") \
ELEM_(int, WaterfallWheelAction, "WATERFALLWHEELACTION", waterfall::WF_CARRIER) \
/* PSK mail interface */ \
ELEM_(bool, PSKmailSweetSpot, "PSKMAILSWEETSPOT", false) \

Wyświetl plik

@ -425,8 +425,11 @@ int configuration::setDefaults() {
valPSKsweetspot->value(PSKsweetspot);
btnWaterfallHistoryDefault->value(WaterfallHistoryDefault);
btnWaterfallQSY->value(WaterfallQSY);
inpWaterfallClickText->input_type(FL_MULTILINE_INPUT);
inpWaterfallClickText->value(WaterfallClickText.c_str());
if (!WaterfallClickInsert)
inpWaterfallClickText->hide();
mnuWaterfallWheelAction->add(waterfall::wf_wheel_action);
mnuWaterfallWheelAction->value(WaterfallWheelAction);

Wyświetl plik

@ -31,6 +31,8 @@
#include <sstream>
#include <vector>
#include <algorithm>
#include <map>
#include "waterfall.h"
#include "threads.h"
#include "main.h"
@ -1441,6 +1443,77 @@ static void hide_cursor(void *w)
reinterpret_cast<Fl_Widget *>(w)->window()->cursor(cursor = FL_CURSOR_NONE);
}
map<string, qrg_mode_t> qsy_map;
static void note_qsy(char c1 = ' ', char c2 = ' ')
{
char buf[64];
time_t t = time(NULL);
struct tm tm;
gmtime_r(&t, &tm);
size_t r1;
if ((r1 = strftime(buf, sizeof(buf), "<<%FT%H:%MZ: ", &tm)) == 0)
return;
qrg_mode_t m;
m.rfcarrier = wf->rfcarrier();
m.carrier = active_modem->get_freq();
m.mode = active_modem->get_mode();
cerr << m << endl;
size_t r2;
if (m.rfcarrier)
r2 = snprintf(buf+r1, sizeof(buf)-r1, "%s @ %lld%c%04d>>",
mode_info[active_modem->get_mode()].name, wf->rfcarrier(),
(wf->USB() ? '+' : '-'), active_modem->get_freq());
else
r2 = snprintf(buf+r1, sizeof(buf)-r1, "%s @ %04d>>",
mode_info[active_modem->get_mode()].name, active_modem->get_freq());
if (r2 >= sizeof(buf)-r1)
return;
qsy_map[buf] = m;
ReceiveText->add(c1);
ReceiveText->add(buf, FTextBase::QSY);
ReceiveText->add(c2);
}
static void insert_text(void)
{
string::size_type i;
if ((i = progdefaults.WaterfallClickText.find("<FREQ>")) != string::npos) {
string s = progdefaults.WaterfallClickText;
s[i] = '\0';
ReceiveText->add(s.c_str());
note_qsy();
ReceiveText->add(s.c_str() + i + strlen("<FREQ>"));
}
else
ReceiveText->add(progdefaults.WaterfallClickText.c_str(), FTextView::SKIP);
}
static void find_signal_text(void)
{
int freq = active_modem->get_freq();
trx_mode mode = active_modem->get_mode();
map<string, qrg_mode_t>::const_iterator i;
for (i = qsy_map.begin(); i != qsy_map.end(); ++i)
if (i->second.mode == mode && abs(i->second.carrier - freq) <= 20)
break;
if (i != qsy_map.end()) {
// Search backward from the current text cursor position, then
// try the other direction
int pos = ReceiveText->insert_position();
if (ReceiveText->buffer()->search_backward(pos, i->first.c_str(), &pos, 1) ||
ReceiveText->buffer()->search_forward(pos, i->first.c_str(), &pos, 1)) {
ReceiveText->insert_position(pos);
ReceiveText->show_insert_position();
}
}
}
int WFdisp::handle(int event)
{
static int pxpos, push;
@ -1492,18 +1565,8 @@ int WFdisp::handle(int event)
if (event == FL_PUSH) {
push = ypos;
pxpos = xpos;
if (!progdefaults.WaterfallClickText.empty()) {
if (progdefaults.WaterfallClickText.find('%') != string::npos) {
time_t t = time(NULL);
struct tm tm;
localtime_r(&t, &tm);
char buf[256];
strftime(buf, sizeof(buf), progdefaults.WaterfallClickText.c_str(), &tm);
ReceiveText->add(buf, FTextBase::CTRL);
}
else
ReceiveText->add(progdefaults.WaterfallClickText.c_str(), FTextBase::CTRL);
}
if (Fl::event_clicks())
return 1;
}
if (progdefaults.WaterfallQSY && push < WFTEXT + WFSCALE) {
long long newrfc = (pxpos - xpos) * (step==4?10:step==2?5:1);
@ -1547,7 +1610,7 @@ int WFdisp::handle(int event)
}
break;
case FL_RELEASE:
switch (Fl::event_button()) {
switch (eb = Fl::event_button()) {
case FL_RIGHT_MOUSE:
tmp_carrier = false;
active_modem->set_freq(oldcarrier);
@ -1557,6 +1620,19 @@ int WFdisp::handle(int event)
case FL_LEFT_MOUSE:
push = 0;
oldcarrier = newcarrier;
if (eb != FL_LEFT_MOUSE)
break;
if (Fl::event_state() == 0) {
if (Fl::event_clicks()) {
if (!progdefaults.WaterfallClickInsert)
note_qsy('\n', '\n');
}
else
if (progdefaults.WaterfallClickInsert)
insert_text();
}
else if (Fl::event_state() & (FL_META | FL_ALT))
find_signal_text();
break;
}
break;

Wyświetl plik

@ -25,6 +25,7 @@
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <map>
#include "FTextView.h"
#include "main.h"
@ -42,6 +43,8 @@
#include "mfsk.h"
#include "icons.h"
#include "globals.h"
#include "re.h"
using namespace std;
@ -187,6 +190,9 @@ void FTextBase::set_style(int attr, Fl_Font f, int s, Fl_Color c, int set)
else
styles[i].color = c;
}
if (i == SKIP) // clickable styles always same as SKIP for now
for (int j = CLICK_START; j < NATTR; j++)
memcpy(&styles[j], &styles[i], sizeof(styles[j]));
}
resize(x(), y(), w(), h()); // to redraw and recalculate the wrap column
@ -287,13 +293,17 @@ void FTextBase::saveFile(void)
///
char *FTextBase::get_word(int x, int y)
{
if (!tbuf->selected()) {
int p = xy_to_position(x + this->x(), y + this->y(),
Fl_Text_Display_mod::CURSOR_POS);
int p = xy_to_position(x + this->x(), y + this->y(),
Fl_Text_Display_mod::CURSOR_POS);
char* s;
if (!tbuf->selected())
s = tbuf->text_range(word_start(p), word_end(p));
else {
tbuf->select(word_start(p), word_end(p));
s = tbuf->selection_text();
tbuf->unselect();
}
char *s = tbuf->selection_text();
tbuf->unselect();
return s;
}
@ -393,6 +403,7 @@ FTextView::FTextView(int x, int y, int w, int h, const char *l)
{
tbuf->remove_modify_callback(buffer_modified_cb, this);
tbuf->add_modify_callback(changed_cb, this);
tbuf->canUndo(0);
cursor_style(Fl_Text_Display_mod::NORMAL_CURSOR);
@ -424,6 +435,8 @@ FTextView::~FTextView()
///
int FTextView::handle(int event)
{
static Fl_Cursor cursor;
switch (event) {
case FL_PUSH:
if (!Fl::event_inside(this))
@ -459,7 +472,29 @@ int FTextView::handle(int event)
show_context_menu();
return 1;
break;
// catch some text-modifying events that are not handled by kf_* functions
case FL_DRAG:
if (Fl::event_button() != FL_LEFT_MOUSE)
return 1;
break;
case FL_RELEASE:
if (cursor == FL_CURSOR_HAND && Fl::event_button() == FL_LEFT_MOUSE &&
Fl::event_is_click() && !Fl::event_clicks()) {
handle_clickable(Fl::event_x() - x(), Fl::event_y() - y());
return 1;
}
break;
case FL_MOVE: {
int p = xy_to_position(Fl::event_x(), Fl::event_y(), Fl_Text_Display_mod::CURSOR_POS);
if (sbuf->character(p) >= CLICK_START + FTEXT_DEF) {
if (cursor != FL_CURSOR_HAND)
window()->cursor(cursor = FL_CURSOR_HAND);
return 1;
}
else
cursor = FL_CURSOR_INSERT;
break;
}
// catch some text-modifying events that are not handled by kf_* functions
case FL_KEYBOARD:
int k;
if (Fl::compose(k))
@ -527,6 +562,58 @@ void FTextView::add(unsigned char c, int attr)
FL_AWAKE_D();
}
void FTextView::clear(void)
{
FTextBase::clear();
extern map<string, qrg_mode_t> qsy_map;
qsy_map.clear();
}
void FTextView::handle_clickable(int x, int y)
{
int pos, style;
pos = xy_to_position(x + this->x(), y + this->y(), Fl_Text_Display_mod::CURSOR_POS);
// return unless clickable style
if ((style = sbuf->character(pos)) < CLICK_START + FTEXT_DEF)
return;
int start, end;
for (start = pos-1; start >= 0; start--)
if (sbuf->character(start) != style)
break;
start++;
int len = sbuf->length();
for (end = pos+1; end < len; end++)
if (sbuf->character(end) != style)
break;
switch (style - FTEXT_DEF) {
case QSY:
handle_qsy(start, end);
break;
// ...
default:
break;
}
}
void FTextView::handle_qsy(int start, int end)
{
char* text = tbuf->text_range(start, end);
extern map<string, qrg_mode_t> qsy_map;
map<string, qrg_mode_t>::const_iterator i;
if ((i = qsy_map.find(text)) != qsy_map.end()) {
const qrg_mode_t& m = i->second;
if (active_modem->get_mode() != m.mode)
init_modem_sync(m.mode);
qsy(m.rfcarrier, m.carrier);
}
free(text);
}
/// The context menu handler
///
/// @param val