diff --git a/src/dialogs/TextView.cxx b/src/dialogs/TextView.cxx index c0b157fa..778e08dc 100644 --- a/src/dialogs/TextView.cxx +++ b/src/dialogs/TextView.cxx @@ -5,9 +5,6 @@ // 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 @@ -17,16 +14,18 @@ // // fldigi is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with fldigi; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // ---------------------------------------------------------------------------- -#include -#include +#include +#include +#include +#include #include "TextView.h" #include "main.h" @@ -35,977 +34,955 @@ #include "main.h" #include "cw.h" +#include "misc.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; +//===================================================================== +// class textview +// a virtual base class for building either a text viewer or editor +// you cannot instantiate this class by itself +// only as a base for child classes +// +// text is stored in arrays +// the attribute of each character is mirrored in another array +//===================================================================== -/// 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) +textview :: textview( int x, int y, int w, int h, const char *label ) + : Fl_Widget( x, y, w, h, label ) { - tbuf = new Fl_Text_Buffer; - sbuf = new Fl_Text_Buffer; + scrollbar = new Fl_Scrollbar( x+w-20, y+2, 20, h-4 ); + scrollbar->linesize( 1 ); + scrollbar->callback( _scrollbarCB, this ); - cursor_style(Fl_Text_Editor::NORMAL_CURSOR); - buffer(tbuf); - highlight_data(sbuf, styles, NSTYLES, DEFAULT, 0, 0); + box( FL_DOWN_BOX ); + color( FL_WHITE ); - wrap_mode(wrap, wrap_col); - - // Do we want narrower scrollbars? The default width is 16. - // scrollbar_width((int)floor(scrollbar_width() * 3.0/4.0)); - - // set some defaults - 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(X, Y, W, 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); + TextFont = FL_SCREEN; + TextSize = FL_NORMAL_SIZE; + + for (int i = 0; i < 16; i++) { + TextColor[i] = FL_BLACK; } - 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 + TextColor[2] = FL_BLUE; + TextColor[3] = FL_GREEN; + TextColor[4] = FL_RED; + + wrappos = 0; + cursorON = false; + startidx = 0; + laststartpos = lastendpos = string::npos; + + clear(); } -/// Reads a file and appends its contents to the buffer. -/// -/// -void TextBase::readFile(void) +textview :: ~textview() { - char *fn = File_Select("Select ASCII text file", "*.txt", "", 0); - if (fn) { - tbuf->appendfile(fn); - insert_position(tbuf->length()); - show_insert_position(); - } + delete scrollbar; } -/// 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()); +void textview::Show() { + scrollbar->show(); + show(); } -/// 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; +void textview::Hide() { + scrollbar->hide(); + hide(); } -/// Displays the menu pointed to by \c context_menu and calls the menu function; -/// @see call_cb. -/// -void TextBase::show_context_menu(void) +int textview::handle(int event) { - 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; - } - } + return 0; } -/// 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) +void textview::setFont(Fl_Font fnt) { - 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_PUSH: - 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; - - // enable/disable menu items - if (tbuf->length()) - view_menu[RX_MENU_CLEAR].flags &= ~FL_MENU_INACTIVE; - else - view_menu[RX_MENU_CLEAR].flags |= FL_MENU_INACTIVE; - - if (tbuf->selected()) - view_menu[RX_MENU_COPY].flags &= ~FL_MENU_INACTIVE; - else - view_menu[RX_MENU_COPY].flags |= FL_MENU_INACTIVE; - if (wrap) - view_menu[RX_MENU_WRAP].flags |= FL_MENU_VALUE; - else - view_menu[RX_MENU_WRAP].flags &= ~FL_MENU_VALUE; - - 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 TextBase::handle(event); -} - -/// 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': - // 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: - char s[] = { c, '\0' }; - insert(s); - s[0] = attr; - sbuf->append(s); - break; - } - + TextFont = fnt; + redraw(); Fl::unlock(); Fl::awake(); } -/// Appends a string to the buffer -/// -/// @param s -/// @param attr -/// -void TextView::add(const char *s, text_attr_t attr) +void textview::setFontSize(int siz) { - while (*s) - add(*s++, attr); + Fl::lock(); + TextSize = siz; + redraw(); + Fl::unlock(); + Fl::awake(); } -/// The context menu handler -/// -/// @param val -/// -void TextView::menu_cb(int val) +void textview::setFontColor(Fl_Color clr) { - handle(FL_UNFOCUS); +} - switch (val) { - char *s; - case RX_MENU_QRZ_THIS: - menu_cb(RX_MENU_CALL); - extern void QRZquery(); - QRZquery(); - break; - case RX_MENU_CALL: - s = get_word(popx, popy); - inpCall->value(s); - free(s); - break; - case RX_MENU_NAME: - s = get_word(popx, popy); - inpName->value(s); - free(s); - break; - case RX_MENU_QTH: - s = get_word(popx, popy); - inpQth->value(s); - free(s); - break; - case RX_MENU_LOC: - s = get_word(popx, popy); - inpLoc->value(s); - free(s); - break; - case RX_MENU_RST_IN: - s = get_word(popx, popy); - inpRstIn->value(s); - free(s); - break; +int textview::lineCount() +{ + int cnt = 1; + size_t len = buff.length(); + if (len == 0) + cnt = 0; + else + for (size_t n = 0; n < len; n++) + if (buff[n] == '\n') cnt++; + return cnt; +} - case RX_MENU_DIV: - add("\n <<================>>\n", RCV); - break; - case RX_MENU_CLEAR: - clear(); - break; - case RX_MENU_COPY: - kf_copy(Fl::event_key(), this); - break; +size_t textview::linePosition(int linenbr) +{ + size_t len = buff.length(); + size_t pos = 0; + while (linenbr && (pos < len) ) { + if (buff[pos] == '\n') + --linenbr; + pos++; + } + if (pos == len) return 0; + return pos; +} - case RX_MENU_SAVE: - saveFile(); - break; +void textview::draw_cursor() +{ + if (cursorStyle == NONE) return; + int X = cursorX, Y = cursorY; + + typedef struct { + int x1, y1, x2, y2; + } + Segment; - case RX_MENU_WRAP: - view_menu[RX_MENU_WRAP].flags ^= FL_MENU_VALUE; - wrap_mode((wrap = !wrap), wrap_col); - show_insert_position(); - break; + Segment segs[ 5 ]; + int left, right, cursorWidth, midY; + int nSegs = 0; + int bot = Y - charheight + 1; + + /* For cursors other than the block, make them around 2/3 of a character + width, rounded to an even number of pixels so that X will draw an + odd number centered on the stem at x. */ + cursorWidth = 4; + X += cursorWidth/2; + left = X - cursorWidth/2; + right = left + cursorWidth; + + if (cursorON == false) { + fl_color(FL_WHITE); + fl_rectf ( X, Y - charheight, cursorWidth, charheight ); + return; + } + + /* Create segments and draw cursor */ + if ( cursorStyle == CARET_CURSOR ) { + midY = bot - charheight / 5; + segs[ 0 ].x1 = left; segs[ 0 ].y1 = bot; segs[ 0 ].x2 = X; segs[ 0 ].y2 = midY; + segs[ 1 ].x1 = X; segs[ 1 ].y1 = midY; segs[ 1 ].x2 = right; segs[ 1 ].y2 = bot; + segs[ 2 ].x1 = left; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = midY - 1; + segs[ 3 ].x1 = X; segs[ 3 ].y1 = midY - 1; segs[ 3 ].x2 = right; segs[ 3 ].y2 = bot; + nSegs = 4; + } else if ( cursorStyle == NORMAL_CURSOR ) { + segs[ 0 ].x1 = left; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = right; segs[ 0 ].y2 = Y; + segs[ 1 ].x1 = X; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = X; segs[ 1 ].y2 = bot; + segs[ 2 ].x1 = left; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = right; segs[ 2 ].y2 = bot; + nSegs = 3; + } else if ( cursorStyle == HEAVY_CURSOR ) { + segs[ 0 ].x1 = X - 1; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = X - 1; segs[ 0 ].y2 = bot; + segs[ 1 ].x1 = X; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = X; segs[ 1 ].y2 = bot; + segs[ 2 ].x1 = X + 1; segs[ 2 ].y1 = Y; segs[ 2 ].x2 = X + 1; segs[ 2 ].y2 = bot; + segs[ 3 ].x1 = left; segs[ 3 ].y1 = Y; segs[ 3 ].x2 = right; segs[ 3 ].y2 = Y; + segs[ 4 ].x1 = left; segs[ 4 ].y1 = bot; segs[ 4 ].x2 = right; segs[ 4 ].y2 = bot; + nSegs = 5; + } else if ( cursorStyle == DIM_CURSOR ) { + midY = Y + charheight / 2; + segs[ 0 ].x1 = X; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = X; segs[ 0 ].y2 = Y; + segs[ 1 ].x1 = X; segs[ 1 ].y1 = midY; segs[ 1 ].x2 = X; segs[ 1 ].y2 = midY; + segs[ 2 ].x1 = X; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = bot; + nSegs = 3; + } else if ( cursorStyle == BLOCK_CURSOR ) { + right = X + maxcharwidth; + segs[ 0 ].x1 = X; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = right; segs[ 0 ].y2 = Y; + segs[ 1 ].x1 = right; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = right; segs[ 1 ].y2 = bot; + segs[ 2 ].x1 = right; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = bot; + segs[ 3 ].x1 = X; segs[ 3 ].y1 = bot; segs[ 3 ].x2 = X; segs[ 3 ].y2 = Y; + nSegs = 4; + } + fl_color( FL_BLACK ); + + for ( int k = 0; k < nSegs; k++ ) { + fl_line( segs[ k ].x1, segs[ k ].y1, segs[ k ].x2, segs[ k ].y2 ); + } +} + +string textview::findtext() +{ + size_t idx, wordstart, wordend; + idx = startidx; + int xc = 0, yc = 0; + char c; + size_t len = buff.length(); + static string selword; + + + fl_font(TextFont, TextSize); + selword = ""; + if (!len) return selword; + while(idx < len) { + if (yc > h()-4) + break; + while (idx < len ) { + c = buff[idx]; + if (c == '\n') { + xc = 0; + yc += fl_height(); + break; + } + xc += (int)(fl_width(c) + 0.5); + idx++; + if ( (yc < popy && yc > popy - charheight) && + (xc >= popx) ) { + wordstart = buff.find_last_of(" \n", idx); + if (wordstart == string::npos) wordstart = 0; + wordend = buff.find_first_of(" ,\n", idx); + if (wordend == string::npos) wordend = len; + if (wordstart && wordend) + selword = buff.substr(wordstart+1, wordend - wordstart - 1); + else if (!wordstart && wordend) + selword = buff.substr(0, wordend); + return selword; + } + } + idx++; + } + return selword; +} + +void textview::drawall() +{ + int line = 0; + size_t idx = 0; + size_t startpos = string::npos; + unsigned int xpos = 0; + unsigned int ypos = 0; + unsigned int len = 0; + char c = 0; + char cstr[] = " "; + unsigned int H = h() - 4, + W = w() - 4 - 20, + X = x() + 2, + Y = y() + 2; + + fl_font(TextFont, TextSize); + charheight = fl_height(); + maxcharwidth = (int)fl_width('X'); + + draw_box(); +// resize the scrollbar to be a constant width + scrollbar->resize( x()+w()-20, y()+2, 20, h()-4 ); + scrollbar->redraw(); + + if ((len = buff.length()) == 0) { + fl_push_clip( X, Y, W, H); + cursorX = X; cursorY = Y + charheight; + draw_cursor(); + fl_pop_clip(); + return; + } + + nlines = lineCount(); + line = nlines - H / charheight - scrollbar->value(); + + xpos = 0; + ypos = charheight; + startpos = idx = linePosition(line); + + fl_push_clip( X, Y, W, H ); + + while(idx < len) { + if (ypos > H) + break; + while (idx < len ) { //&& (c = buff[idx]) != '\n') { + c = buff[idx]; + if (c == '\n') { + xpos = 0; + ypos += charheight; + idx++; + break; + } + cstr[0] = c; + fl_color (TextColor[(int)attr[idx]]); + fl_draw ( cstr, 1, X + xpos, Y + ypos ); + xpos += (int)(fl_width(c) + 0.5); + idx++; + } + } + lastendpos = idx; + laststartpos = startpos; + + cursorX = X + xpos; cursorY = Y + ypos; + draw_cursor(); + fl_pop_clip(); +} + +void textview::drawchars() +{ + int line = 0; + size_t idx = 0; + size_t startpos = string::npos; + unsigned int xpos = 0; + unsigned int ypos = 0; + unsigned int len = 0; + char c = 0; + char cstr[] = " "; + unsigned int H = h() - 4, + W = w() - 4 - 20, + X = x() + 2, + Y = y() + 2; + + if ((len = buff.length()) == 0) { + drawall(); + return; + } + + fl_font(TextFont, TextSize); + charheight = fl_height(); + maxcharwidth = (int)fl_width('X'); + + nlines = lineCount(); + line = nlines - H / charheight - scrollbar->value(); + + xpos = 0; + ypos = charheight; + startpos = idx = linePosition(line); + + if (startpos != string::npos && startpos == laststartpos) { + if (lastendpos != string::npos) + while (idx < lastendpos ) { + if (ypos > H) + break; + while (idx < lastendpos) { + c = buff[idx]; + if (c == '\n') { + xpos = 0; + ypos += charheight; + idx++; + break; + } + xpos += (int)(fl_width(c) + 0.5); + idx++; + } + } + } else { + draw_box(); + scrollbar->resize( x()+w()-20, y()+2, 20, h()-4 ); + scrollbar->redraw(); + } + + fl_push_clip( X, Y, W, H ); + + while(idx < len) { + if (ypos > H) + break; + while (idx < len ) { //&& (c = buff[idx]) != '\n') { + c = buff[idx]; + if (c == '\n') { + xpos = 0; + ypos += charheight; + idx++; + break; + } + cstr[0] = c; + fl_color (TextColor[(int)attr[idx]]); + fl_draw ( cstr, 1, X + xpos, Y + ypos ); + xpos += (int)(fl_width(c) + 0.5); + idx++; + } + } + lastendpos = idx; + laststartpos = startpos; + + cursorX = X + xpos; cursorY = Y + ypos; + draw_cursor(); + fl_pop_clip(); +} + +void textview::drawbs() +{ +} + +void textview::draw() +{ + if (damage() & FL_DAMAGE_ALL) { + drawall(); + return; + } + if (damage() & (FL_DAMAGE_ALL | 1)) { + drawchars(); + return; + } + if (damage() & (FL_DAMAGE_ALL | 2)) { + drawbs(); + return; + } + +} + +void textview::scrollbarCB() +{ + damage(FL_DAMAGE_ALL); +} + +void textview::_backspace() +{ + int c; + size_t lastcrlf = buff.rfind('\n'); + + if (lastcrlf == string::npos) lastcrlf = 0; + + if (attr[attr.length() - 1] == -1) { // soft linefeed skip over + buff.erase(buff.length()-1); + attr.erase(attr.length()-1); + wrappos = 0; + lastcrlf = buff.rfind('\n'); + if (lastcrlf == string::npos) lastcrlf = 0; + fl_font(TextFont, TextSize); + for (size_t i = lastcrlf; i < buff.length(); i++) { + wrappos += (int)(fl_width(buff[i])); + } + } + + c = buff[buff.length()-1]; + if (c == '\n') { + buff.erase(buff.length()-1); + attr.erase(attr.length()-1); + wrappos = 0; + lastcrlf = buff.rfind('\n'); + if (lastcrlf == string::npos) lastcrlf = 0; + fl_font(TextFont, TextSize); + for (size_t i = lastcrlf; i < buff.length(); i++) { + wrappos += (int)(fl_width(buff[i])); + } + } else { + buff.erase(buff.length()-1); + attr.erase(attr.length()-1); + fl_font(TextFont, TextSize); + wrappos -= (int)(fl_width(c) + 0.5); + } + damage(FL_DAMAGE_ALL); +} + +void textview::add( char c, int attribute) +{ + Fl::lock(); + if (c == 0x08) { + if (buff.length() > 0) + _backspace(); + Fl::unlock(); + Fl::awake(); + return; + } else { + if (c >= ' ' && c <= '~') { + buff += c; + attr += attribute; + fl_font(TextFont, TextSize); + charwidth = (int)(fl_width(c) + 0.5); + if (charwidth > maxcharwidth) + maxcharwidth = charwidth; + wrappos += charwidth; + } else if (c == '\n') { + buff += c; + attr += attribute; + wrappos = 0; + } + } + + int endpos = w() - 24 - maxcharwidth; + if (wrappos >= endpos) { + size_t lastspace = buff.find_last_of(' '); + if (!wordwrap + || lastspace == string::npos + || (buff.length() - lastspace) >= 10 + || (buff.length() - lastspace) == 1) { + buff += '\n'; + attr += -1; // soft linefeed attribute + wrappos = 0; + damage(1); + } else { + buff.insert(lastspace+1, 1, '\n'); + attr.insert(lastspace+1, 1, -1); + wrappos = 0; + fl_font(TextFont, TextSize); + for (size_t i = lastspace+2; i < buff.length(); i++) + wrappos += (int)(fl_width(buff[i]) + 0.5); + damage(FL_DAMAGE_ALL); + } + } else + damage(1); + Fl::unlock(); + Fl::awake(); + setScrollbar(); +} + +void textview::add( char *text, int attr ) +{ + for (unsigned int i = 0; i < strlen(text); i++) + add(text[i], attr); + return; +} + +void textview::clear() +{ + Fl::lock(); + buff.erase(); + attr.erase(); + wrappos = 0; + startidx = 0; + laststartpos = lastendpos = string::npos; + setScrollbar(); + damage(FL_DAMAGE_ALL); + Fl::unlock(); + Fl::awake(); +} + + +void textview :: setScrollbar() +{ + int lines = lineCount(); + double size; + + fl_font(TextFont, TextSize); + charheight = fl_height(); + scrollbar->range (lines, 0); + if (lines * charheight <= h()-4) + size = 1.0; + else + size = (double)(h()-4) / (double)(lines * charheight); + if (size < 0.08) size = 0.08; + scrollbar->slider_size( size ); +} + +void textview :: rebuildsoft(int W) +{ + size_t lfpos, chpos; + if (W == w()) + return; // same width no rebuild needed +// remove all soft linefeeds + while ((lfpos = attr.find(-1)) != string::npos) { + buff.erase(lfpos, 1); + attr.erase(lfpos, 1); + } + int endpos = W - 24 - maxcharwidth; + wrappos = 0; + chpos = 0; + while (chpos < buff.length()) { + if (buff[chpos] == '\n') + wrappos = 0; + else + fl_font(TextFont, TextSize); + wrappos += (int)(fl_width(buff[chpos]) + 0.5); + if (wrappos >= endpos) { + size_t lastspace = buff.find_last_of(' ', chpos); + if (!wordwrap + || lastspace == string::npos + || (chpos - lastspace) >= 10 + || (chpos == lastspace) ) { + buff.insert(chpos, 1, '\n'); + attr.insert(chpos, 1, -1); + wrappos = 0; + chpos++; + } else { + buff.insert(lastspace+1, 1, '\n'); + attr.insert(lastspace+1, 1, -1); + wrappos = 0; + chpos++; + fl_font(TextFont, TextSize); + for (size_t i = lastspace+2; i < chpos; i++) + wrappos += (int)(fl_width(buff[i]) + 0.5); + } + } + chpos++; } } -/// 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) +void textview :: resize( int x, int y, int w, int h ) { - TextView *v = (TextView *)arg; - - // 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); - - // Only scroll when the end of the text is visible. - if (v->mTopLineNum - 1 + v->mNVisibleLines >= v->mNBufferLines) - v->show_insert_position(); + rebuildsoft(w); + Fl_Widget::resize( x, y, w, h ); + setScrollbar(); } -/// 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); +//===================================================================== +// Class TextView +// Viewer for received text +// derived from Class textview +// +// redefines the handle() and menu_cb() functions specified in the +// base class. All other functions are in the base class +//===================================================================== + +void TextView::saveFile() +{ + char * fn = File_Select( + "Select ASCII text file", + "*.txt", + "", 0); + if (fn) { + ofstream out(fn); + out << buff; + out.close(); } } - -// ---------------------------------------------------------------------------- - - -Fl_Menu_Item TextEdit::edit_menu[] = { - {"&Transmit", 0, 0 }, - {"&Receive", 0, 0 }, - {"Send &image...", 0, 0, 0, FL_MENU_DIVIDER }, - {"C&lear", 0, 0, }, - {"Cu&t", 0, 0, }, - {"&Copy", 0, 0, }, - {"&Paste", 0, 0, 0, FL_MENU_DIVIDER }, - {"Append &file...", 0, 0, 0, FL_MENU_DIVIDER }, - {"Word &wrap", 0, 0, 0, FL_MENU_TOGGLE } , +Fl_Menu_Item TextView::viewmenu[] = { + {"@-9$returnarrow &QRZ this call", 0, 0 }, // 0 + {"@-9-> &Call", 0, 0 }, // 1 + {"@-9-> &Name", 0, 0 }, // 2 + {"@-9-> QT&H", 0, 0 }, // 3 + {"@-9-> &Locator", 0, 0 }, // 4 + {"@-9-> &RSTin", 0, 0, 0, FL_MENU_DIVIDER }, // 5 + {"Insert divider", 0, 0 }, // 6 + {"C&lear", 0, 0 }, // 7 +// {"&Copy", 0, 0, 0, FL_MENU_DIVIDER }, + {"Save to &file...", 0, 0, 0, FL_MENU_DIVIDER }, // 8 + {"Word &wrap", 0, 0, 0, FL_MENU_TOGGLE }, // 9 { 0 } }; -// needed by our static kf functions, which may restrict editing depending on -// the transmit cursor position -int *TextEdit::ptxpos; +int viewmenuNbr = 10; -TextEdit::TextEdit(int x, int y, int w, int h, const char *l) - : TextBase(x, y, w, h, l), PauseBreak(false), txpos(0), bkspaces(0) +TextView::TextView( int x, int y, int w, int h, const char *label ) + : textview ( x, y, w, h, label ) { - ptxpos = &txpos; - - cursor_style(Fl_Text_Display::NORMAL_CURSOR); - tbuf->add_modify_callback(changed_cb, this); - - context_menu = edit_menu; - change_keybindings(); + cursorStyle = NONE;// BLOCK_CURSOR; + cursorON = true; + wordwrap = true; + viewmenu[9].set(); } -/// 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) +void TextView::menu_cb(int val) { - if (event == FL_KEYBOARD) { - autolock txlock; - return handle_key(Fl::event_key()) ? 1 : TextBase::handle(event); +// handle(FL_UNFOCUS); + switch (val) { + case 0: // select call & do qrz query + menu_cb(1); + extern void QRZquery(); + QRZquery(); + break; + case 1: // select call + inpCall->value(findtext().c_str()); + break; + case 2: // select name + inpName->value(findtext().c_str()); + break; + case 3: // select QTH + inpQth->value(findtext().c_str()); + break; + case 4: // select locator + inpLoc->value(findtext().c_str()); + break; + case 5: // select RST rcvd + inpRstIn->value(findtext().c_str()); + break; + case 6: + add("\n <<================>>\n", RCV); + break; + case 7: + clear(); + break; +// case 8: // clipboard copy +// clipboard_copy(); +// break; + case 8: + saveFile(); + break; + + case 9: // wordwrap toggle + wordwrap = !wordwrap; + if (wordwrap) + viewmenu[9].set(); + else + viewmenu[9].clear(); + break; } - 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 - if (active_modem != mfsk16_modem) - edit_menu[TX_MENU_MFSK16_IMG].flags |= FL_MENU_INACTIVE; - else - edit_menu[TX_MENU_MFSK16_IMG].flags &= ~FL_MENU_INACTIVE; - if (tbuf->length()) - edit_menu[TX_MENU_CLEAR].flags &= ~FL_MENU_INACTIVE; - else - edit_menu[TX_MENU_CLEAR].flags |= FL_MENU_INACTIVE; - if (tbuf->selected()) { - edit_menu[TX_MENU_CUT].flags &= ~FL_MENU_INACTIVE; - edit_menu[TX_MENU_COPY].flags &= ~FL_MENU_INACTIVE; +int TextView::handle(int event) +{ +// handle events inside the textview and invoked by Right Mouse button or scrollbar + if (Fl::event_inside( this )) { + const Fl_Menu_Item * m; + int xpos = Fl::event_x(); + int ypos = Fl::event_y(); + if (xpos > x() + w() - 20) { + scrollbar->handle(event); + return 1; + } + if (event == FL_PUSH && Fl::event_button() == 3) { + popx = xpos - x(); + popy = ypos - y(); + m = viewmenu->popup(xpos, ypos, 0, 0, 0); + if (m) { + for (int i = 0; i < viewmenuNbr; i++) + if (m == &viewmenu[i]) { + menu_cb(i); + break; + } + } + return 1; + } } - else { - edit_menu[TX_MENU_CUT].flags |= FL_MENU_INACTIVE; - edit_menu[TX_MENU_COPY].flags |= FL_MENU_INACTIVE; - } - if (wrap) - edit_menu[TX_MENU_WRAP].flags |= FL_MENU_VALUE; - else - edit_menu[TX_MENU_WRAP].flags &= ~FL_MENU_VALUE; - - show_context_menu(); - return 1; + return 0; } -/// @see TextView::add -/// -/// @param s -/// @param attr -/// -void TextEdit::add(const char *s, text_attr_t attr) +//===================================================================== +// Class TextEdit +// derived from base class textview +// redfines the handle() and menu_cb() functions specified in the +// base class. All other functions are in the base class +//===================================================================== + +Fl_Menu_Item editmenu[] = { + {"clear", 0, 0, 0, FL_MENU_DIVIDER }, + {"File", 0, 0, 0, FL_MENU_DIVIDER }, + {"^t", 0, 0 }, + {"^r", 0, 0, 0, FL_MENU_DIVIDER }, + {"Picture", 0, 0 }, + {0} +}; +int editmenuNbr = 5; + +TextEdit::TextEdit( int x, int y, int w, int h, const char *label ) + : textview ( x, y, w, h, label ) { - insert(s); - - int n = strlen(s); - char a[n + 1]; - memset(a, attr, n); - a[n] = '\0'; - sbuf->replace(insert_position() - n, insert_position(), a); -} - -/// @see TextEdit::add -/// -/// @param s -/// @param attr -/// -void TextEdit::add(char c, text_attr_t attr) -{ - char s[] = { c, '\0' }; - insert(s); - - s[0] = attr; - sbuf->replace(insert_position() - 1, insert_position(), s); -} - -/// Clears the buffer. -/// Also resets the transmit position, stored backspaces and tx pause flag. -/// -void TextEdit::clear(void) -{ - autolock lock; - - TextBase::clear(); - txpos = 0; + chrptr = 0; bkspaces = 0; + textview::cursorStyle = HEAVY_CURSOR; + PauseBreak = false; + wordwrap = false; +} + + +void TextEdit::readFile() +{ + char * fn = File_Select( + "Select ASCII text file", + "*.txt", + "", 0); + if (fn) { + string inbuff; + ifstream in(fn); + if (in) { + char ch; + while (!in.eof()) { + ch = in.get(); + inbuff += ch; + } + in.close(); + add (inbuff.c_str()); + } + } +} + +void TextEdit::menu_cb(int val) +{ + if (val == 0) { + clear(); + chrptr = 0; + bkspaces = 0; + } + if (val == 1) + readFile(); + if (val == 2 && buff.empty()) { + fl_lock(&trx_mutex); + trx_state = STATE_TX; + fl_unlock(&trx_mutex); + wf->set_XmtRcvBtn(true); + } + if (val == 3) + add("^r"); + if (val == 4) + if (active_modem->get_mode() == MODE_MFSK16) + active_modem->makeTxViewer(0,0); +} + +void TextEdit::clear() { + textview::clear(); + chrptr = 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; - char c; +int TextEdit::handle_fnckey(int key) { + int b = key - FL_F - 1; + if (b > 9) + return 0; + + b += (altMacros ? 10 : 0); + if (!(macros.text[b]).empty()) + macros.execute(b); - if (bkspaces) { - --bkspaces; - c = '\b'; - } - else if (PauseBreak) { - PauseBreak = false; - c = 0x03; - } - else if (insert_position() <= txpos) // empty buffer or cursor inside transmitted text - c = '\0'; - else { - 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(); - } - } - - return c; + return 1; } -/// Handles keyboard events to override Fl_Text_Editor's handling of some -/// keystrokes. -/// -/// @param key -/// -/// @return -/// -int TextEdit::handle_key(int key) -{ - switch (key) { - case FL_Escape: +int TextEdit::handle_key() { + int key = Fl::event_key(); + + if (key == FL_Escape) { clear(); active_modem->set_stopflag(true); return 1; - break; - case 't': // transmit for C-t - if (Fl::event_state() & FL_CTRL) { - menu_cb(TX_MENU_TX); - return 1; - } - break; - case 'r':// receive for C-r - if (Fl::event_state() & FL_CTRL) { - menu_cb(TX_MENU_RX); - return 1; - } - else if (!(Fl::event_state() & (FL_META | FL_ALT))) - break; - // fall through to (un)pause for M-r or A-r - case FL_Pause: + } + + if (key == FL_Pause) { if (trx_state != STATE_TX) { fl_lock(&trx_mutex); trx_state = STATE_TX; fl_unlock(&trx_mutex); wf->set_XmtRcvBtn(true); - } - else + } else PauseBreak = true; return 1; - break; - case (FL_KP + '+'): - if (active_modem == cw_modem) - active_modem->incWPM(); + } + + if (key == (FL_KP + '+')) { + if (active_modem == cw_modem) active_modem->incWPM(); return 1; - break; - case (FL_KP + '-'): - if (active_modem == cw_modem) - active_modem->decWPM(); + } + if (key == (FL_KP + '-')) { + if (active_modem == cw_modem) active_modem->decWPM(); return 1; - break; - case (FL_KP + '*'): - if (active_modem == cw_modem) - active_modem->toggleWPM(); + } + if (key == (FL_KP + '*')) { + if (active_modem == cw_modem) active_modem->toggleWPM(); return 1; - break; - case FL_Tab: - // In non-CW modes: Tab and Ctrl-tab both pause until user moves the - // 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, 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()); - - if (!(Fl::event_state() & FL_CTRL) && active_modem == cw_modem) { - int n = tbuf->length() - txpos; - char s[n + 1]; - - memset(s, SKIP, n); - s[n] = 0; - sbuf->replace(txpos, sbuf->length(), s); - insert_position(txpos = tbuf->length()); - redraw(); + if (key >= FL_F && key <= FL_F_Last) + return handle_fnckey(key); + + if (key == FL_Tab && active_modem == cw_modem) { + while (chrptr < buff.length()) { + attr[chrptr] = 2; + chrptr++; } - // show_insert_position(); + redraw(); 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(); - return 1; - } - break; - case FL_Right: - if (Fl::event_state() & (FL_META | FL_ALT)) { - active_modem->searchUp(); - 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(); - if (txpos > 0 && txpos >= ipos) { - if (tbuf->length() >= txpos && txpos > ipos) - return 1; + } + + if (key == FL_Left) { + active_modem->searchDown(); + return 1; + } + if (key == FL_Right) { + active_modem->searchUp(); + return 1; + } + + if (key == FL_Enter) { + add('\n'); + return 1; + } + + if (key == FL_BackSpace) { + add (0x08); + if (chrptr > buff.length()) { + chrptr = buff.length(); bkspaces++; - txpos--; } - return 0; + return 1; } - // case '`': // debug - // cerr << "bkspaces=" << bkspaces << " tbuf_length=" << tbuf->length() - // << " tx_pos=" << txpos << " cursor_pos=" << insert_position() << endl; - // return 1; - // break; - default: - // insert a macro - if (key >= FL_F && key <= FL_F_Last && insert_position() >= txpos) - return handle_key_macro(key); + + const char *ch = Fl::event_text(); + add(ch); + return 1; +} - // read A/M-ddd, where d is a digit, as ascii characters (in base 10) - // and insert verbatim; e.g. M-001 inserts a - if (Fl::event_state() & (FL_META | FL_ALT) && isdigit(key) && - insert_position() >= txpos) - return handle_key_ascii(key); +int TextEdit::handle(int event) +{ +// handle events inside the textedit widget + if (event == FL_UNFOCUS && Fl::focus() != this) { + textview::cursorON = false; + redraw(); + return 1; + } + if (event == FL_FOCUS && Fl::focus() == this) { + textview::cursorON = true; + redraw(); + return 1; + } + if (event == FL_KEYBOARD) { + return handle_key(); + } + if (Fl::event_inside( this )) { + const Fl_Menu_Item * m; + int xpos = Fl::event_x(); + int ypos = Fl::event_y(); + if (xpos > x() + w() - 20) { + scrollbar->handle(event); + return 1; + } + if (event == FL_PUSH && Fl::event_button() == 3) { + popx = xpos - x(); + popy = ypos - y(); + m = editmenu->popup(xpos, ypos, 0, 0, 0); + if (m) { + for (int i = 0; i < editmenuNbr; i++) + if (m == &editmenu[i]) { + menu_cb(i); + break; + } + } + return 1; + } - // do not insert printable characters in the transmitted text - if (insert_position() < txpos) { - int d; - if (Fl::compose(d)) + switch (event) { + case FL_PUSH: + textview::cursorON = true; + redraw(); + Fl::focus(this); + return 1; + case FL_FOCUS: + textview::cursorON = true; + redraw(); + return 1; + case FL_UNFOCUS: + textview::cursorON = false; + redraw(); return 1; } - break; } - return 0; } -/// Inserts the macro for function key \c key. -/// -/// @param key An integer in the range [FL_F, FL_F_Last] -/// -/// @return 1 -/// -int TextEdit::handle_key_macro(int key) +int TextEdit::nextChar() { - key -= FL_F + 1; - if (key > 9) - return 0; - - key += (altMacros ? 10 : 0); - if (!(macros.text[key]).empty()) - macros.execute(key); - - return 1; -} - -/// Composes ascii characters and adds them to the TextEdit buffer. -/// Control characters are inserted with the CTRL style. Values larger than 127 -/// (0x7f) are ignored. We cannot really add NULs for the time being. -/// -/// @param key A digit character -/// -/// @return 1 -/// -int TextEdit::handle_key_ascii(int key) -{ - static char ascii_cnt = 0; - static unsigned ascii_chr = 0; - - key -= '0'; - ascii_cnt++; - for (int i = 0; i < 3 - ascii_cnt; i++) - key *= 10; - ascii_chr += key; - if (ascii_cnt == 3) { - if (ascii_chr <= 0x7F) - add(ascii_chr, (iscntrl(ascii_chr) ? CTRL : DEFAULT)); - ascii_cnt = ascii_chr = 0; + if (bkspaces) { + --bkspaces; + return 0x08; } - - return 1; -} - - -/// 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: - add("^r", CTRL); - break; - case TX_MENU_MFSK16_IMG: - if (active_modem->get_mode() == MODE_MFSK16) - active_modem->makeTxViewer(0, 0); - break; - - case TX_MENU_CLEAR: - clear(); - break; - case TX_MENU_CUT: - kf_cut(Fl::event_text()[0], this); - break; - case TX_MENU_COPY: - kf_copy(Fl::event_text()[0], this); - break; - case TX_MENU_PASTE: - kf_paste(Fl::event_text()[0], this); - break; - - case TX_MENU_READ: - readFile(); - break; - - case TX_MENU_WRAP: - edit_menu[TX_MENU_WRAP].flags ^= FL_MENU_VALUE; - wrap_mode((wrap = !wrap), wrap_col); - show_insert_position(); - break; + if (PauseBreak) { + PauseBreak = false; + return 0x03; } -} - -/// 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 (buff.empty()) return 0; + if (chrptr == buff.length()) return 0; + if (attr[chrptr] == -1) { + chrptr++; + if (chrptr == buff.length()) return 0; } - - 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); + Fl::lock(); + attr[chrptr] = 4; + redraw(); + Fl::unlock(); +// Fl::awake(); + return (buff[chrptr++]); } -/// 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 { - Fl_Text_Editor::Key_Func function, override; - } fbind[] = { { Fl_Text_Editor::kf_default, TextEdit::kf_default }, - { Fl_Text_Editor::kf_enter, TextEdit::kf_enter }, - { Fl_Text_Editor::kf_delete, TextEdit::kf_delete }, - { Fl_Text_Editor::kf_cut, TextEdit::kf_cut }, - { 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) - k->function = fbind[i].override; - } +void TextEdit::cursorON() +{ + textview::cursorON = true; + redraw(); } -// 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; - return e->insert_position() < *ptxpos ? 1 : Fl_Text_Editor::kf_default(c, e); -} - -int TextEdit::kf_enter(int c, Fl_Text_Editor* e) -{ - autolock txlock; - return e->insert_position() < *ptxpos ? 1 : Fl_Text_Editor::kf_enter(c, e); -} - -int TextEdit::kf_delete(int c, Fl_Text_Editor* e) -{ - autolock txlock; - - // single character - if (!e->buffer()->selected()) - return e->insert_position() < *ptxpos ? 1 : Fl_Text_Editor::kf_delete(c, e); - - // region: delete as much as we can - int start, end; - e->buffer()->selection_position(&start, &end); - if (*ptxpos >= end) - return 1; - if (*ptxpos > start) - e->buffer()->select(*ptxpos, end); - - return Fl_Text_Editor::kf_delete(c, e); -} - -int TextEdit::kf_cut(int c, Fl_Text_Editor* e) -{ - autolock txlock; - - if (e->buffer()->selected()) { - int start, end; - e->buffer()->selection_position(&start, &end); - if (*ptxpos >= end) - return 1; - if (*ptxpos > start) - e->buffer()->select(*ptxpos, end); - } - - return Fl_Text_Editor::kf_cut(c, e); -} - -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); -} diff --git a/src/dialogs/TextView_resize.cxx b/src/dialogs/TextView_resize.cxx deleted file mode 100644 index d44788d3..00000000 --- a/src/dialogs/TextView_resize.cxx +++ /dev/null @@ -1,170 +0,0 @@ -// This code is part of Fl_Text_Display::resize from fltk 1.1.7, modified -// to not hide the horizontal scrollbar when word wrapping is enabled. - -#define TOP_MARGIN 1 -#define BOTTOM_MARGIN 1 -#define LEFT_MARGIN 3 -#define RIGHT_MARGIN 3 - -#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()); - - 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; - - /* 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(); - - // try without scrollbars first - mVScrollBar->clear_visible(); - mHScrollBar->clear_visible(); - - 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); - -#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]; - } - - calc_line_starts(0, mNVisibleLines); - calc_last_char(); - - // 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); - } - } - - /* - 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; - } - } - } - - // user request to change viewport - if (mTopLineNumHint != mTopLineNum || mHorizOffsetHint != mHorizOffset) - scroll_(mTopLineNumHint, mHorizOffsetHint); - - // 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); - - // 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 4fa645db..d028b068 100644 --- a/src/dialogs/fl_digi.cxx +++ b/src/dialogs/fl_digi.cxx @@ -62,7 +62,6 @@ #include "globals.h" #include "misc.h" //#include "help.h" -#include "TextView.h" #include "Config.h" #include "configuration.h" @@ -146,10 +145,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(); @@ -1067,6 +1066,15 @@ void activate_rig_menu_item(bool b) 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"); @@ -1139,6 +1147,7 @@ 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)); @@ -1146,6 +1155,7 @@ 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)); @@ -1341,29 +1351,24 @@ void set_video(double *data, int len) void put_rx_char(unsigned int data) { - static unsigned int last = 0; + static bool nulinepending = false; const char **asc = ascii; rxmsgid = msgget( (key_t) 9876, 0666); - if (mailclient || mailserver || rxmsgid != -1) asc = ascii2; - - switch (data) { - case '\n': - if (last == '\r') - break; - // or fall through to insert \n - case '\r': - ReceiveText->add('\n', TextBase::RCV); - break; - default: - if (asc == ascii2 && iscntrl(data & 0x7F)) - ReceiveText->add(data & 0x7F, TextBase::CTRL); - else - ReceiveText->add(asc[data & 0x7F], TextBase::RCV); + if (data == '\r') { + ReceiveText->add(asc['\n' & 0x7F],1); + nulinepending = true; + } else if (nulinepending && data == '\r') { + ReceiveText->add(asc['\n' & 0x7F],1); + } else if (nulinepending && data == '\n') { + nulinepending = false; + } else if (nulinepending && data != '\n') { + ReceiveText->add(asc[data & 0x7F], 1); + nulinepending = false; + } else { + ReceiveText->add(asc[data & 0x7F],1); } - last = data; - if ( rxmsgid != -1) { rxmsgst.msg_type = 1; rxmsgst.c = data & 0x7F; @@ -1372,6 +1377,7 @@ void put_rx_char(unsigned int data) if (Maillogfile) Maillogfile->log_to_file(cLogfile::LOG_RX, asc[data & 0x7F]); + if (logging) logfile->log_to_file(cLogfile::LOG_RX, asc[data & 0x7F]); } @@ -1511,20 +1517,17 @@ char get_tx_char(void) void put_echo_char(unsigned int data) { - static unsigned int last = 0; + static bool nulinepending = false; const char **asc = ascii; - if (mailclient || mailserver || arqmode) asc = ascii2; - - if (data == '\r' && last == '\r') // reject multiple CRs + if (data == '\r' && nulinepending) // reject multiple CRs return; - if (asc == ascii2 && iscntrl(data & 0x7F)) - ReceiveText->add(data & 0x7F, TextBase::CTRL); - else - ReceiveText->add(asc[data & 0x7F], TextBase::XMT); - last = data; - + if (data == '\r') nulinepending = true; + if (nulinepending && data == '\n') { + nulinepending = false; + } + ReceiveText->add(asc[data & 0x7F],4); if (Maillogfile) Maillogfile->log_to_file(cLogfile::LOG_TX, asc[data & 0x7F]); if (logging) diff --git a/src/include/TextView.h b/src/include/TextView.h index 42f80ab4..da09f386 100644 --- a/src/include/TextView.h +++ b/src/include/TextView.h @@ -5,9 +5,6 @@ // 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 @@ -17,180 +14,154 @@ // // fldigi is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with fldigi; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // ---------------------------------------------------------------------------- #ifndef _TextView_H #define _TextView_H -#include "threads.h" +#include +#include +#include +#include +#include /* fltk includes */ #include +#include +#include +#include #include #include #include -#include -#include +using namespace std; -/// -/// The text widgets base class. -/// This class implements a basic text editing widget based on Fl_Text_Editor. -/// -class TextBase : public Fl_Text_Editor +class textview : public Fl_Widget { -public: - - /// - /// 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; +friend void processinput(void *); 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); - +enum TV_ATTR {RCV, XMT}; +enum CURSOR_TYPE {CARET_CURSOR, NORMAL_CURSOR, HEAVY_CURSOR, DIM_CURSOR, BLOCK_CURSOR, NONE}; 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 -{ + string buff; + string attr; + string inbuff; + string inattr; + int nlines; + int wrappos; + int charwidth; + int maxcharwidth; + int charheight; + int cursorX; + int cursorY; + size_t laststartpos; + size_t lastendpos; + int startidx; + int popx; + int popy; + bool cursorON; + bool wordwrap; + bool inprocess; + bool timerstarted; + + CURSOR_TYPE cursorStyle; + Fl_Font TextFont; + Fl_Color TextColor[16]; + int TextSize; public: - TextView(int x, int y, int w, int h, const char *l = 0); - 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); + textview( int x, int y, int w, int h, const char *label = 0 ); + virtual ~textview() = 0; + virtual int handle(int event); + virtual void resize( int x, int y, int w, int h ); + void draw_cursor(); + void draw(); + void drawall(); + void drawchars(); + void drawbs(); + void Show(); + void Hide(); + + virtual void add( char *text, int attr = 1 ); + virtual void add( char c, int attr = 1); + virtual void clear(); + virtual string findtext(); + + virtual void setFont(Fl_Font fnt); + virtual void setFontSize(int siz); + virtual void setFontColor(Fl_Color clr); + + inline void setTextStyle(int n, Fl_Color c ) + { if (n < 0 || n > 15) return; + TextColor[n] = c; + } 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 }; + Fl_Scrollbar *scrollbar; + Fl_Menu_Button *mpopup; - 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); + void scrollbarCB(); + inline static void _scrollbarCB( Fl_Widget* w, void* arg ) + { + ((textview*)arg)->scrollbarCB(); + } + + int lineCount(); + size_t linePosition(int linenbr); -private: - TextView(); - TextView(const TextView &t); - -protected: - static Fl_Menu_Item view_menu[]; + void _backspace(); + void setScrollbar(); + void rebuildsoft(int w); }; -/// -/// A TextBase subclass to display and edit text to be transmitted -/// -class TextEdit : public TextBase -{ +class TextView : public textview { public: - TextEdit(int x, int y, int w, int h, const char *l = 0); - - virtual int handle(int event); - virtual void add(const char *s, text_attr_t attr = DEFAULT); - virtual void add(char c, text_attr_t attr = DEFAULT); - void clear(void); - int nextChar(void); - + TextView( int x, int y, int w, int h, const char *label = 0 ); + virtual void add( char *text, int attr = 1 ) {textview::add(text, attr);}; + virtual void add( const char *text, int attr = 1) {textview::add((char*)text);}; + virtual void add( char c, int attr = 1) {textview::add(c, attr);}; + virtual void clear() {textview::clear();}; + virtual void setFont(Fl_Font fnt) { textview::setFont(fnt); } + virtual void setFontSize(int siz) { textview::setFontSize(siz); } + virtual void setFontColor(Fl_Color clr) { textview::setFontColor(clr); } 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); - int handle_key_macro(int key); - int handle_key_ascii(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: - static Fl_Menu_Item edit_menu[]; - bool PauseBreak; - int txpos; - static int *ptxpos; - int bkspaces; + static Fl_Menu_Item viewmenu[]; + int handle (int event); + void menu_cb(int val); + void saveFile(); }; -/// -/// 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 -{ + +class TextEdit : public textview { public: - autolock() : m(0) { Fl::lock(); } - autolock(Fl_Mutex *m_) : m(m_) { fl_lock(m); } - ~autolock() { if (m) fl_unlock(m); else Fl::unlock(); } + TextEdit( int x, int y, int w, int h, const char *label = 0 ); + virtual void add( char *text, int attr = 1 ) {textview::add(text);}; + virtual void add( const char *text, int attr = 1) {textview::add((char*)text);}; + virtual void add( char c, int attr = 1) {textview::add(c);}; + virtual void clear(); + int nextChar(); + void readFile(); + virtual void setFont(Fl_Font fnt) { textview::setFont(fnt); } + virtual void setFontSize(int siz) { textview::setFontSize(siz); } + virtual void setFontColor(Fl_Color clr) { textview::setFontColor(clr); } + void cursorON(); +protected: + int handle_fnckey(int key); + int handle_key(); + int handle (int event); + void menu_cb(int val); private: - autolock(const autolock &a); // no copying - Fl_Mutex *m; + unsigned int chrptr; + int bkspaces; + bool PauseBreak; }; #endif diff --git a/src/include/pskcoeff.h b/src/include/pskcoeff.h index aee70712..8639f5b9 100644 --- a/src/include/pskcoeff.h +++ b/src/include/pskcoeff.h @@ -3,8 +3,8 @@ #define FIRLEN 64 -extern double fir1c[FIRLEN]; -extern double fir2c[FIRLEN]; +extern double gmfir1c[FIRLEN]; +extern double gmfir2c[FIRLEN]; extern double syncfilt[16]; extern void raisedcosfilt(double *); diff --git a/src/include/version.h b/src/include/version.h index 9ef1b0b0..08fd1f60 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.36e" +#define FLDIGI_VERSION "1.37-exp" #endif diff --git a/src/include/waterfall.h b/src/include/waterfall.h index 6fc2482e..1b1bec1e 100644 --- a/src/include/waterfall.h +++ b/src/include/waterfall.h @@ -249,6 +249,8 @@ private: void drawgrayWF(); void drawspectrum(); void drawsignal(); +friend + void evaluate(void *); protected: public: bool wantcursor; diff --git a/src/psk/psk.cxx b/src/psk/psk.cxx index 454e716f..058408a0 100644 --- a/src/psk/psk.cxx +++ b/src/psk/psk.cxx @@ -164,10 +164,14 @@ psk::psk(trx_mode pskmode) : modem() // raisedcosfilt(fir1c); // creates fir1c - wsincfilt(fir1c, 1.0 / symbollen); // creates fir1c matched sin(x)/x filter - - wsincfilt(fir2c, 1.0 / 16.0); // creates fir2c matched sin(x)/x filter - +// wsincfilt(fir1c, 1.0 / symbollen); // creates fir1c matched sin(x)/x filter +// wsincfilt(fir2c, 1.0 / 16.0); // creates fir2c matched sin(x)/x filter +// or following uses the original gmfsk matched filters + for (int i = 0; i < 64; i++) { + fir1c[i] = gmfir1c[i]; + fir2c[i] = gmfir2c[i]; + } + fir1 = new C_FIR_filter(); fir1->init(FIRLEN, symbollen / 16, fir1c, fir1c); @@ -190,7 +194,7 @@ psk::psk(trx_mode pskmode) : modem() bandwidth = samplerate / symbollen; wfid = new id(this); - pipeptr = 0; +// pipeptr = 0; if (mailserver && progdefaults.PSKmailSweetSpot) sigsearch = 3; else @@ -330,9 +334,9 @@ void psk::afc() error -= 2 * M_PI; error *= ((samplerate / (symbollen * 2 * M_PI)/16)); if (fabs(error) < (bandwidth / 2.0)) { -// freqerr = decayavg( freqerr, error, 4);//32); -// frequency -= freqerr; - frequency -= error; + freqerr = decayavg( freqerr, error, 32); + frequency -= freqerr; +// frequency -= error; set_freq (frequency); } if (mailserver && progdefaults.PSKmailSweetSpot) @@ -379,12 +383,7 @@ void psk::rx_symbol(complex symbol) else dcd = false; } - if (squelchon == false) - set_phase(phase, true); - else if (metric > squelch) - set_phase(phase, true); - else - set_phase(M_PI, false); + set_phase(phase, dcd); if (dcd == true || squelchon == false) { if (_qpsk) @@ -430,7 +429,7 @@ void psk::update_syncscope() } - +char bitstatus[100]; int psk::rx_process(double *buf, int len) { @@ -455,28 +454,25 @@ int psk::rx_process(double *buf, int len) // final filter fir2->run( z, z ); // fir3 returns value on every sample -// Now the sync correction routine... -// save amplitude value for the sync scope int idx = (int) bitclk; double sum = 0.0; - -// scope_pipe[pipeptr % PipeLen] = syncbuf[idx] = z.mag(); - scope_pipe[pipeptr] = syncbuf[idx] = z.mag(); + syncbuf[idx] = 0.8 * syncbuf[idx] + 0.2 * z.mag(); +// syncbuf[idx] = z.mag(); for (int i = 0; i < 8; i++) sum += (syncbuf[i] - syncbuf[i+8]); bitclk -= sum / 5.0; bitclk += 1; - if (bitclk < 0) bitclk += 16; - if (bitclk >= 16) { - bitclk -= 16; + + if (bitclk < 0) bitclk += 16.0; + if (bitclk >= 16.0) { + bitclk -= 16.0; rx_symbol(z); update_syncscope(); if (dcd && afcon) afc(); } - pipeptr = (pipeptr + 1) % PipeLen; } if (!dcd && afcon) findsignal(); diff --git a/src/psk/pskcoeff.cxx b/src/psk/pskcoeff.cxx index 6b48c9ad..1a524fdd 100644 --- a/src/psk/pskcoeff.cxx +++ b/src/psk/pskcoeff.cxx @@ -10,12 +10,12 @@ #include -/* 64-tap raised-cosine FIR*/ +// 64-tap raised-cosine FIR // implements // u[n] = (1.0 - cos(2PI * n / 64))/128.0 // used in gmfsk, twpsk etc. -/* -double fir1c[64] = { + +double gmfir1c[64] = { 0.000000, //0 0.000038, //1 0.000150, //2 @@ -86,7 +86,7 @@ double fir1c[64] = { // Designed by G3PLX // -double fir2c[64] = { +double gmfir2c[64] = { 0.000625000, 0.000820912, 0.001374651, @@ -152,9 +152,11 @@ double fir2c[64] = { 0.001374651, 0.000820912 }; -*/ + // sync filter +// weighting for sync samples +// sum of all weights = 1.0 double syncfilt[16] = { -0.097545161, diff --git a/src/waterfall/waterfall.cxx b/src/waterfall/waterfall.cxx index 8a65117a..02605b10 100644 --- a/src/waterfall/waterfall.cxx +++ b/src/waterfall/waterfall.cxx @@ -375,11 +375,50 @@ void WFdisp::redrawCursor() cursormoved = true; } +bool evaluating = false; + +void evaluate(void *who) +{ + WFdisp *me = (WFdisp *)who; + + if (evaluating) return; + evaluating = true; + + static char szFrequency[14]; + + if (me->mode == me->SCOPE) + me->process_analog(me->circbuff, FFT_LEN * 2); + + me->processFFT(); + + Fl::lock(); + if (me->usebands) + me->rfc = (long long)(atof(cboBand->value()) * 1000.0); + + if (me->rfc) { + if (me->usb) + me->dfreq = me->rfc + active_modem->get_txfreq(); + else + me->dfreq = me->rfc - active_modem->get_txfreq(); + sprintf(szFrequency, "%-.3f", me->dfreq / 1000.0); + } else { + me->dfreq = active_modem->get_txfreq(); + sprintf(szFrequency, "%-.0f", me->dfreq); + } + + inpFreq->value(szFrequency); + inpFreq->redraw(); + Fl::unlock(); + put_WARNstatus(me->overload); + + Fl::awake(); + evaluating = false; +} + void WFdisp::sig_data( double *sig, int len ) { int movedbls = (FFT_LEN * 2) - len; //SCBLOCKSIZE; int movesize = movedbls * sizeof(double); double *pcircbuff1 = &circbuff[len]; - static char szFrequency[14]; //if sound card sampling rate changed reset the waterfall buffer if (srate != active_modem->get_samplerate()) { @@ -388,39 +427,14 @@ void WFdisp::sig_data( double *sig, int len ) { } else memmove(circbuff, pcircbuff1, movesize); - overload = false; int i, j; for (i = movedbls, j = 0; j < len; i++, j++) if (fabs(circbuff[i] = sig[j]) > 0.9) overload = true; - if (mode == SCOPE) - process_analog(circbuff, FFT_LEN * 2); - - processFFT(); - - Fl::lock(); - if (usebands) - rfc = (long long)(atof(cboBand->value()) * 1000.0); - - if (rfc) { - if (usb) - dfreq = rfc + active_modem->get_txfreq(); - else - dfreq = rfc - active_modem->get_txfreq(); - sprintf(szFrequency, "%-.3f", dfreq / 1000.0); - } else { - dfreq = active_modem->get_txfreq(); - sprintf(szFrequency, "%-.0f", dfreq); - } - - inpFreq->value(szFrequency); - inpFreq->redraw(); - Fl::unlock(); - put_WARNstatus(overload); - - Fl::awake(); + Fl::add_timeout(0.05, evaluate, this); + return; }