kopia lustrzana https://github.com/jamescoxon/dl-fldigi
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
rodzic
a0af30f208
commit
5eda8b6077
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue