From f2e8c9a210608900ed55ea5ff34b74c40523e545 Mon Sep 17 00:00:00 2001 From: Stelios Bounanos Date: Sat, 21 Jul 2007 13:15:41 +0100 Subject: [PATCH] Upstream version 1.36b --- ChangeLog | 9 +- src/config.h | 4 + src/dialogs/TextView.cxx | 856 +++++++++++++++++++------------- src/dialogs/TextView_resize.cxx | 196 ++++++-- src/dialogs/fl_digi.cxx | 144 +++--- src/globals/globals.cxx | 2 + src/ider/id.cxx | 1 + src/include/TextView.h | 203 ++++---- src/include/fl_digi.h | 2 + src/include/globals.h | 2 + src/include/id.h | 2 +- src/include/main.h | 21 +- src/include/modem.h | 2 + src/include/version.h | 2 +- src/main.cxx | 64 ++- src/misc/pskmail.cxx | 53 +- src/psk/psk.cxx | 21 +- src/trx/modem.cxx | 2 + src/waterfall/waterfall.cxx | 14 + 19 files changed, 1019 insertions(+), 581 deletions(-) diff --git a/ChangeLog b/ChangeLog index a9d10a8f..eca284bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ Change Log: -1.35 1) Increased TxPPM to +/- 50000 to match the RxPPM range +1.36 1) Increased TxPPM to +/- 50000 to match the RxPPM range 2) Changed all audio i/o to separate 2 channel control (stereo) 3) Added right channel CW QRQ signal 4) Added right channel pseudo FSK signal @@ -13,12 +13,17 @@ Change Log: 10) Added "Test" capability to save Rx or Tx waveforms in ".wav" format and to play them back for testing modem decoders. 11) Replaced text editing widgets with new widgets capable of cut, - copy and paste. + copy and paste. + ===> temporarily disabled pending resolution of cpu% problem 12) Added new functions on text widget popup menus 13) Added a QSY undo (right button press) 14) Added a who is this capability to waterfall -- press right button and hold on a signal and decoding occurs on that signal. Release and decoding returns to normal tracking point. + 15) This release requires libsndfile, libportaudio and libportaudiocpp + The new libraries provide portaudio sound card mapping, and the + ability to save and playback wav files (when fldigi is run as: + fldigi TEST). 1.34 1) Cleaned up unused code segments 2) Modified CW decoder to allow setting upper and lower limits on Tx WPM diff --git a/src/config.h b/src/config.h index 7f6cd0e0..f88a835b 100644 --- a/src/config.h +++ b/src/config.h @@ -90,4 +90,8 @@ #define Wstatus (WNOM - Wmode - Ws2n - Wimd - Wwarn - bwAfcOnOff - bwSqlOnOff) +//remove the comment delimiter to enable experimental psk250 and qpsk250 modes + +#define USE250 + #endif diff --git a/src/dialogs/TextView.cxx b/src/dialogs/TextView.cxx index 5dcd82c1..62326bbf 100644 --- a/src/dialogs/TextView.cxx +++ b/src/dialogs/TextView.cxx @@ -5,6 +5,9 @@ // Copyright (C) 2006 // Dave Freese, W1HKJ // +// Copyright (C) 2007 +// Stelios Bounanos, 2E0DLX +// // This file is part of fldigi. // // fldigi is free software; you can redistribute it and/or modify @@ -22,8 +25,8 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // ---------------------------------------------------------------------------- -#include #include +#include #include "TextView.h" #include "main.h" @@ -33,194 +36,364 @@ #include "cw.h" -#include #include "File_Selector.h" + +#if (FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1 && \ + (FL_PATCH_VERSION == 7 || FL_PATCH_VERSION == 8)) && \ + !defined(NO_HSCROLLBAR_KLUDGE) +# define HSCROLLBAR_KLUDGE +#else +# ifndef NO_HSCROLLBAR_KLUDGE +# warning "Not suppressing horizontal scrollbars with this version of fltk" +# endif +# undef HSCROLLBAR_KLUDGE +#endif + using namespace std; -Fl_Menu_Item TextView::viewmenu[] = { - {"@-9$returnarrow &QRZ this call", 0, 0 }, - {"@-9-> &Call", 0, 0 }, - {"@-9-> &Name", 0, 0 }, - {"@-9-> QT&H", 0, 0 }, - {"@-9-> &Locator", 0, 0 }, - {"@-9-> &RSTin", 0, 0, 0, FL_MENU_DIVIDER }, - {"Insert divider", 0, 0 }, - {"C&lear", 0, 0 }, - {"&Copy", 0, 0, 0, FL_MENU_DIVIDER }, - {"Save to &file...", 0, 0, 0, FL_MENU_DIVIDER }, - {"Word &wrap", 0, 0, 0, FL_MENU_TOGGLE }, - { 0 } -}; - -Fl_Text_Display::Style_Table_Entry TextView::styles[NSTYLES]; - -TextView::TextView(int x, int y, int w, int h, const char *l) - : Fl_Text_Display(x, y, w, h, l), wrap(true) +/// TextBase constructor. +/// Word wrapping is enabled by default at column 80, but see \c reset_wrap_col. +/// @param x +/// @param y +/// @param w +/// @param h +/// @param l +TextBase::TextBase(int x, int y, int w, int h, const char *l) + : Fl_Text_Editor(x, y, w, h, l), wrap(true), wrap_col(80), max_lines(0) { tbuf = new Fl_Text_Buffer; sbuf = new Fl_Text_Buffer; + cursor_style(Fl_Text_Editor::NORMAL_CURSOR); buffer(tbuf); - highlight_data(sbuf, styles, NSTYLES, 'A', 0, 0); - tbuf->add_modify_callback(changed_cb, this); - cursor_style(Fl_Text_Display::BLOCK_CURSOR); + highlight_data(sbuf, styles, NSTYLES, DEFAULT, 0, 0); - wrap_mode(wrap, 0); - scrollbar_width((int)floor(scrollbar_width() * 3.0/4.0)); + wrap_mode(wrap, wrap_col); +// change by W1HKJ +// scrollbar_width((int)floor(scrollbar_width() * 3.0/4.0)); + scrollbar_width(16); // set some defaults - setFont(FL_COURIER); - setFontSize(12); - setFontColor(FL_BLACK); - setFontColor(2, FL_BLUE); - setFontColor(3, FL_GREEN); - setFontColor(4, FL_RED); -} -TextView::~TextView() -{ - delete tbuf; - delete sbuf; + set_style(NSTYLES, FL_COURIER, 12, FL_FOREGROUND_COLOR); + set_style(XMT, FL_COURIER, 12, FL_RED, SET_COLOR); + set_style(SKIP, FL_COURIER, 12, FL_BLUE, SET_COLOR); + set_style(CTRL, FL_COURIER, 12, FL_DARK_GREEN, SET_COLOR); } + +void TextBase::setFont(Fl_Font f, text_attr_t attr) +{ + set_style(attr, f, textsize(), textcolor(), SET_FONT); +} + +void TextBase::setFontSize(int s, text_attr_t attr) +{ + set_style(attr, textfont(), s, textcolor(), SET_SIZE); +} + +void TextBase::setFontColor(Fl_Color c, text_attr_t attr) +{ + set_style(attr, textfont(), textsize(), c, SET_COLOR); +} + +/// Resizes the text widget. +/// The real work is done by \c Fl_Text_Editor::resize or, if \c HSCROLLBAR_KLUDGE +/// is defined, a version of that code modified so that no horizontal +/// scrollbars are displayed when word wrapping. +/// +/// @param X +/// @param Y +/// @param W +/// @param H +/// +void TextBase::resize(int X, int Y, int W, int H) +{ + reset_wrap_col(); + +#ifdef HSCROLLBAR_KLUDGE +# include "TextView_resize.cxx" +#else + Fl_Text_Editor::resize(int X, int Y, int W, int H); +#endif // HSCROLLBAR_KLUDGE +} + +/// Changes text style attributes +/// +/// @param attr The attribute name to change, or \c NSTYLES to change all styles. +/// @param f The new font +/// @param s The new font size +/// @param c The new font color +/// @param set One or more (OR'd together) SET operations; @see set_style_op_e +/// +void TextBase::set_style(text_attr_t attr, Fl_Font f, int s, Fl_Color c, int set) +{ + int start, end; + if (attr == NSTYLES) { // update all styles + start = 0; + end = NSTYLES; + if (set & SET_FONT) + textfont(f); + if (set & SET_SIZE) + textsize(s); + if (set & SET_COLOR) + textcolor(c); + } + else { + start = attr - DEFAULT; + end = start + 1; + } + for (int i = start; i < end; i++) { + if (set & SET_FONT) + styles[i].font = f; + if (set & SET_SIZE) + styles[i].size = s; + if (set & SET_COLOR) + styles[i].color = c; + } + resize(x(), y(), w(), h()); // to redraw and recalculate the wrap column +} + +/// Reads a file and appends its contents to the buffer. +/// +/// +void TextBase::readFile(void) +{ + char *fn = File_Select("Select ASCII text file", "*.txt", "", 0); + if (fn) { + tbuf->appendfile(fn); + insert_position(tbuf->length()); + show_insert_position(); + } +} + +/// Writes all buffer text out to a file. +/// +/// +void TextBase::saveFile(void) +{ + char *fn = File_Select("Select ASCII text file", "*.txt", "", 0); + if (fn) + tbuf->outputfile(fn, 0, tbuf->length()); +} + +/// Returns a character string containing the word at (\a x, \a y) relative to +/// the widget's \c x() and \c y(). +/// +/// @param x +/// @param y +/// +/// @return The word text at (x,y). Must be freed by the caller. +/// +char *TextBase::get_word(int x, int y) +{ + int p = xy_to_position(x + this->x(), y + this->y(), + Fl_Text_Display::CURSOR_POS); + tbuf->select(word_start(p), word_end(p)); + char *s = tbuf->selection_text(); + tbuf->unselect(); + + return s; +} + +/// Displays the menu pointed to by \c context_menu and calls the menu function; +/// @see call_cb. +/// +void TextBase::show_context_menu(void) +{ + const Fl_Menu_Item *m; + int xpos = Fl::event_x(); + int ypos = Fl::event_y(); + + popx = xpos - x(); + popy = ypos - y(); + m = context_menu->popup(xpos, ypos, 0, 0, 0); + if (!m) + return; + for (int i = 0; i < context_menu->size(); ++i) { + if (m->text == context_menu[i].text) { + menu_cb(i); + break; + } + } +} + +/// Recalculates the wrap margin when the font is changed or the widget resized. +/// At the moment we only handle constant width fonts. Using proportional fonts +/// will result in a small amount of unused space at the end of each line. +/// +int TextBase::reset_wrap_col(void) +{ + if (!wrap || wrap_col == 0 || text_area.w == 0) + return wrap_col; + + int old_wrap_col = wrap_col; + + fl_font(textfont(), textsize()); + wrap_col = (int)floor(text_area.w / fl_width('X')); + // wrap_mode triggers a resize; don't call it if wrap_col hasn't changed + if (old_wrap_col != wrap_col) + wrap_mode(wrap, wrap_col); + + return old_wrap_col; +} + + +// ---------------------------------------------------------------------------- + + +Fl_Menu_Item TextView::view_menu[] = { + { "@-9$returnarrow &QRZ this call", 0, 0 }, + { "@-9-> &Call", 0, 0 }, + { "@-9-> &Name", 0, 0 }, + { "@-9-> QT&H", 0, 0 }, + { "@-9-> &Locator", 0, 0 }, + { "@-9-> &RSTin", 0, 0, 0, FL_MENU_DIVIDER }, + { "Insert divider", 0, 0 }, + { "C&lear", 0, 0 }, + { "&Copy", 0, 0, 0, FL_MENU_DIVIDER }, + { "Save to &file...", 0, 0, 0, FL_MENU_DIVIDER }, + { "Word &wrap", 0, 0, 0, FL_MENU_TOGGLE }, + { 0 } +}; + +/// TextView constructor. +/// We remove \c Fl_Text_Display::buffer_modified_cb from the list of callbacks +/// because we want to scroll depending on the visibility of the last line; @see +/// changed_cb. +/// @param x +/// @param y +/// @param w +/// @param h +/// @param l +TextView::TextView(int x, int y, int w, int h, const char *l) + : TextBase(x, y, w, h, l) +{ + tbuf->remove_modify_callback(Fl_Text_Display::buffer_modified_cb, this); + tbuf->add_modify_callback(changed_cb, this); + + cursor_style(Fl_Text_Display::BLOCK_CURSOR); + + context_menu = view_menu; + // disable some keybindings that are not allowed in TextView buffers + change_keybindings(); +} + +/// Handles fltk events for this widget. + +/// We only care about mouse presses (to display the popup menu and prevent +/// pasting) and keyboard events (to make sure no text can be inserted). +/// Everything else is passed to the base class handle(). +/// +/// @param event +/// +/// @return +/// int TextView::handle(int event) { switch (event) { - case FL_FOCUS: - show_cursor(1); - if (tbuf->selected()) - redraw(); - Fl::focus(this); - return 1; - break; - case FL_UNFOCUS: - show_cursor(1); - if (tbuf->selected()) - redraw(); - return 1; - case FL_ENTER: - show_cursor(1); - return 1; - break; case FL_PUSH: - if ( !(Fl::event_inside(this) && Fl::event_button() == FL_RIGHT_MOUSE) ) + if (!Fl::event_inside(this)) + break; + // stop mouse2 text paste events from reaching Fl_Text_Editor + if (Fl::event_button() == FL_MIDDLE_MOUSE) + return 1; + if (Fl::event_button() != FL_RIGHT_MOUSE) break; - const Fl_Menu_Item * m; - int xpos = Fl::event_x(); - int ypos = Fl::event_y(); - + // enable/disable menu items if (tbuf->length()) - viewmenu[RX_MENU_CLEAR].flags &= ~FL_MENU_INACTIVE; + view_menu[RX_MENU_CLEAR].flags &= ~FL_MENU_INACTIVE; else - viewmenu[RX_MENU_CLEAR].flags |= FL_MENU_INACTIVE; + view_menu[RX_MENU_CLEAR].flags |= FL_MENU_INACTIVE; if (tbuf->selected()) - viewmenu[RX_MENU_COPY].flags &= ~FL_MENU_INACTIVE; + view_menu[RX_MENU_COPY].flags &= ~FL_MENU_INACTIVE; else - viewmenu[RX_MENU_COPY].flags |= FL_MENU_INACTIVE; + view_menu[RX_MENU_COPY].flags |= FL_MENU_INACTIVE; if (wrap) - viewmenu[RX_MENU_WRAP].flags |= FL_MENU_VALUE; + view_menu[RX_MENU_WRAP].flags |= FL_MENU_VALUE; else - viewmenu[RX_MENU_WRAP].flags &= ~FL_MENU_VALUE; + view_menu[RX_MENU_WRAP].flags &= ~FL_MENU_VALUE; - popx = xpos - x(); - popy = ypos - y(); - m = viewmenu->popup(xpos, ypos, 0, 0, 0); - if (m) { - int msize = sizeof(viewmenu) / sizeof(viewmenu[0]); - for (int i = 0; i < msize; i++) { - if (m == &viewmenu[i]) { - menu_cb(i); - break; - } - } - } + show_context_menu(); return 1; break; + // catch some text-modifying events that are not handled by kf_* functions + case FL_KEYBOARD: + int d; + if (Fl::compose(d)) + return 1; + int k = Fl::event_key(); + if (k == FL_BackSpace || k == FL_Tab) + return 1; } - return Fl_Text_Display::handle(event); + return TextBase::handle(event); } -void TextView::add(char c, int attr) +/// Adds a char to the buffer +/// +/// @param c The character +/// @param attr The attribute (@see enum text_attr_e); DEFAULT if omitted. +/// +void TextView::add(char c, text_attr_t attr) { if (c == '\r') return; + Fl::lock(); + + // The user may have moved the cursor by selecting text or + // scrolling. Place it at the end of the buffer. + insert_position(tbuf->length()); + switch (c) { case '\b': - { - int tl = tbuf->length(), sl = sbuf->length(); - tbuf->remove(tl - 1, tl); - sbuf->remove(sl - 1, sl); - } - break; + // we don't call kf_backspace because it kills selected text + tbuf->remove(tbuf->length() - 1, tbuf->length()); + sbuf->remove(sbuf->length() - 1, sbuf->length()); + break; + case '\n': + // maintain the scrollback limit, if we have one + if (max_lines > 0 && tbuf->count_lines(0, tbuf->length()) >= max_lines) { + int le = tbuf->line_end(0) + 1; // plus 1 for the newline + tbuf->remove(0, le); + sbuf->remove(0, le); + } + // fall-through default: - attr = (attr >= 0 && attr < NSTYLES) ? attr : 0; char s[] = { c, '\0' }; - tbuf->append(s); - s[0] = 'A' + attr; + insert(s); + s[0] = attr; sbuf->append(s); break; } - redraw(); Fl::unlock(); Fl::awake(); } -void TextView::add(const char *s, int attr) + +/// Appends a string to the buffer +/// +/// @param s +/// @param attr +/// +void TextView::add(const char *s, text_attr_t attr) { while (*s) add(*s++, attr); } -void TextView::clear(void) -{ - tbuf->text(""); - sbuf->text(""); -} - - -void TextView::setFont(int n, Fl_Font f) -{ - if (n >= 0 && n < NSTYLES) - styles[n].font = f; - else if (n == -1) - for (int i = 0; i < NSTYLES; i++) - styles[i].font = f; - redraw(); -} -void TextView::setFontSize(int n, int s) -{ - if (n >= 0 && n < NSTYLES) - styles[n].size = s; - else if (n == -1) - for (int i = 0; i < NSTYLES; i++) - styles[i].size = s; - redraw(); -} -void TextView::setFontColor(int n, Fl_Color c) -{ - if (n >= 0 && n < NSTYLES) - styles[n].color = c; - else if (n == -1) - for (int i = 0; i < NSTYLES; i++) - styles[i].color = c; - redraw(); -} - - -void TextView::draw(void) -{ - Fl_Text_Display::draw(); - redraw(); -} - +/// The context menu handler +/// +/// @param val +/// void TextView::menu_cb(int val) { handle(FL_UNFOCUS); + switch (val) { char *s; case RX_MENU_QRZ_THIS: @@ -261,7 +434,7 @@ void TextView::menu_cb(int val) clear(); break; case RX_MENU_COPY: - clipboard_copy(); + kf_copy(Fl::event_key(), this); break; case RX_MENU_SAVE: @@ -269,72 +442,86 @@ void TextView::menu_cb(int val) break; case RX_MENU_WRAP: - viewmenu[RX_MENU_WRAP].flags ^= FL_MENU_VALUE; - wrap_mode((wrap = !wrap), 0); + view_menu[RX_MENU_WRAP].flags ^= FL_MENU_VALUE; + wrap_mode((wrap = !wrap), wrap_col); show_insert_position(); break; } } +/// Scrolls down if the buffer has been modified and the last line is +/// visible. See Fl_Text_Buffer::add_modify_callback() for parameter details. +/// +/// @param pos +/// @param nins +/// @param ndel +/// @param nsty +/// @param dtext +/// @param arg +/// void TextView::changed_cb(int pos, int nins, int ndel, int nsty, const char *dtext, void *arg) { TextView *v = (TextView *)arg; - // if we are displaying the last line we should scroll down to keep it visible - if (v->mTopLineNum + v->mNVisibleLines > v->mNBufferLines) - v->scroll(v->mNBufferLines - 1, 0); - if (!v->wrap) { - // keep the cursor at the end of the buffer; this does not affect mouse - // selections or scrolling - v->insert_position(v->tbuf->length()); - v->show_insert_position(); + // In the ctor we removed the Fl_Text_Display callback because we want + // it to run *before* our callback, so call it now. + v->buffer_modified_cb(pos, nins, ndel, nsty, dtext, arg); + + // Is the last line visible? To scroll when it isn't would make + // text selection impossible while receiving. + if (v->mTopLineNum + v->mNVisibleLines > v->mNBufferLines) { + if (v->wrap) { + // v->scroll(v->mNBufferLines, 0); + + // The scrolling code below is a little expensive, but + // takes care of the only known case where the simple + // scroll above is not enough. Specifically, the height + // of the widget and font can be such that the last text + // line is partially outside the text area, but + // technically still visible. This can only happen once, + // until the next newline displays the scrollbar, so I + // am just being pedantic here. + + // scroll if the last character is vertically outside the text area + int x, y; + if (v->position_to_xy(v->tbuf->length() - 1, &x, &y) == 0 || + y + fl_height() >= v->text_area.h) + v->show_insert_position(); + } + else + v->show_insert_position(); + } +} + +/// Removes Fl_Text_Edit keybindings that would modify text and put it out of +/// sync with the style buffer. At some point we may decide that we want +/// TextView to be editable (e.g., to insert comments about a QSO), in which +/// case we'll keep the keybindings and add some code to changed_cb to update +/// the style buffer. +/// +void TextView::change_keybindings(void) +{ + Fl_Text_Editor::Key_Func fdelete[] = { Fl_Text_Editor::kf_default, + Fl_Text_Editor::kf_enter, + Fl_Text_Editor::kf_delete, + Fl_Text_Editor::kf_cut, + Fl_Text_Editor::kf_paste }; + int n = sizeof(fdelete) / sizeof(fdelete[0]); + + // walk the keybindings linked list and delete items containing elements + // of fdelete + for (Fl_Text_Editor::Key_Binding *k = key_bindings; k; k = k->next) { + for (int i = 0; i < n; i++) + if (k->function == fdelete[i]) + remove_key_binding(k->key, k->state); } } -// caller must free() returned string -char *TextView::get_word(int x, int y) -{ - int p = xy_to_position(x + this->x(), y + this->y(), Fl_Text_Display::CURSOR_POS); - tbuf->select(word_start(p), word_end(p)); - char *s = tbuf->selection_text(); - tbuf->unselect(); - - return s; -} - -void TextView::saveFile(void) -{ - char *fn = File_Select("Select ASCII text file", "*.txt", "", 0); - if (fn) - tbuf->outputfile(fn, 0, tbuf->length()); -} - -void TextView::clipboard_copy(void) -{ - if (tbuf->selected()) { // copy selection to primary clipboard - char *s = tbuf->selection_text(); - - Fl::copy(s, strlen(s), 1); - free(s); - } -} - -#ifdef HSCROLLBAR_KLUDGE -void TextView::resize(int X, int Y, int W, int H) -{ - Fl_Text_Display::resize(X, Y, W, H); - if (!wrap || !mVScrollBar->visible()) - return; -# include "TextView_resize.cxx" -} -#endif // HSCROLLBAR_KLUDGE +// ---------------------------------------------------------------------------- -//============================================================================== - - -Fl_Menu_Item TextEdit::editmenu[] = { +Fl_Menu_Item TextEdit::edit_menu[] = { {"&Transmit", 0, 0 }, {"&Receive", 0, 0 }, {"Send &image...", 0, 0, 0, FL_MENU_DIVIDER }, @@ -347,109 +534,112 @@ Fl_Menu_Item TextEdit::editmenu[] = { { 0 } }; -Fl_Text_Display::Style_Table_Entry TextEdit::styles[NSTYLES]; -int *TextEdit::ptxpos = 0; // needed by our static kf functions +// needed by our static kf functions, which may restrict editing depending on +// the transmit cursor position +int *TextEdit::ptxpos; TextEdit::TextEdit(int x, int y, int w, int h, const char *l) - : Fl_Text_Editor(x, y, w, h, l), PauseBreak(false), txpos(0), - bkspaces(0), wrap(true) + : TextBase(x, y, w, h, l), PauseBreak(false), txpos(0), bkspaces(0) { ptxpos = &txpos; - tbuf = new Fl_Text_Buffer; - sbuf = new Fl_Text_Buffer; - cursor_style(Fl_Text_Display::NORMAL_CURSOR); - buffer(tbuf); - highlight_data(sbuf, styles, NSTYLES, 'A', 0, 0); tbuf->add_modify_callback(changed_cb, this); - wrap_mode(wrap, 0); - scrollbar_width((int)floor(scrollbar_width() * 3.0/4.0)); - - // set some defaults - setFont(-1, FL_COURIER); - setFontSize(-1, 12); - setFontColor(-1, FL_BLACK); - setFontColor(2, FL_BLUE); - setFontColor(3, FL_GREEN); - setFontColor(4, FL_RED); - + context_menu = edit_menu; change_keybindings(); } -TextEdit::~TextEdit() -{ - delete tbuf; - delete sbuf; -} +/// Handles fltk events for this widget. +/// We pass keyboard events to handle_key() and handle mouse3 presses to show +/// the popup menu. We also disallow mouse2 events in the transmitted text area. +/// Everything else is passed to the base class handle(). +/// +/// @param event +/// +/// @return +/// int TextEdit::handle(int event) { if (event == FL_KEYBOARD) { autolock txlock; - return handle_key(Fl::event_key()) ? 1 : Fl_Text_Editor::handle(event); + return handle_key(Fl::event_key()) ? 1 : TextBase::handle(event); } - if ( !(Fl::event_inside(this) && event == FL_PUSH && - Fl::event_button() == FL_RIGHT_MOUSE) ) - return Fl_Text_Editor::handle(event); + if (!(Fl::event_inside(this) && event == FL_PUSH)) + return TextBase::handle(event); + + // do not mouse2-paste in the transmitted text + if (Fl::event_button() == FL_MIDDLE_MOUSE && + xy_to_position(Fl::event_x(), Fl::event_y(), + Fl_Text_Display::CHARACTER_POS) < txpos) + return 1; + + if (Fl::event_button() != FL_RIGHT_MOUSE) + return TextBase::handle(event); // handle a right click - const Fl_Menu_Item * m; - int xpos = Fl::event_x(); - int ypos = Fl::event_y(); - if (active_modem != mfsk16_modem) - editmenu[TX_MENU_MFSK16_IMG].flags |= FL_MENU_INACTIVE; + edit_menu[TX_MENU_MFSK16_IMG].flags |= FL_MENU_INACTIVE; else - editmenu[TX_MENU_MFSK16_IMG].flags &= ~FL_MENU_INACTIVE; + edit_menu[TX_MENU_MFSK16_IMG].flags &= ~FL_MENU_INACTIVE; if (tbuf->length()) - editmenu[TX_MENU_CLEAR].flags &= ~FL_MENU_INACTIVE; + edit_menu[TX_MENU_CLEAR].flags &= ~FL_MENU_INACTIVE; else - editmenu[TX_MENU_CLEAR].flags |= FL_MENU_INACTIVE; + edit_menu[TX_MENU_CLEAR].flags |= FL_MENU_INACTIVE; if (tbuf->selected()) { - editmenu[TX_MENU_CUT].flags &= ~FL_MENU_INACTIVE; - editmenu[TX_MENU_COPY].flags &= ~FL_MENU_INACTIVE; + edit_menu[TX_MENU_CUT].flags &= ~FL_MENU_INACTIVE; + edit_menu[TX_MENU_COPY].flags &= ~FL_MENU_INACTIVE; } else { - editmenu[TX_MENU_CUT].flags |= FL_MENU_INACTIVE; - editmenu[TX_MENU_COPY].flags |= FL_MENU_INACTIVE; + edit_menu[TX_MENU_CUT].flags |= FL_MENU_INACTIVE; + edit_menu[TX_MENU_COPY].flags |= FL_MENU_INACTIVE; } if (wrap) - editmenu[TX_MENU_WRAP].flags |= FL_MENU_VALUE; + edit_menu[TX_MENU_WRAP].flags |= FL_MENU_VALUE; else - editmenu[TX_MENU_WRAP].flags &= ~FL_MENU_VALUE; + edit_menu[TX_MENU_WRAP].flags &= ~FL_MENU_VALUE; - popx = xpos - x(); - popy = ypos - y(); - m = editmenu->popup(xpos, ypos, 0, 0, 0); - if (m) { - int msize = sizeof(editmenu) / sizeof(editmenu[0]); - for (int i = 0; i < msize; i++) - if (m == &editmenu[i]) { - menu_cb(i); - break; - } - } + show_context_menu(); return 1; } -void TextEdit::add(const char *s, int attr) +/// @see TextView::add +/// +/// @param s +/// @param attr +/// +void TextEdit::add(const char *s, text_attr_t attr) { - tbuf->append(s); + insert(s); + + int n = strlen(s); + char a[n + 1]; + memset(a, attr, n); + a[n] = '\0'; + sbuf->replace(sbuf->length() - n, sbuf->length(), a); + insert_position(tbuf->length()); show_insert_position(); } + +/// Clears the buffer. +/// Also resets the transmit position, stored backspaces and tx pause flag. +/// void TextEdit::clear(void) { autolock lock; - tbuf->text(""); - sbuf->text(""); + TextBase::clear(); txpos = 0; bkspaces = 0; PauseBreak = false; } +/// Returns the next character to be transmitted. +/// +/// @return The next character, or ETX if the transmission has been paused, or +/// NUL if no text should be transmitted. +/// int TextEdit::nextChar(void) { autolock txlock; @@ -463,55 +653,28 @@ int TextEdit::nextChar(void) PauseBreak = false; c = 0x03; } - else if (insert_position() <= txpos) + else if (insert_position() <= txpos) // empty buffer or cursor inside transmitted text c = '\0'; else { - if ((c = tbuf->character(txpos))) + if ((c = tbuf->character(txpos))) { ++txpos; - // we do not call tbuf->call_modify_callbacks() here - // because we are only updating the style buffer - changed_cb(0, 0, 0, 0, 0, this); - redraw(); + // we do not call tbuf->call_modify_callbacks() here + // because we are only updating the style buffer + changed_cb(0, 0, 0, 0, 0, this); + redraw(); + } } return c; } - -void TextEdit::setFont(int n, Fl_Font f) -{ - if (n >= 0 && n < NSTYLES) - styles[n].font = f; - else if (n == -1) - for (int i = 0; i < NSTYLES; i++) - styles[i].font = f; - - redraw(); -} -void TextEdit::setFontSize(int n, int s) -{ - if (n >= 0 && n < NSTYLES) - styles[n].size = s; - else if (n == -1) - for (int i = 0; i < NSTYLES; i++) - styles[i].size = s; - redraw(); -} -void TextEdit::setFontColor(int n, Fl_Color c) -{ - if (n >= 0 && n < NSTYLES) - styles[n].color = c; - else if (n == -1) - for (int i = 0; i < NSTYLES; i++) - styles[i].color = c; - redraw(); -} -void TextEdit::cursorON(void) -{ - show_cursor(); -} - - +/// Handles keyboard events to override Fl_Text_Editor's handling of some +/// keystrokes. +/// +/// @param key +/// +/// @return +/// int TextEdit::handle_key(int key) { switch (key) { @@ -565,8 +728,8 @@ int TextEdit::handle_key(int key) // cursor to let some more text through. Another (ctrl-)tab goes back to // the end of the buffer and resumes sending. - // In CW mode: Tab pauses, skips rest of buffer, paints it blue, then - // resumes sending when new text is entered. + // In CW mode: Tab pauses, skips rest of buffer, applies the + // SKIP style, then resumes sending when new text is entered. // Ctrl-tab does the same thing as for all other modes. insert_position(txpos != insert_position() ? txpos : tbuf->length()); @@ -574,15 +737,16 @@ int TextEdit::handle_key(int key) int n = tbuf->length() - txpos; char s[n + 1]; - memset(s, 'C', n); + memset(s, SKIP, n); s[n] = 0; sbuf->replace(txpos, sbuf->length(), s); - txpos = tbuf->length(); - insert_position(txpos); + insert_position(txpos = tbuf->length()); + redraw(); } // show_insert_position(); return 1; break; + // Move cursor, or search up/down with the Meta/Alt modifiers case FL_Left: if (Fl::event_state() & (FL_META | FL_ALT)) { active_modem->searchDown(); @@ -595,6 +759,7 @@ int TextEdit::handle_key(int key) return 1; } break; + // queue a BS and decr. the txpos, unless the cursor is in the tx text case FL_BackSpace: { int ipos = insert_position(); @@ -612,7 +777,7 @@ int TextEdit::handle_key(int key) // return 1; // break; default: - if (key >= FL_F && key <= FL_F_Last) { + if (key >= FL_F && key <= FL_F_Last) { // insert a macro int b = key - FL_F - 1; if (b > 9) return 0; @@ -636,57 +801,23 @@ int TextEdit::handle_key(int key) return 0; } -void TextEdit::readFile(void) -{ - char *fn = File_Select("Select ASCII text file", "*.txt", "", 0); - if (fn) { - tbuf->appendfile(fn); - insert_position(tbuf->length()); - show_insert_position(); - } -} - -void TextEdit::changed_cb(int pos, int nins, int ndel, int nsty, const char *dtext, void *arg) -{ - TextEdit *e = (TextEdit *)arg; - - if (nins == 0 && ndel == 0) { - if (pos == 0 && nsty == 0) { // update transmitted text style - char s[2] = { 'E', '\0' }; - e->sbuf->replace(e->txpos - 1, e->txpos, s); - } - - e->sbuf->unselect(); - return; - } - - if (nins > 0) { - char *s = new char[nins + 1]; - - memset(s, 'A', nins); - s[nins] = 0; - e->sbuf->replace(pos, pos + ndel, s); - delete [] s; - } - else - e->sbuf->remove(pos, pos + ndel); - - e->sbuf->select(pos, pos + nins - ndel); -} - +/// The context menu handler +/// +/// @param val +/// void TextEdit::menu_cb(int val) { handle(FL_UNFOCUS); switch (val) { case TX_MENU_TX: + active_modem->set_stopflag(false); fl_lock(&trx_mutex); trx_state = STATE_TX; fl_unlock(&trx_mutex); wf->set_XmtRcvBtn(true); break; case TX_MENU_RX: - tbuf->append("^r"); - insert_position(tbuf->length()); + add("^r", CTRL); break; case TX_MENU_MFSK16_IMG: if (active_modem->get_mode() == MODE_MFSK16) @@ -711,14 +842,63 @@ void TextEdit::menu_cb(int val) break; case TX_MENU_WRAP: - editmenu[TX_MENU_WRAP].flags ^= FL_MENU_VALUE; - wrap_mode((wrap = !wrap), 0); + edit_menu[TX_MENU_WRAP].flags ^= FL_MENU_VALUE; + wrap_mode((wrap = !wrap), wrap_col); show_insert_position(); break; } } +/// This function is called by Fl_Text_Buffer when the buffer is modified, and +/// also by nextChar when a character has been passed up the transmit path. In +/// the first case either nins or ndel will be nonzero, and we change a +/// corresponding amount of text in the style buffer. +/// +/// In the latter case, nins, ndel, pos and nsty are all zero and we update the +/// style buffer to mark the last character in the buffer with the XMT +/// attribute. +/// +/// The select/unselect calls are there to minimize redrawing (copied from +/// Fl_Text_Display/Editor; need to verify that they work as expected). +/// +/// @param pos +/// @param nins +/// @param ndel +/// @param nsty +/// @param dtext +/// @param arg +/// +void TextEdit::changed_cb(int pos, int nins, int ndel, int nsty, const char *dtext, void *arg) +{ + TextEdit *e = (TextEdit *)arg; + if (nins == 0 && ndel == 0) { + if (pos == 0 && nsty == 0) { // update transmitted text style + char s[2] = { XMT, '\0' }; + e->sbuf->replace(e->txpos - 1, e->txpos, s); + } + + e->sbuf->unselect(); + return; + } + + if (nins > 0) { // set the default style for newly inserted text + char *s = new char[nins + 1]; + + memset(s, DEFAULT, nins); + s[nins] = 0; + e->sbuf->replace(pos, pos + ndel, s); + delete [] s; + } + else + e->sbuf->remove(pos, pos + ndel); + + e->sbuf->select(pos, pos + nins - ndel); +} + +/// Overrides some useful Fl_Text_Edit keybindings that we want to keep working, +/// provided that they don't try to change chunks of transmitted text. +/// void TextEdit::change_keybindings(void) { struct { @@ -730,6 +910,8 @@ void TextEdit::change_keybindings(void) { Fl_Text_Editor::kf_paste, TextEdit::kf_paste } }; int n = sizeof(fbind) / sizeof(fbind[0]); + // walk the keybindings linked list and replace items containing + // functions for which we have an override in fbind for (Fl_Text_Editor::Key_Binding *k = key_bindings; k; k = k->next) { for (int i = 0; i < n; i++) if (fbind[i].function == k->function) @@ -737,6 +919,9 @@ void TextEdit::change_keybindings(void) } } +// The kf_* functions below call the corresponding Fl_Text_Editor routines, but +// may make adjustments so that no transmitted text is modified. + int TextEdit::kf_default(int c, Fl_Text_Editor* e) { autolock txlock; @@ -789,14 +974,3 @@ int TextEdit::kf_paste(int c, Fl_Text_Editor* e) autolock txlock; return e->insert_position() < *ptxpos ? 1 : Fl_Text_Editor::kf_paste(c, e); } - - -#ifdef HSCROLLBAR_KLUDGE -void TextEdit::resize(int X, int Y, int W, int H) -{ - Fl_Text_Editor::resize(X, Y, W, H); - if (!wrap || !mVScrollBar->visible()) - return; -# include "TextView_resize.cxx" -} -#endif // HSCROLLBAR_KLUDGE diff --git a/src/dialogs/TextView_resize.cxx b/src/dialogs/TextView_resize.cxx index 070451b5..d44788d3 100644 --- a/src/dialogs/TextView_resize.cxx +++ b/src/dialogs/TextView_resize.cxx @@ -6,61 +6,165 @@ #define LEFT_MARGIN 3 #define RIGHT_MARGIN 3 -const int oldWidth = w(); +#ifdef DEBUG + printf("Fl_Text_Display::resize(X=%d, Y=%d, W=%d, H=%d)\n", X, Y, W, H); +#endif // DEBUG + const int oldWidth = w(); +#ifdef DEBUG + printf(" oldWidth=%d, mContinuousWrap=%d, mWrapMargin=%d\n", oldWidth, + mContinuousWrap, mWrapMargin); +#endif // DEBUG + Fl_Widget::resize(X,Y,W,H); + if (!buffer()) return; + X += Fl::box_dx(box()); + Y += Fl::box_dy(box()); + W -= Fl::box_dw(box()); + H -= Fl::box_dh(box()); -mHScrollBar->clear_visible(); -mVScrollBar->resize(mVScrollBar->x(), mVScrollBar->y(), - mVScrollBar->w(), mVScrollBar->h() + scrollbar_width()); + text_area.x = X+LEFT_MARGIN; + text_area.y = Y+BOTTOM_MARGIN; + text_area.w = W-LEFT_MARGIN-RIGHT_MARGIN; + text_area.h = H-TOP_MARGIN-BOTTOM_MARGIN; + int i; -text_area.h += scrollbar_width(); -if (scrollbar_align() & FL_ALIGN_TOP) - text_area.y -= scrollbar_width(); -mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y, - text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width()); + /* Find the new maximum font height for this text display */ + for (i = 0, mMaxsize = fl_height(textfont(), textsize()); i < mNStyles; i++) + mMaxsize = max(mMaxsize, fl_height(mStyleTable[i].font, mStyleTable[i].size)); + // did we have scrollbars initially? + int hscrollbarvisible = mHScrollBar->visible(); + int vscrollbarvisible = mVScrollBar->visible(); -if (mContinuousWrap && !mWrapMargin && W!=oldWidth) { - int oldFirstChar = mFirstChar; - mNBufferLines = count_lines(0, buffer()->length(), true); - mFirstChar = line_start(mFirstChar); - mTopLineNum = count_lines(0, mFirstChar, true)+1; - absolute_top_line_number(oldFirstChar); -} + // try without scrollbars first + mVScrollBar->clear_visible(); + mHScrollBar->clear_visible(); -int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize; -if (nvlines < 1) nvlines = 1; -if (mNVisibleLines != nvlines) { - mNVisibleLines = nvlines; - if (mLineStarts) delete[] mLineStarts; - mLineStarts = new int [mNVisibleLines]; -} + int again_ = 1; + for (int again = 1; again;) { + again = 0; + /* In continuous wrap mode, a change in width affects the total number of + lines in the buffer, and can leave the top line number incorrect, and + the top character no longer pointing at a valid line start */ + if (mContinuousWrap && !mWrapMargin && W!=oldWidth) { + int oldFirstChar = mFirstChar; + mNBufferLines = count_lines(0, buffer()->length(), true); + mFirstChar = line_start(mFirstChar); + mTopLineNum = count_lines(0, mFirstChar, true)+1; + absolute_top_line_number(oldFirstChar); -calc_line_starts(0, mNVisibleLines); -calc_last_char(); +#ifdef DEBUG + printf(" mNBufferLines=%d\n", mNBufferLines); +#endif // DEBUG + } + + /* reallocate and update the line starts array, which may have changed + size and / or contents. */ + int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize; + if (nvlines < 1) nvlines = 1; + if (mNVisibleLines != nvlines) { + mNVisibleLines = nvlines; + if (mLineStarts) delete[] mLineStarts; + mLineStarts = new int [mNVisibleLines]; + } -// user request to change viewport -if (mTopLineNumHint != mTopLineNum || mHorizOffsetHint != mHorizOffset) - scroll_(mTopLineNumHint, mHorizOffsetHint); + calc_line_starts(0, mNVisibleLines); + calc_last_char(); -// everything will fit in the viewport -if (mNBufferLines < mNVisibleLines || mBuffer == NULL || mBuffer->length() == 0) - scroll_(1, mHorizOffset); -/* if empty lines become visible, there may be an opportunity to - display more text by scrolling down */ -else while (mLineStarts[mNVisibleLines-2] == -1) - scroll_(mTopLineNum-1, mHorizOffset); + // figure the scrollbars + if (scrollbar_width()) { + /* Decide if the vertical scroll bar needs to be visible */ + if (scrollbar_align() & (FL_ALIGN_LEFT|FL_ALIGN_RIGHT) && + mNBufferLines >= mNVisibleLines - 1) + { + mVScrollBar->set_visible(); + if (scrollbar_align() & FL_ALIGN_LEFT) { + text_area.x = X+scrollbar_width()+LEFT_MARGIN; + text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN; + mVScrollBar->resize(X, text_area.y-TOP_MARGIN, scrollbar_width(), + text_area.h+TOP_MARGIN+BOTTOM_MARGIN); + } else { + text_area.x = X+LEFT_MARGIN; + text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN; + mVScrollBar->resize(X+W-scrollbar_width(), text_area.y-TOP_MARGIN, + scrollbar_width(), text_area.h+TOP_MARGIN+BOTTOM_MARGIN); + } + } -// in case horizontal offset is now greater than longest line -int maxhoffset = max(0, longest_vline()-text_area.w); -if (mHorizOffset > maxhoffset) - scroll_(mTopLineNumHint, maxhoffset); + /* + Decide if the horizontal scroll bar needs to be visible. If there + is a vertical scrollbar, a horizontal is always created too. This + is because the alternatives are unatractive: + * Dynamically creating a horizontal scrollbar based on the currently + visible lines is what the original nedit does, but it always wastes + space for the scrollbar even when it's not used. Since the FLTK + widget dynamically allocates the space for the scrollbar and + rearranges the widget to make room for it, this would create a very + visually displeasing "bounce" effect when the vertical scrollbar is + dragged. Trust me, I tried it and it looks really bad. + * The other alternative would be to keep track of what the longest + line in the entire buffer is and base the scrollbar on that. I + didn't do this because I didn't see any easy way to do that using + the nedit code and this could involve a lengthy calculation for + large buffers. If an efficient and non-costly way of doing this + can be found, this might be a way to go. + */ + /* WAS: Suggestion: Try turning the horizontal scrollbar on when + you first see a line that is too wide in the window, but then + don't turn it off (ie mix both of your solutions). */ + if (!mContinuousWrap && scrollbar_align() & (FL_ALIGN_TOP|FL_ALIGN_BOTTOM) && + (mVScrollBar->visible() || longest_vline() > text_area.w)) + { + if (!mHScrollBar->visible()) { + mHScrollBar->set_visible(); + again = 1; // loop again to see if we now need vert. & recalc sizes + } + if (scrollbar_align() & FL_ALIGN_TOP) { + text_area.y = Y + scrollbar_width()+TOP_MARGIN; + text_area.h = H - scrollbar_width()-TOP_MARGIN-BOTTOM_MARGIN; + mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y, + text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width()); + } else { + text_area.y = Y+TOP_MARGIN; + text_area.h = H - scrollbar_width()-TOP_MARGIN-BOTTOM_MARGIN; + mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y+H-scrollbar_width(), + text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width()); + } + } else if (again_ == 1) { // loop once more + again_ = 0; + again = 1; + } + } + } -mTopLineNumHint = mTopLineNum; -mHorizOffsetHint = mHorizOffset; -display_insert_position_hint = 0; + // user request to change viewport + if (mTopLineNumHint != mTopLineNum || mHorizOffsetHint != mHorizOffset) + scroll_(mTopLineNumHint, mHorizOffsetHint); -if (mContinuousWrap) - redraw(); + // everything will fit in the viewport + if (mNBufferLines < mNVisibleLines || mBuffer == NULL || mBuffer->length() == 0) + scroll_(1, mHorizOffset); + /* if empty lines become visible, there may be an opportunity to + display more text by scrolling down */ + else while (mLineStarts[mNVisibleLines-2] == -1) + scroll_(mTopLineNum-1, mHorizOffset); -update_v_scrollbar(); -update_h_scrollbar(); + // user request to display insert position + if (display_insert_position_hint) + display_insert(); + + // in case horizontal offset is now greater than longest line + int maxhoffset = max(0, longest_vline()-text_area.w); + if (mHorizOffset > maxhoffset) + scroll_(mTopLineNumHint, maxhoffset); + + mTopLineNumHint = mTopLineNum; + mHorizOffsetHint = mHorizOffset; + display_insert_position_hint = 0; + + if (mContinuousWrap || + hscrollbarvisible != mHScrollBar->visible() || + vscrollbarvisible != mVScrollBar->visible()) + redraw(); + + update_v_scrollbar(); + update_h_scrollbar(); diff --git a/src/dialogs/fl_digi.cxx b/src/dialogs/fl_digi.cxx index b9f6e054..2a646f3d 100644 --- a/src/dialogs/fl_digi.cxx +++ b/src/dialogs/fl_digi.cxx @@ -22,6 +22,10 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // ---------------------------------------------------------------------------- +#include +#include +#include + #include #include @@ -58,6 +62,7 @@ #include "globals.h" #include "misc.h" //#include "help.h" +#include "TextView.h" #include "Config.h" #include "configuration.h" @@ -141,10 +146,10 @@ void startup_modem(modem *m) if (m == feld_modem || m == feld_FMmodem || m == feld_FM105modem ) { - ReceiveText->Hide(); + ReceiveText->hide(); FHdisp->show(); } else { - ReceiveText->Show(); + ReceiveText->show(); FHdisp->hide(); } Fl::unlock(); @@ -301,6 +306,19 @@ void cb_mnuPSK125(Fl_Menu_*, void*) { initPSK125(); } +void initPSK250() +{ + clearStatus(); + if(!psk250_modem) + psk250_modem = new psk(MODE_PSK250); + startup_modem (psk250_modem); + progStatus.saveModeState(MODE_PSK250); +} + +void cb_mnuPSK250(Fl_Menu_*, void*) { + initPSK250(); +} + void initQPSK31() { clearStatus(); @@ -340,6 +358,19 @@ void cb_mnuQPSK125(Fl_Menu_*, void*) { initQPSK125(); } +void initQPSK250() +{ + clearStatus(); + if (!qpsk250_modem) + qpsk250_modem = new psk(MODE_QPSK250); + startup_modem (qpsk250_modem); + progStatus.saveModeState(MODE_QPSK250); +} + +void cb_mnuQPSK250(Fl_Menu_*, void*) { + initQPSK250(); +} + void initRTTY() { clearStatus(); @@ -984,43 +1015,40 @@ Fl_Menu_Item menu_[] = { {"qpsk 63", 0, (Fl_Callback*)cb_mnuQPSK63, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 29 {"psk 125", 0, (Fl_Callback*)cb_mnuPSK125, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 30 {"qpsk 125", 0, (Fl_Callback*)cb_mnuQPSK125, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 31 -{0,0,0,0,0,0,0,0,0}, // 32 -{"Olivia", 0, (Fl_Callback*)cb_mnuOlivia, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 33 -{"rtty", 0, (Fl_Callback*)cb_mnuRTTY, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 34 -{"Throb", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 35 -{"Throb 1", 0, (Fl_Callback*)cb_mnuTHROB1, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 36 -{"Throb 2", 0, (Fl_Callback*)cb_mnuTHROB2, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 37 -{"Throb 4", 0, (Fl_Callback*)cb_mnuTHROB4, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 38 -{"ThrobX 1", 0, (Fl_Callback*)cb_mnuTHROBX1, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 39 -{"ThrobX 2", 0, (Fl_Callback*)cb_mnuTHROBX2, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 40 -{"ThrobX 4", 0, (Fl_Callback*)cb_mnuTHROBX4, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 41 -{0,0,0,0,0,0,0,0,0}, // 42 -{"WWV", 0, (Fl_Callback*)cb_mnuWWV, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 43 -{"Freq Analysis", 0, (Fl_Callback*)cb_mnuANALYSIS, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 44 -{0,0,0,0,0,0,0,0,0}, // 45 -{"Configure", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 46 -{"Defaults", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 47 -{"Fonts", 0, (Fl_Callback*)cb_mnuConfigFonts, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 48 -{"Interface", 0, (Fl_Callback*)cb_mnuConfigInterface, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 49 -{"Operator", 0, (Fl_Callback*)cb_mnuConfigOperator, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 50 -{"Sound Card", 0, (Fl_Callback*)cb_mnuConfigSoundCard, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 51 -{"Waterfall", 0, (Fl_Callback*)cb_mnuConfigWaterfall, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 52 -{0,0,0,0,0,0,0,0,0}, // 53 -{"Modems", 0, (Fl_Callback*)cb_mnuConfigModems, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 54 -{"Save Config", 0, (Fl_Callback*)cb_mnuSaveConfig, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 55 -{0,0,0,0,0,0,0,0,0}, // 56 -{" ", 0, 0, 0, FL_MENU_INACTIVE, FL_NORMAL_LABEL, 0, 14, 0}, // 57 -{"Rig", 0, (Fl_Callback*)cb_mnuRig, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 58 +{"psk 250", 0, (Fl_Callback*)cb_mnuPSK250, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 32 +{"qpsk 250", 0, (Fl_Callback*)cb_mnuQPSK250, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 33 +{0,0,0,0,0,0,0,0,0}, // 34 +{"Olivia", 0, (Fl_Callback*)cb_mnuOlivia, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 35 +{"rtty", 0, (Fl_Callback*)cb_mnuRTTY, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 36 +{"Throb", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 37 +{"Throb 1", 0, (Fl_Callback*)cb_mnuTHROB1, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 38 +{"Throb 2", 0, (Fl_Callback*)cb_mnuTHROB2, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 39 +{"Throb 4", 0, (Fl_Callback*)cb_mnuTHROB4, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 40 +{"ThrobX 1", 0, (Fl_Callback*)cb_mnuTHROBX1, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 41 +{"ThrobX 2", 0, (Fl_Callback*)cb_mnuTHROBX2, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 42 +{"ThrobX 4", 0, (Fl_Callback*)cb_mnuTHROBX4, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 43 +{0,0,0,0,0,0,0,0,0}, // 44 +{"WWV", 0, (Fl_Callback*)cb_mnuWWV, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 45 +{"Freq Analysis", 0, (Fl_Callback*)cb_mnuANALYSIS, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 46 +{0,0,0,0,0,0,0,0,0}, // 47 +{"Configure", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 48 +{"Defaults", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 49 +{"Fonts", 0, (Fl_Callback*)cb_mnuConfigFonts, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 50 +{"Interface", 0, (Fl_Callback*)cb_mnuConfigInterface, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 51 +{"Operator", 0, (Fl_Callback*)cb_mnuConfigOperator, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 52 +{"Sound Card", 0, (Fl_Callback*)cb_mnuConfigSoundCard, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 53 +{"Waterfall", 0, (Fl_Callback*)cb_mnuConfigWaterfall, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 54 +{0,0,0,0,0,0,0,0,0}, // 55 +{"Modems", 0, (Fl_Callback*)cb_mnuConfigModems, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 56 +{"Save Config", 0, (Fl_Callback*)cb_mnuSaveConfig, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 57 +{0,0,0,0,0,0,0,0,0}, // 58 {" ", 0, 0, 0, FL_MENU_INACTIVE, FL_NORMAL_LABEL, 0, 14, 0}, // 59 -{"Test", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 60 -{"Capture", 0, (Fl_Callback*)cb_mnuCapture, 0, FL_MENU_TOGGLE, FL_NORMAL_LABEL, 0, 14, 0}, // 61 -{"Generate", 0, (Fl_Callback*)cb_mnuGenerate, 0, FL_MENU_TOGGLE, FL_NORMAL_LABEL, 0, 14, 0}, // 62 -{"Playback", 0, (Fl_Callback*)cb_mnuPlayback, 0, FL_MENU_TOGGLE, FL_NORMAL_LABEL, 0, 14, 0}, // 63 +{"Rig", 0, (Fl_Callback*)cb_mnuRig, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 60 +{" ", 0, 0, 0, FL_MENU_INACTIVE, FL_NORMAL_LABEL, 0, 14, 0}, // 61 +{"Help", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 62 +{"About", 0, (Fl_Callback*)cb_mnuAbout, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 63 {0,0,0,0,0,0,0,0,0}, // 64 -{"Help", 0, 0, 0, FL_SUBMENU, FL_NORMAL_LABEL, 0, 14, 0}, // 65 -{"About", 0, (Fl_Callback*)cb_mnuAbout, 0, 0, FL_NORMAL_LABEL, 0, 14, 0}, // 66 -{0,0,0,0,0,0,0,0,0}, // 67 -{0,0,0,0,0,0,0,0,0}, // 68 +{0,0,0,0,0,0,0,0,0}, // 65 }; Fl_Menu_Bar *mnu; @@ -1029,31 +1057,27 @@ void activate_rig_menu_item(bool b) { if (b) { bSaveFreqList = true; - menu_[58].activate(); + menu_[60].activate(); } else { - menu_[58].deactivate(); + menu_[60].deactivate(); if (rigcontrol) rigcontrol->hide(); } mnu->redraw(); } -void activate_test_menu_item(bool b) -{ - if (b) - menu_[60].show(); - else - menu_[60].hide(); - mnu->redraw(); -} - void create_fl_digi_main() { int Y = 0; fl_digi_main = new Fl_Double_Window(WNOM, HNOM, "fldigi"); mnu = new Fl_Menu_Bar(0, 0, WNOM - 142, Hmenu); mnu->menu(menu_); +#ifndef USE250 + menu_[32].hide(); + menu_[33].hide(); +#endif + btnTune = new Fl_Button(WNOM - 142, 0, 60, Hmenu, "TUNE"); btnTune->type(FL_TOGGLE_BUTTON); btnTune->callback(cbTune, 0); @@ -1115,7 +1139,6 @@ void create_fl_digi_main() { int sw = 15; Fl_Group *MixerFrame = new Fl_Group(0,Y,sw, Hrcvtxt + Hxmttxt); -// valRcvMixer = new Fl_Slider(0, Y, sw, (Hrcvtxt + Hxmttxt)/2 - 15, "R"); valRcvMixer = new Fl_Slider(0, Y, sw, (Htext)/2, ""); valRcvMixer->type(FL_VERT_NICE_SLIDER); valRcvMixer->color(fl_rgb_color(0,110,30)); @@ -1123,7 +1146,6 @@ void create_fl_digi_main() { valRcvMixer->selection_color(fl_rgb_color(255,255,0)); valRcvMixer->range(1.0,0.0); valRcvMixer->callback( (Fl_Callback *)cb_RcvMixer); -// valXmtMixer = new Fl_Slider(0, Y + (Hrcvtxt + Hxmttxt)/2, sw, (Hrcvtxt + Hxmttxt)/2 - 15, "T"); valXmtMixer = new Fl_Slider(0, Y + (Htext)/2, sw, (Htext)/2, ""); valXmtMixer->type(FL_VERT_NICE_SLIDER); valXmtMixer->color(fl_rgb_color(110,0,30)); @@ -1321,22 +1343,30 @@ void put_rx_char(unsigned int data) { static bool nulinepending = false; const char **asc = ascii; - if (mailclient || mailserver) + rxmsgid = msgget( (key_t) 9876, 0666); + + if (mailclient || mailserver || rxmsgid != -1) asc = ascii2; if (data == '\r') { - ReceiveText->add(asc['\n' & 0x7F],1); + ReceiveText->add(asc['\n' & 0x7F], TextBase::RCV); nulinepending = true; } else if (nulinepending && data == '\r') { - ReceiveText->add(asc['\n' & 0x7F],1); + ReceiveText->add(asc['\n' & 0x7F], TextBase::RCV); } else if (nulinepending && data == '\n') { nulinepending = false; } else if (nulinepending && data != '\n') { - ReceiveText->add(asc[data & 0x7F], 1); + ReceiveText->add(asc[data & 0x7F], TextBase::RCV); nulinepending = false; } else { - ReceiveText->add(asc[data & 0x7F],1); + ReceiveText->add(asc[data & 0x7F], TextBase::RCV); } + if ( rxmsgid != -1) { + rxmsgst.msg_type = 1; + rxmsgst.c = data & 0x7F; + msgsnd (rxmsgid, (void *)&rxmsgst, 1, IPC_NOWAIT); + } + if (Maillogfile) Maillogfile->log_to_file(cLogfile::LOG_RX, asc[data & 0x7F]); if (logging) @@ -1480,7 +1510,7 @@ void put_echo_char(unsigned int data) { static bool nulinepending = false; const char **asc = ascii; - if (mailclient || mailserver) + if (mailclient || mailserver || arqmode) asc = ascii2; if (data == '\r' && nulinepending) // reject multiple CRs return; @@ -1488,7 +1518,7 @@ void put_echo_char(unsigned int data) if (nulinepending && data == '\n') { nulinepending = false; } - ReceiveText->add(asc[data & 0x7F],4); + ReceiveText->add(asc[data & 0x7F], TextBase::XMT); if (Maillogfile) Maillogfile->log_to_file(cLogfile::LOG_TX, asc[data & 0x7F]); if (logging) diff --git a/src/globals/globals.cxx b/src/globals/globals.cxx index 86ed8a4a..91b3a842 100644 --- a/src/globals/globals.cxx +++ b/src/globals/globals.cxx @@ -42,6 +42,8 @@ const char *mode_names[] = { "QPSK63", "PSK125", "QPSK-125", + "PSK-250", + "QPSK-250", "MT63", "FELDHELL", "FSK-HELL", diff --git a/src/ider/id.cxx b/src/ider/id.cxx index b7487209..8083e490 100644 --- a/src/ider/id.cxx +++ b/src/ider/id.cxx @@ -29,6 +29,7 @@ #undef CLAMP #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x))) +using namespace std; id::~id() { diff --git a/src/include/TextView.h b/src/include/TextView.h index 2faeaebe..799f9c2b 100644 --- a/src/include/TextView.h +++ b/src/include/TextView.h @@ -5,6 +5,9 @@ // Copyright (C) 2006 // Dave Freese, W1HKJ // +// Copyright (C) 2007 +// Stelios Bounanos, 2E0DLX +// // This file is part of fldigi. // // fldigi is free software; you can redistribute it and/or modify @@ -25,12 +28,6 @@ #ifndef _TextView_H #define _TextView_H -#include -#include -#include -#include -#include - #include "threads.h" /* fltk includes */ @@ -41,125 +38,147 @@ #include #include -using namespace std; -#if (FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 1 && \ - FL_PATCH_VERSION == 7 || FL_PATCH_VERSION == 8) && \ - !defined(NO_HSCROLLBAR_KLUDGE) -# define HSCROLLBAR_KLUDGE -#else -# warning "Not suppressing horizontal scrollbars with this version of fltk" -# undef HSCROLLBAR_KLUDGE -#endif +/// +/// The text widgets base class. +/// This class implements a basic text editing widget based on Fl_Text_Editor. +/// +class TextBase : public Fl_Text_Editor +{ +public: -class TextView : public Fl_Text_Display + /// + /// Text styles used for highlighting + /// + enum text_attr_e { + DEFAULT = 'A', ///< Default text style + RCV, ///< Received text style + XMT, ///< Transmitted text style + SKIP, ///< Skipped text style + CTRL, ///< Control character style + NSTYLES = 5 + }; + typedef enum text_attr_e text_attr_t; + +public: + TextBase(int x, int y, int w, int h, const char *l = 0); + virtual ~TextBase() { delete tbuf; delete sbuf; } + + virtual int handle(int event) { return Fl_Text_Editor::handle(event); }; + virtual void add(const char *s, text_attr_t attr = DEFAULT) { insert(s); } + void clear(void) { tbuf->text(""); sbuf->text(""); } + + void setFont(Fl_Font f, text_attr_t attr = NSTYLES); + void setFontSize(int s, text_attr_t attr = NSTYLES); + void setFontColor(Fl_Color c, text_attr_t attr = NSTYLES); + + void cursorON(void) { show_cursor(); } + virtual void resize(int X, int Y, int W, int H); + +protected: + void set_style(text_attr_t attr, Fl_Font f, int s, Fl_Color c, + int set = SET_FONT | SET_SIZE | SET_COLOR); + void readFile(void); + void saveFile(void); + char *get_word(int x, int y); + void show_context_menu(void); + virtual void menu_cb(int val) { } + int reset_wrap_col(void); + +private: + TextBase(); + TextBase(const TextBase &t); + +protected: + enum set_style_op_e { SET_FONT = 1 << 0, SET_SIZE = 1 << 1, SET_COLOR = 1 << 2 }; + enum { RESIZING = 1 << 0 }; + Fl_Text_Buffer *tbuf; ///< text buffer + Fl_Text_Buffer *sbuf; ///< style buffer + Fl_Text_Display::Style_Table_Entry styles[NSTYLES]; + Fl_Menu_Item *context_menu; + int popx, popy; + bool wrap; + int wrap_col; + int max_lines; +}; + +/// +/// A TextBase subclass to display received & transmitted text +/// +class TextView : public TextBase { public: TextView(int x, int y, int w, int h, const char *l = 0); - ~TextView(); - int handle(int event); - void add(char c, int attr = 0); - void add(const char *s, int attr = 0); - void clear(void); - - void Show(void) { show(); } - void Hide(void) { hide(); } - void setFont(Fl_Font f) { setFont(-1, f); } - void setFontSize(int s) { setFontSize(-1, s); } - void setFontColor(Fl_Color c) { setFontColor(-1, c); } - void setFont(int n, Fl_Font f); - void setFontSize(int n, int s); - void setFontColor(int n, Fl_Color c); -#ifdef HSCROLLBAR_KLUDGE - void resize(int X, int Y, int W, int H); -#endif - -public: - enum TV_ATTR {RCV, XMT}; - enum { NSTYLES = 16 }; + virtual int handle(int event); + virtual void add(char c, text_attr_t attr = DEFAULT); + virtual void add(const char *s, text_attr_t attr = DEFAULT); protected: enum { RX_MENU_QRZ_THIS, RX_MENU_CALL, RX_MENU_NAME, RX_MENU_QTH, RX_MENU_LOC, RX_MENU_RST_IN, RX_MENU_DIV, RX_MENU_CLEAR, RX_MENU_COPY, RX_MENU_SAVE, RX_MENU_WRAP }; - void draw(void); - void menu_cb(int val); - static void changed_cb(int pos, int nins, int ndel, int nsty, - const char *dtext, void *arg); - char *get_word(int x, int y); - void saveFile(void); - void clipboard_copy(void); + + virtual void menu_cb(int val); + static void changed_cb(int pos, int nins, int ndel, int nsty, + const char *dtext, void *arg); + void change_keybindings(void); private: TextView(); + TextView(const TextView &t); protected: - Fl_Text_Buffer *tbuf, *sbuf; - static Fl_Text_Display::Style_Table_Entry styles[NSTYLES]; - static Fl_Menu_Item viewmenu[]; - int popx, popy; - bool wrap; + static Fl_Menu_Item view_menu[]; }; -class TextEdit : public Fl_Text_Editor +/// +/// A TextBase subclass to display and edit text to be transmitted +/// +class TextEdit : public TextBase { public: TextEdit(int x, int y, int w, int h, const char *l = 0); - ~TextEdit(); - int handle(int event); - void add(const char *s, int attr = 1); - void clear(void); - int nextChar(void); - - void setFont(int n, Fl_Font f); - void setFontSize(int n, int s); - void setFontColor(int n, Fl_Color c); - void setFont(Fl_Font f) { setFont(-1, f); } - void setFontSize(int s) { setFontSize(-1, s); } - void setFontColor(Fl_Color c) { setFontColor(-1, c); } - void cursorON(void); -#ifdef HSCROLLBAR_KLUDGE - void resize(int X, int Y, int W, int H); -#endif - -public: - enum { NSTYLES = 16 }; + virtual int handle(int event); + virtual void add(const char *s, text_attr_t attr = DEFAULT); + void clear(void); + int nextChar(void); protected: enum { TX_MENU_TX, TX_MENU_RX, TX_MENU_MFSK16_IMG, TX_MENU_CLEAR, TX_MENU_CUT, TX_MENU_COPY, TX_MENU_PASTE, TX_MENU_READ, - TX_MENU_WRAP }; - int handle_key(int key); - void readFile(void); - static void changed_cb(int pos, int nins, int ndel, int nsty, - const char *dtext, void *arg); - void menu_cb(int val); - - void change_keybindings(void); - static int kf_default(int c, Fl_Text_Editor* e); - static int kf_enter(int c, Fl_Text_Editor* e); - static int kf_delete(int c, Fl_Text_Editor* e); - static int kf_cut(int c, Fl_Text_Editor* e); - static int kf_paste(int c, Fl_Text_Editor* e); + TX_MENU_WRAP + }; + int handle_key(int key); + virtual void menu_cb(int val); + static void changed_cb(int pos, int nins, int ndel, int nsty, + const char *dtext, void *arg); + void change_keybindings(void); + static int kf_default(int c, Fl_Text_Editor* e); + static int kf_enter(int c, Fl_Text_Editor* e); + static int kf_delete(int c, Fl_Text_Editor* e); + static int kf_cut(int c, Fl_Text_Editor* e); + static int kf_paste(int c, Fl_Text_Editor* e); private: TextEdit(); + TextEdit(const TextEdit &t); protected: - Fl_Text_Buffer *tbuf, *sbuf; - static Fl_Text_Display::Style_Table_Entry styles[NSTYLES]; - static Fl_Menu_Item editmenu[]; - bool PauseBreak; - int txpos; - static int *ptxpos; - int bkspaces; - int popx, popy; - bool wrap; + static Fl_Menu_Item edit_menu[]; + bool PauseBreak; + int txpos; + static int *ptxpos; + int bkspaces; }; +/// +/// A lock class meant to be instantiated on the stack to acquire a lock which +/// is released when the object goes out of scope. +/// The no-arg ctor calls Fl::lock(), and the Fl_Mutex ctor locks that mutex. +/// class autolock { public: @@ -167,7 +186,7 @@ public: autolock(Fl_Mutex *m_) : m(m_) { fl_lock(m); } ~autolock() { if (m) fl_unlock(m); else Fl::unlock(); } private: - autolock(const autolock &a); + autolock(const autolock &a); // no copying Fl_Mutex *m; }; diff --git a/src/include/fl_digi.h b/src/include/fl_digi.h index 2b260b58..28732f69 100644 --- a/src/include/fl_digi.h +++ b/src/include/fl_digi.h @@ -133,9 +133,11 @@ extern void initMFSK16(); extern void initPSK31(); extern void initPSK63(); extern void initPSK125(); +extern void initPSK250(); extern void initQPSK31(); extern void initQPSK63(); extern void initQPSK125(); +extern void initQPSK250(); extern void initRTTY(); extern void initOLIVIA(); extern void initDOMINOEX4(); diff --git a/src/include/globals.h b/src/include/globals.h index fcec64a0..eb89d34c 100644 --- a/src/include/globals.h +++ b/src/include/globals.h @@ -46,6 +46,8 @@ enum trx_mode { MODE_QPSK63, MODE_PSK125, MODE_QPSK125, + MODE_PSK250, + MODE_QPSK250, MODE_MT63, MODE_FELDHELL, MODE_FSKHELL, diff --git a/src/include/id.h b/src/include/id.h index a3c21696..d7c13e37 100644 --- a/src/include/id.h +++ b/src/include/id.h @@ -49,7 +49,7 @@ private: void make_pulse(); void make_tones(); void send(long int); - void sendchars(string); + void sendchars(std::string); public: id(modem *md); diff --git a/src/include/main.h b/src/include/main.h index 98b9b65f..01fe8c99 100644 --- a/src/include/main.h +++ b/src/include/main.h @@ -28,9 +28,7 @@ extern Fl_Thread trx_thread; extern state_t trx_state; extern modem *active_modem; extern string HomeDir; -extern string PskMailDir; extern string xmlfname; -extern bool gmfskmail; extern std::string scDevice; extern PTT *push2talk; @@ -40,11 +38,30 @@ extern Rig *xcvr; extern cLogfile *Maillogfile; extern cLogfile *logfile; +extern string PskMailDir; +extern bool gmfskmail; +extern bool arqmode; +extern string ArqFilename; extern bool mailclient; extern bool mailserver; extern bool pskmail_text_available; extern char pskmail_get_char(); extern void pskmail_loop(void *); +struct RXMSGSTRUC { + long int msg_type; + char c; +}; + +struct TXMSGSTRUC { + long int msg_type; + char buffer[BUFSIZ]; +}; + +extern RXMSGSTRUC rxmsgst; +extern int rxmsgid; +extern TXMSGSTRUC txmsgst; +extern int txmsgid; + #endif diff --git a/src/include/modem.h b/src/include/modem.h index cae2bcb5..f6076482 100644 --- a/src/include/modem.h +++ b/src/include/modem.h @@ -141,9 +141,11 @@ extern modem *feld_CMTmodem; extern modem *psk31_modem; extern modem *psk63_modem; extern modem *psk125_modem; +extern modem *psk250_modem; extern modem *qpsk31_modem; extern modem *qpsk63_modem; extern modem *qpsk125_modem; +extern modem *qpsk250_modem; extern modem *rtty_modem; extern modem *olivia_modem; extern modem *dominoex4_modem; diff --git a/src/include/version.h b/src/include/version.h index 8fd14907..8029899f 100644 --- a/src/include/version.h +++ b/src/include/version.h @@ -1,6 +1,6 @@ #ifndef _VERSION_H #define _VERSION_H -#define FLDIGI_VERSION "1.35R" +#define FLDIGI_VERSION "1.36b" #endif diff --git a/src/main.cxx b/src/main.cxx index c8ae34be..9aef3d73 100644 --- a/src/main.cxx +++ b/src/main.cxx @@ -20,6 +20,11 @@ // // Please report all bugs and problems to "w1hkj@w1hkj.com". // + +#include +#include +#include + #include #ifdef PORTAUDIO #include @@ -54,11 +59,10 @@ char szHomedir[120] = ""; char szPskMailDir[120] = ""; string PskMailDir; string PskMailFile; +string ArqFilename; string HomeDir; string xmlfname; -bool testmenu = false; - bool gmfskmail = false; PTT *push2talk = (PTT *)0; @@ -71,31 +75,23 @@ cLogfile *logfile = 0;; cLogfile *Maillogfile = (cLogfile *)0; FILE *server; FILE *client; -bool mailserver = false, mailclient = false; +bool mailserver = false, mailclient = false, arqmode = false; extern void start_pskmail(); -int main(int argc, char ** argv) { +RXMSGSTRUC rxmsgst; +int rxmsgid = -1; - if (argc == 2) - if (strcasecmp(argv[1], "TEST") == 0) - testmenu = true; +TXMSGSTRUC txmsgst; +int txmsgid = -1; - fl_filename_expand(szHomedir, 119, "$HOME/.fldigi/"); - if (fl_filename_isdir(szHomedir) == 0) - HomeDir = "./"; - else - HomeDir = szHomedir; - xmlfname = HomeDir; - xmlfname.append("rig.xml"); - - string lfname = HomeDir; - lfname.append("fldigi.log"); - logfile = new cLogfile(lfname); - logfile->log_to_file_start(); +void arqchecks() +{ + txmsgid = msgget( (key_t) 6789, 0666 ); + if (txmsgid != -1) + return; fl_filename_expand(szPskMailDir, 119, "$HOME/pskmail.files/"); PskMailDir = szPskMailDir; - PskMailFile = PskMailDir; PskMailFile += "PSKmailserver"; ifstream testFile; @@ -131,6 +127,24 @@ int main(int argc, char ** argv) { } } } +} + +int main(int argc, char ** argv) { + + fl_filename_expand(szHomedir, 119, "$HOME/.fldigi/"); + if (fl_filename_isdir(szHomedir) == 0) + HomeDir = "./"; + else + HomeDir = szHomedir; + xmlfname = HomeDir; + xmlfname.append("rig.xml"); + + string lfname = HomeDir; + lfname.append("fldigi.log"); + logfile = new cLogfile(lfname); + logfile->log_to_file_start(); + + arqchecks(); Fl::lock(); // start the gui thread!! Fl::visual(FL_RGB); // insure 24 bit color operation @@ -140,8 +154,6 @@ int main(int argc, char ** argv) { rigcontrol = createRigDialog(); create_fl_digi_main(); - activate_test_menu_item(testmenu); - createConfig(); macros.loadDefault(); @@ -218,15 +230,15 @@ int main(int argc, char ** argv) { std::cout << "Starting pskmail transport layer" << std::endl; fflush(stdout); string PskMailLogName = PskMailDir; - if (gmfskmail == false) - PskMailLogName += "mail-io.log"; - else + if (gmfskmail == true) PskMailLogName += "gMFSK.log"; + else + PskMailLogName += "mail-io.log"; Maillogfile = new cLogfile(PskMailLogName.c_str()); Maillogfile->log_to_file_start(); - Fl::add_timeout(10.0, pskmail_loop); } + Fl::add_timeout(1.0, pskmail_loop); return Fl::run(); } diff --git a/src/misc/pskmail.cxx b/src/misc/pskmail.cxx index 808408f9..f5801026 100644 --- a/src/misc/pskmail.cxx +++ b/src/misc/pskmail.cxx @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "main.h" #include "configuration.h" @@ -104,13 +107,50 @@ void mailZDT(string &s) #define TIMEOUT 180 // 3 minutes + +void process_msgque() +{ + int nbytes = msgrcv (txmsgid, (void *)&txmsgst, BUFSIZ, 0, IPC_NOWAIT); + if (nbytes > 0) { + mailtext = txmsgst.buffer; + parse_mailtext(); + if (mailtext.length() > 0) { + if (mailserver && progdefaults.PSKmailSweetSpot) + active_modem->set_freq(progdefaults.PSKsweetspot); + + pText = mailtext.begin(); + pskmail_text_available = true; + + active_modem->set_stopflag(false); + + fl_lock(&trx_mutex); + trx_state = STATE_TX; + fl_unlock(&trx_mutex); + wf->set_XmtRcvBtn(true); + } + } +} + void check_formail() { time_t start_time, prog_time; string sAutoFile = PskMailDir; - if (gmfskmail == false) - sAutoFile += "pskmail_out"; - else + + txmsgid = msgget( (key_t) 6789, 0666 ); + if (txmsgid != -1) { + process_msgque(); + arqmode = true; + return; + } + arqmode = false; + + if (! (mailserver || mailclient) ) + return; + + if (gmfskmail == true) sAutoFile += "gmfsk_autofile"; + else + sAutoFile += "pskmail_out"; + ifstream autofile(sAutoFile.c_str()); if(autofile) { mailtext = ""; @@ -162,6 +202,13 @@ char pskmail_get_char() if (pText != mailtext.end()) return *pText++; + rxmsgid = msgget( (key_t) 9876, 0666 ); + if ( rxmsgid != -1) { + rxmsgst.msg_type = 1; + rxmsgst.c = 0x06; // tell arq client that transmit complete + msgsnd (rxmsgid, (void *)&rxmsgst, 1, IPC_NOWAIT); + } + pskmail_text_available = false; return 0x03; // tells psk modem to return to rx } diff --git a/src/psk/psk.cxx b/src/psk/psk.cxx index 8b0f9a18..66800bc1 100644 --- a/src/psk/psk.cxx +++ b/src/psk/psk.cxx @@ -138,16 +138,16 @@ psk::psk(trx_mode pskmode) : modem() _qpsk = true; dcdbits = 128; break; -// case MODE_PSK256: -// symbollen = 32; -// _qpsk = false; -// dcdbits = 256; -// break; -// case MODE_QPSK256: -// symbollen = 32; -// _qpsk = true; -// dcdbits = 256; -// break; + case MODE_PSK250: + symbollen = 32; + _qpsk = false; + dcdbits = 256; + break; + case MODE_QPSK250: + symbollen = 32; + _qpsk = true; + dcdbits = 256; + break; default: mode = MODE_BPSK31; symbollen = 256; @@ -586,3 +586,4 @@ int psk::tx_process() } + diff --git a/src/trx/modem.cxx b/src/trx/modem.cxx index 4a50a54e..fbf0f2bc 100644 --- a/src/trx/modem.cxx +++ b/src/trx/modem.cxx @@ -16,9 +16,11 @@ modem *feld_CMTmodem = 0; modem *psk31_modem = 0; modem *psk63_modem = 0; modem *psk125_modem = 0; +modem *psk250_modem = 0; modem *qpsk31_modem = 0; modem *qpsk63_modem = 0; modem *qpsk125_modem = 0; +modem *qpsk250_modem = 0; modem *olivia_modem = 0; modem *rtty_modem = 0; modem *dominoex4_modem = 0; diff --git a/src/waterfall/waterfall.cxx b/src/waterfall/waterfall.cxx index 314bc257..44329870 100644 --- a/src/waterfall/waterfall.cxx +++ b/src/waterfall/waterfall.cxx @@ -1152,6 +1152,14 @@ int waterfall::handle(int event) { wfdisp->redrawCursor(); restoreFocus(); break; + case FL_MIDDLE_MOUSE: + if (event == FL_DRAG) + break; + bool toggle = !active_modem->get_afcOnOff(); + active_modem->set_afcOnOff(toggle); + extern Fl_Light_Button *afconoff; + afconoff->value(toggle); + break; } break; case FL_RELEASE: @@ -1168,6 +1176,12 @@ int waterfall::handle(int event) { break; } break; + case FL_MOUSEWHEEL: + if (Fl::event_dy() > 0 || Fl::event_dx() > 0) + active_modem->searchUp(); + else if (Fl::event_dy() < 0 || Fl::event_dx() < 0) + active_modem->searchDown(); + break; } return 1; } else if (wfdisp->wantcursor == true) {