summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FL/Fl_Simple_Terminal.H199
-rw-r--r--documentation/Doxyfile.in2
-rw-r--r--documentation/src/simple-terminal-default-ansi.pngbin0 -> 31676 bytes
-rw-r--r--examples/Makefile1
-rw-r--r--examples/simple-terminal.cxx60
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/Fl_Simple_Terminal.cxx747
-rw-r--r--src/Makefile1
-rw-r--r--test/Makefile2
-rw-r--r--test/browser.cxx11
-rw-r--r--test/file_chooser.cxx16
-rw-r--r--test/input.cxx16
-rw-r--r--test/input_choice.cxx13
-rw-r--r--test/menubar.cxx18
-rw-r--r--test/native-filechooser.cxx24
-rw-r--r--test/table.cxx18
-rw-r--r--test/tree.fl43
-rw-r--r--test/unittest_simple_terminal.cxx124
-rw-r--r--test/unittests.cxx1
19 files changed, 1248 insertions, 49 deletions
diff --git a/FL/Fl_Simple_Terminal.H b/FL/Fl_Simple_Terminal.H
new file mode 100644
index 000000000..3ea3fb51d
--- /dev/null
+++ b/FL/Fl_Simple_Terminal.H
@@ -0,0 +1,199 @@
+//
+// "$Id$"
+//
+// A simple terminal widget for Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2011 by Bill Spitzak and others.
+// Copyright 2017 by Greg Ercolano.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+/* \file
+ Fl_Simple_Terminal widget . */
+
+#ifndef Fl_Simple_Terminal_H
+#define Fl_Simple_Terminal_H
+
+#include "Fl_Export.H"
+#include <FL/Fl_Text_Display.H>
+
+/**
+ This is a continuous text scroll widget for logging and debugging
+ output, much like a terminal. Includes printf() for appending messages,
+ a line limit for the screen history size, ANSI sequences to control
+ text color, font face, font weight and font size.
+
+ This is useful in place of using stdout/stderr for logging messages
+ when no terminal is available, such as when an application is invoked
+ from a desktop shortcut, dock, or file browser.
+
+ Like a regular console terminal, the vertical scrollbar 'tracks'
+ the bottom of the buffer as new output is added. If the user scrolls
+ away from the bottom, this 'tracking' feature is temporarily suspended,
+ so the user can browse the terminal history without fighting the scrollbar
+ when new text is added asynchronously. When the user returns the
+ scroller to the bottom of the display, the scrollbar's tracking resumes.
+
+ Features include:
+
+ - history_lines(int) can define a maximum size for the terminal screen history
+ - stay_at_bottom(bool) can be used to cause the terminal to keep scrolled to the bottom
+ - ansi(bool) enables ANSI sequences within the text to control text colors
+ - style_table() can be used to define custom color/font/weight/size combinations
+
+ What this widget is NOT is a full terminal emulator; it does NOT
+ handle stdio redirection, pipes, pseudo ttys, termio character cooking,
+ keyboard input processing, screen addressing, random cursor positioning,
+ curses(3) compatibility, or VT100/xterm emulation.
+
+ It is a simple text display widget that leverages the features of the
+ Fl_Text_Display base class to handle terminal-like behavior, such as
+ logging events or debug information.
+
+ Example use:
+ \code
+
+ #include <FL/Fl_Simple_Terminal.H>
+ :
+ tty = new Fl_Simple_Terminal(...);
+ tty->ansi(true); // enable use of "\033[#m"
+ :
+ tty->printf("The time is now: \033[32m%s\033[0m", date_time_str);
+
+ \endcode
+
+ Example application:
+ \dontinclude simple-terminal.cxx
+ \skip //START
+ \until //END
+
+ Style Tables For Color/Font/Fontsize Control
+ --------------------------------------------
+ Internally this widget derives from Fl_Text_Display, and therefore
+ inherits some of its idiosyncracies. In particular, when colors
+ are used, the base class's concept of a 'style table' is used.
+
+ The 'style table' is similar to a color mapped image; where each
+ pixel is a single value that is an index into a table of colors
+ to minimize per-pixel memory use.
+
+ The style table has a similar goal; since every character in the
+ terminal can potentially be a different color, instead of managing
+ several integer attribute values per-character, a single character
+ for each character is used as an index into the style table, choosing
+ one of the available color/font/weight/size values available.
+ This saves on as much as 3 to 4 times the memory use, useful when
+ there's a large amount of text.
+
+ When ansi() is set to 'true', ANSI sequences of the form "\033[#m"
+ can be used to select different colors, font faces, font weights (bold,italic..),
+ and font sizes, where '#' is the index number into the style table. Example:
+
+ \code
+ "\033[0mThis text uses the 1st entry in the style table\n"
+ "\033[1mThis text uses the 2nd entry in the style table\n"
+ "\033[2mThis text uses the 3rd entry in the style table\n"
+ etc..
+ \endcode
+
+ There is a built-in style table that provides some
+ commonly used ANSI colors for "\033[30m" through "\033[37m"
+ (blk,red,grn,yel,blu,mag,cyn,wht), and a brighter version of those
+ colors for "\033[40" through "\033[47m". See ansi(bool) for more info.
+
+ You can also supply a custom style table using
+ style_table(Style_Table_Entry*,int,int), allowing you to define
+ your own color/font/weight/size combinations. See that method's docs
+ for more info.
+
+ All style index numbers are rounded to the size of the style table
+ (via modulus) to protect the style array from overruns.
+
+*/
+class FL_EXPORT Fl_Simple_Terminal : public Fl_Text_Display {
+protected:
+ Fl_Text_Buffer *buf; // text buffer
+ Fl_Text_Buffer *sbuf; // style buffer
+
+private:
+ int history_lines_; // max lines allowed in screen history
+ bool stay_at_bottom_; // lets scroller chase last line in buffer
+ bool ansi_; // enables ANSI sequences
+ // scroll management
+ int lines; // #lines in buffer (optimization: Fl_Text_Buffer slow to calc this)
+ bool scrollaway; // true when user changed vscroll away from bottom
+ bool scrolling; // true while scroll callback active
+ // Fl_Text_Display vscrollbar's callback+data
+ Fl_Callback *orig_vscroll_cb;
+ void *orig_vscroll_data;
+ // Style table
+ const Fl_Text_Display::Style_Table_Entry *stable_; // the active style table
+ int stable_size_; // active style table size (in bytes)
+ int normal_style_index_; // "normal" style used by "\033[0m" reset sequence
+ int current_style_index_; // current style used for drawing text
+
+public:
+ Fl_Simple_Terminal(int X,int Y,int W,int H,const char *l=0);
+ ~Fl_Simple_Terminal();
+
+ // Terminal options
+ void stay_at_bottom(bool);
+ bool stay_at_bottom() const;
+ void history_lines(int);
+ int history_lines() const;
+ void ansi(bool val);
+ bool ansi() const;
+ void style_table(Fl_Text_Display::Style_Table_Entry *stable, int stable_size, int normal_style_index=0);
+ const Fl_Text_Display::Style_Table_Entry *style_table() const;
+ int style_table_size() const;
+ void normal_style_index(int);
+ int normal_style_index() const;
+ void current_style_index(int);
+ int current_style_index() const;
+
+ // Terminal text management
+ void append(const char *s, int len=-1);
+ void text(const char *s, int len=-1);
+ const char* text() const;
+ void printf(const char *fmt, ...);
+ void vprintf(const char *fmt, va_list ap);
+ void clear();
+ void remove_lines(int start, int count);
+
+private:
+ // Methods blocking public access to the subclass
+ // These are subclass methods that would give unexpected
+ // results if used. By making them private, we effectively
+ // "block" them.
+ //
+ // TODO: There are probably other Fl_Text_Display methods that
+ // need to be blocked.
+ //
+ void insert(const char*) { }
+
+public:
+ // Fltk
+ virtual void draw();
+
+protected:
+ // Internal methods
+ void enforce_stay_at_bottom();
+ void enforce_history_lines();
+ void vscroll_cb2(Fl_Widget*, void*);
+ static void vscroll_cb(Fl_Widget*, void*);
+};
+
+#endif
+
+//
+// End of "$Id$".
+//
diff --git a/documentation/Doxyfile.in b/documentation/Doxyfile.in
index 0508919b4..c0f7b26bd 100644
--- a/documentation/Doxyfile.in
+++ b/documentation/Doxyfile.in
@@ -652,7 +652,7 @@ EXCLUDE_SYMBOLS += Fl_Native_File_Chooser_FLTK_Driver
# directories that contain example code fragments that are included (see
# the \include command).
-EXAMPLE_PATH = ../test
+EXAMPLE_PATH = ../test ../examples
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
diff --git a/documentation/src/simple-terminal-default-ansi.png b/documentation/src/simple-terminal-default-ansi.png
new file mode 100644
index 000000000..1c49b3337
--- /dev/null
+++ b/documentation/src/simple-terminal-default-ansi.png
Binary files differ
diff --git a/examples/Makefile b/examples/Makefile
index 4bd44c838..166086995 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -26,6 +26,7 @@ ALL = clipboard$(EXEEXT) \
table-spreadsheet-with-keyboard-nav$(EXEEXT) \
table-with-keynav$(EXEEXT) \
tabs-simple$(EXEEXT) \
+ simple-terminal$(EXEEXT) \
textdisplay-with-colors$(EXEEXT) \
texteditor-simple$(EXEEXT) \
tree-simple$(EXEEXT) \
diff --git a/examples/simple-terminal.cxx b/examples/simple-terminal.cxx
new file mode 100644
index 000000000..5e14cf7b9
--- /dev/null
+++ b/examples/simple-terminal.cxx
@@ -0,0 +1,60 @@
+//
+// "$Id$"
+//
+// Simple Example app using Fl_Simple_Terminal. - erco 10/12/2017
+//
+// Copyright 2017 Greg Ercolano.
+// Copyright 1998-2016 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+#include <time.h> //START
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Simple_Terminal.H>
+
+#define TERMINAL_HEIGHT 120
+
+// Globals
+Fl_Double_Window *G_win = 0;
+Fl_Box *G_box = 0;
+Fl_Simple_Terminal *G_tty = 0;
+
+// Append a date/time message to the terminal every 2 seconds
+void tick_cb(void *data) {
+ time_t lt = time(NULL);
+ G_tty->printf("Timer tick: \033[32m%s\033[0m\n", ctime(&lt));
+ Fl::repeat_timeout(2.0, tick_cb, data);
+}
+
+int main(int argc, char **argv) {
+ G_win = new Fl_Double_Window(500, 200+TERMINAL_HEIGHT, "Your App");
+ G_win->begin();
+
+ G_box = new Fl_Box(0, 0, G_win->w(), 200,
+ "Your app GUI in this area.\n\n"
+ "Your app's debugging output in tty below");
+
+ // Add simple terminal to bottom of app window for scrolling history of status messages.
+ G_tty = new Fl_Simple_Terminal(0,200,G_win->w(),TERMINAL_HEIGHT);
+ G_tty->ansi(true); // enable use of "\033[32m"
+
+ G_win->end();
+ G_win->resizable(G_win);
+ G_win->show();
+ Fl::add_timeout(0.5, tick_cb);
+ return Fl::run();
+} //END
+
+//
+// End of "$Id$".
+//
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5c6532a59..ca6dc74be 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -59,6 +59,7 @@ set (CPPFILES
Fl_Scroll.cxx
Fl_Scrollbar.cxx
Fl_Shared_Image.cxx
+ Fl_Simple_Terminal.cxx
Fl_Single_Window.cxx
Fl_Slider.cxx
Fl_Spinner.cxx
diff --git a/src/Fl_Simple_Terminal.cxx b/src/Fl_Simple_Terminal.cxx
new file mode 100644
index 000000000..c10e8c832
--- /dev/null
+++ b/src/Fl_Simple_Terminal.cxx
@@ -0,0 +1,747 @@
+//
+// "$Id$"
+//
+// A simple terminal widget for Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2011 by Bill Spitzak and others.
+// Copyright 2017 by Greg Ercolano.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+#include <ctype.h> /* isdigit */
+#include <string.h> /* memset */
+#include <stdlib.h> /* strtol */
+#include <FL/Fl_Simple_Terminal.H>
+#include <FL/Fl.H>
+#include <stdarg.h>
+#include "flstring.h"
+
+#define STE_SIZE sizeof(Fl_Text_Display::Style_Table_Entry)
+
+// Default style table
+// Simple ANSI style colors with an FL_COURIER font.
+// Due to how the modulo works for 20 items, the first 10 map to 40
+// and the second 10 map to 30.
+//
+static const Fl_Text_Display::Style_Table_Entry builtin_stable[] = {
+ // FONT COLOR FONT FACE SIZE INDEX COLOR NAME ANSI ANSI MODULO INDEX
+ // ---------- --------------- ------ ------ -------------- -------- -----------------
+ { 0x80808000, FL_COURIER, 14 }, // 0 - Bright Black \033[40m 0,20,40,..
+ { 0xff000000, FL_COURIER, 14 }, // 1 - Bright Red \033[41m ^^
+ { 0x00ff0000, FL_COURIER, 14 }, // 2 - Bright Green \033[42m
+ { 0xffff0000, FL_COURIER, 14 }, // 3 - Bright Yellow \033[43m
+ { 0x0000ff00, FL_COURIER, 14 }, // 4 - Bright Blue \033[44m
+ { 0xff00ff00, FL_COURIER, 14 }, // 5 - Bright Magenta \033[45m
+ { 0x00ffff00, FL_COURIER, 14 }, // 6 - Bright Cyan \033[46m
+ { 0xffffff00, FL_COURIER, 14 }, // 7 - Bright White \033[47m
+ { 0x00000000, FL_COURIER, 14 }, // 8 - x
+ { 0x00000000, FL_COURIER, 14 }, // 9 - x
+ { 0x00000000, FL_COURIER, 14 }, // 10 - Medium Black \033[30m 10,30,50,..
+ { 0xbb000000, FL_COURIER, 14 }, // 11 - Medium Red \033[31m ^^
+ { 0x00bb0000, FL_COURIER, 14 }, // 12 - Medium Green \033[32m
+ { 0xbbbb0000, FL_COURIER, 14 }, // 13 - Medium Yellow \033[33m
+ { 0x0000cc00, FL_COURIER, 14 }, // 14 - Medium Blue \033[34m
+ { 0xbb00bb00, FL_COURIER, 14 }, // 15 - Medium Magenta \033[35m
+ { 0x00bbbb00, FL_COURIER, 14 }, // 16 - Medium Cyan \033[36m
+ { 0xbbbbbb00, FL_COURIER, 14 }, // 17 - Medium White \033[37m (also "\033[0m" reset)
+ { 0x00000000, FL_COURIER, 14 }, // 18 - x
+ { 0x00000000, FL_COURIER, 14 } // 19 - x
+};
+static const int builtin_stable_size = sizeof(builtin_stable);
+static const char builtin_normal_index = 17; // the reset style index used by \033[0m
+
+// Count how many times character 'c' appears in string 's'
+static int strcnt(const char *s, char c) {
+ int count = 0;
+ while ( *s ) { if ( *s++ == c ) ++count; }
+ return count;
+}
+
+// Vertical scrollbar callback intercept
+void Fl_Simple_Terminal::vscroll_cb2(Fl_Widget *w, void*) {
+ scrolling = 1;
+ orig_vscroll_cb(w, orig_vscroll_data);
+ scrollaway = (mVScrollBar->value() != mVScrollBar->maximum());
+ scrolling = 0;
+}
+void Fl_Simple_Terminal::vscroll_cb(Fl_Widget *w, void *data) {
+ Fl_Simple_Terminal *o = (Fl_Simple_Terminal*)data;
+ o->vscroll_cb2(w,(void*)0);
+}
+
+/**
+ Creates a new Fl_Simple_Terminal widget that can be a child of other FLTK widgets.
+*/
+Fl_Simple_Terminal::Fl_Simple_Terminal(int X,int Y,int W,int H,const char *l) : Fl_Text_Display(X,Y,W,H,l) {
+ history_lines_ = 500; // something 'reasonable'
+ stay_at_bottom_ = true;
+ ansi_ = false;
+ lines = 0; // note: lines!=mNBufferLines when lines are wrapping
+ scrollaway = false;
+ scrolling = false;
+ // These defaults similar to typical DOS/unix terminals
+ textfont(FL_COURIER);
+ color(FL_BLACK);
+ textcolor(FL_WHITE);
+ selection_color(FL_YELLOW); // default dark blue looks bad for black background
+ show_cursor(true);
+ cursor_color(FL_GREEN);
+ cursor_style(Fl_Text_Display::BLOCK_CURSOR);
+ // Setup text buffer
+ buf = new Fl_Text_Buffer();
+ buffer(buf);
+ sbuf = new Fl_Text_Buffer(); // allocate whether we use it or not
+ // XXX: We use WRAP_AT_BOUNDS to prevent the hscrollbar from /always/
+ // being present, an annoying UI bug in Fl_Text_Display.
+ wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
+ // Style table
+ stable_ = &builtin_stable[0];
+ stable_size_ = builtin_stable_size;
+ normal_style_index_ = builtin_normal_index;
+ current_style_index_ = builtin_normal_index;
+ // Intercept vertical scrolling
+ orig_vscroll_cb = mVScrollBar->callback();
+ orig_vscroll_data = mVScrollBar->user_data();
+ mVScrollBar->callback(vscroll_cb, (void*)this);
+}
+
+/**
+ Destructor for this widget; removes any internal allocations
+ for the terminal, including text buffer, style buffer, etc.
+*/
+Fl_Simple_Terminal::~Fl_Simple_Terminal() {
+ buffer(0); // disassociate buffer /before/ we delete it
+ if ( buf ) { delete buf; buf = 0; }
+ if ( sbuf ) { delete sbuf; sbuf = 0; }
+}
+
+/**
+ Gets the current value of the stay_at_bottom(bool) flag.
+
+ When true, the terminal tries to keep the scrollbar scrolled
+ to the bottom when new text is added.
+
+ \see stay_at_bottom(bool)
+*/
+bool Fl_Simple_Terminal::stay_at_bottom() const {
+ return stay_at_bottom_;
+}
+
+/**
+ Configure the terminal to remain scrolled to the bottom when possible,
+ chasing the end of the buffer whenever new text is added.
+
+ If disabled, the terminal behaves more like a text display widget;
+ the scrollbar does not chase the bottom of the buffer.
+
+ If the user scrolls away from the bottom, this 'chasing' feature is
+ temporarily disabled. This prevents the user from having to fight
+ the scrollbar chasing the end of the buffer while browsing when
+ new text is also being added asynchronously. When the user returns the
+ scroller to the bottom of the display, the chasing behavior resumes.
+
+ The default is 'true'.
+*/
+void Fl_Simple_Terminal::stay_at_bottom(bool val) {
+ if ( stay_at_bottom_ == val ) return; // no change
+ stay_at_bottom_ = val;
+ if ( stay_at_bottom_ ) enforce_stay_at_bottom();
+}
+
+/**
+ Get the maximum number of terminal history lines last set by history_lines(int).
+
+ -1 indicates an unlimited scroll history.
+
+ \see history_lines(int)
+*/
+int Fl_Simple_Terminal::history_lines() const {
+ return history_lines_;
+}
+
+/**
+ Sets the maximum number of lines for the terminal history.
+
+ The new limit value is automatically enforced on the current screen
+ history, truncating off any lines that exceed the new limit.
+
+ When a limit is set, the buffer is trimmed as new text is appended,
+ ensuring the buffer never displays more than the specified number of lines.
+
+ The default maximum is 500 lines.
+
+ \param maxlines Maximum number of lines kept on the terminal buffer history.
+ Use -1 for an unlimited scroll history.
+ A value of 0 is not recommended.
+*/
+void Fl_Simple_Terminal::history_lines(int maxlines) {
+ history_lines_ = maxlines;
+ enforce_history_lines();
+}
+
+/**
+ Get the state of the ANSI flag which enables/disables
+ the handling of ANSI sequences in text.
+
+ When true, ANSI sequences in the text stream control color, font
+ and font sizes of text (e.g. "\033[41mThis is Red\033[0m").
+ For more info, see ansi(bool).
+
+ \see ansi(bool)
+*/
+bool Fl_Simple_Terminal::ansi() const {
+ return ansi_;
+}
+
+/**
+ Enable/disable support of ANSI sequences like "\033[31m", which sets the
+ color/font/weight/size of any text that follows.
+
+ If enabled, ANSI sequences of the form "\033[#m" can be used to change
+ font color, face, and size, where '#' is an index number into the current
+ style table. These "escape sequences" are hidden from view.
+
+ If disabled, the textcolor() / textfont() / textsize() methods define
+ the color and font for all text in the terminal. ANSI sequences are not
+ handled specially, and rendered as raw text.
+
+ A built-in style table is provided, but you can configure a custom style table
+ using style_table(Style_Table_Entry*,int,int) for your own colors and fonts.
+
+ The built-in style table supports these ANSI sequences:
+
+ ANSI Sequence Color Name Font Face + Size Remarks
+ ------------- -------------- ---------------- -----------------------
+ "\033[0m" "Normal" FL_COURIER, 14 Resets to default color/font/weight/size
+ "\033[30m" Medium Black FL_COURIER, 14
+ "\033[31m" Medium Red FL_COURIER, 14
+ "\033[32m" Medium Green FL_COURIER, 14
+ "\033[33m" Medium Yellow FL_COURIER, 14
+ "\033[34m" Medium Blue FL_COURIER, 14
+ "\033[35m" Medium Magenta FL_COURIER, 14
+ "\033[36m" Medium Cyan FL_COURIER, 14
+ "\033[37m" Medium White FL_COURIER, 14 The color when "\033[0m" reset is used
+ "\033[40m" Bright Black FL_COURIER, 14
+ "\033[41m" Bright Red FL_COURIER, 14
+ "\033[42m" Bright Green FL_COURIER, 14
+ "\033[43m" Bright Yellow FL_COURIER, 14
+ "\033[44m" Bright Blue FL_COURIER, 14
+ "\033[45m" Bright Magenta FL_COURIER, 14
+ "\033[46m" Bright Cyan FL_COURIER, 14
+ "\033[47m" Bright White FL_COURIER, 14
+
+ Here's example code demonstrating the use of ANSI codes to select
+ the built-in colors, and how it looks in the terminal:
+
+ \image html simple-terminal-default-ansi.png "Fl_Simple_Terminal built-in ANSI sequences"
+ \image latex simple-terminal-default-ansi.png "Fl_Simple_Terminal built-in ANSI sequences" width=4cm
+
+ \note Changing the ansi(bool) value clears the buffer and forces a redraw().
+ \note Enabling ANSI mode overrides textfont(), textsize(), textcolor()
+ completely, which are controlled instead by current_style_index()
+ and the current style_table().
+ \see style_table(Style_Table_Entry*,int,int),
+ current_style_index(),
+ normal_style_index()
+*/
+void Fl_Simple_Terminal::ansi(bool val) {
+ ansi_ = val;
+ clear();
+ if ( ansi_ ) {
+ highlight_data(sbuf, stable_, stable_size_/STE_SIZE, 'A', 0, 0);
+ } else {
+ // XXX: highlight_data(0,0,0,'A',0,0) can crash, so to disable
+ // we use sbuf + builtin_stable but /set nitems to 0/.
+ highlight_data(sbuf, builtin_stable, 0, 'A', 0, 0);
+ }
+ redraw();
+}
+
+/**
+ Return the current style table being used.
+
+ This is the value last passed as the 1st argument to
+ style_table(Style_Table_Entry*,int,int). If no style table
+ was defined, the built-in style table is returned.
+
+ ansi(bool) must be set to 'true' for the style table to be used at all.
+
+ \see style_table(Style_Table_Entry*,int,int)
+*/
+const Fl_Text_Display::Style_Table_Entry *Fl_Simple_Terminal::style_table() const {
+ return stable_;
+}
+
+/**
+ Return the current style table's size (in bytes).
+
+ This is the value last passed as the 2nd argument to
+ style_table(Style_Table_Entry*,int,int).
+*/
+int Fl_Simple_Terminal::style_table_size() const {
+ return stable_size_;
+}
+
+/**
+ Sets the style table index used by the ANSI terminal reset
+ sequence "\033[0m", which resets the current drawing
+ color/font/weight/size to "normal".
+
+ Effective only when ansi(bool) is 'true'.
+
+ \see ansi(bool), style_table(Style_Table_Entry*,int,int)
+ \note Changing this value does *not* change the current drawing color.
+ To change that, use current_style_index(int).
+*/
+void Fl_Simple_Terminal::normal_style_index(int val) {
+ // Wrap index to ensure it's never larger than table
+ normal_style_index_ = val % (stable_size_ / STE_SIZE);
+}
+
+/**
+ Gets the style table index used by the ANSI terminal reset
+ sequence "\033[0m".
+
+ This is the value last set by normal_style_index(int), or as set by
+ the 3rd argument to style_table(Style_Table_Entry*,int,int).
+
+ \see normal_style_index(int), ansi(bool), style_table(Style_Table_Entry*,int,int)
+*/
+int Fl_Simple_Terminal::normal_style_index() const {
+ return normal_style_index_;
+}
+
+/**
+ Set the style table index used as the current drawing
+ color/font/weight/size for new text.
+
+ For example:
+ \code
+ :
+ tty->ansi(true);
+ tty->append("Some normal text.\n");
+ tty->current_style_index(2); // same as "\033[2m"
+ tty->append("This text will be green.\n");
+ tty->current_style_index(tty->normal_style_index()); // same as "\033[0m"
+ tty->append("Back to normal text.\n");
+ :
+ \endcode
+
+ This value can also be changed by an ANSI sequence like "\033[#m",
+ where # would be a new style index value. So if the application executes:
+ <tt>term->append("\033[4mTesting")</tt>, then current_style_index()
+ will be left set to 4.
+
+ The index number specified should be within the number of items in the
+ current style table. Values larger than the table will be clamped to
+ the size of the table with a modulus operation.
+
+ Effective only when ansi(bool) is 'true'.
+*/
+void Fl_Simple_Terminal::current_style_index(int val) {
+ // Wrap index to ensure it's never larger than table
+ current_style_index_ = abs(val) % (stable_size_ / STE_SIZE);
+}
+
+/**
+ Get the style table index used as the current drawing
+ color/font/weight/size for new text.
+
+ This value is also controlled by the ANSI sequence "\033[#m",
+ where # would be a new style index value. So if the application executes:
+ <tt>term->append("\033[4mTesting")</tt>, then current_style_index()
+ returns 4.
+
+ \see current_style_index(int)
+*/
+int Fl_Simple_Terminal::current_style_index() const {
+ return current_style_index_;
+}
+
+/**
+ Set a user defined style table, which controls the font colors,
+ faces, weights and sizes available for the terminal's text content.
+
+ ansi(bool) must be set to 'true' for the defined style table
+ to be used at all.
+
+ If 'table' and 'size' are 0, then the "built in" style table is used.
+ For info about the built-in colors, see ansi(bool).
+
+ Which style table entry used for drawing depends on the value last set
+ by current_style_index(), or by the ANSI sequence "\033[#m", where '#'
+ is the index into the style table, limited to the size of the table
+ via modulus.
+
+ If the index# passed via "\033[#m" is larger than the number of elements
+ in the table, the value is clamped via modulus. So for a 10 element table,
+ the following ANSI codes would all be equivalent, selecting the 5th element
+ in the table: "\033[5m", "\033[15m", "\033[25m", etc. This is because
+ 5==(15%10)==(25%10), etc.
+
+ A special exception is made for "\033[0m", which is supposed to "reset"
+ the current style table to default color/font/weight/size, as last set by
+ \p normal_style_index, or by the API method normal_style_index(int).
+
+ In cases like the built-in style table, where the 17th item is the
+ "normal" color, the 'normal_style_index' is set to 17 so that "\033[0m"
+ resets to that color, instead of the first element in the table.
+
+ If you want "\033[0m" to simply pick the first element in the table,
+ then set 'normal_style_index' to 0.
+
+ An example of defining a custom style table (white courier 14, red courier 14,
+ and white helvetica 14):
+ \code
+ int main() {
+ :
+ // Our custom style table
+ Fl_Text_Display::Style_Table_Entry mystyle[] = {
+ // Font Color Font Face Font Size Index ANSI Sequence
+ // ---------- ---------------- --------- ----- -------------
+ { FL_WHITE, FL_COURIER_BOLD, 14 }, // 0 "\033[0m" ("default")
+ { FL_RED, FL_COURIER_BOLD, 14 }, // 1 "\033[1m"
+ { FL_WHITE, FL_HELVETICA, 14 } // 2 "\033[2m"
+ };
+ // Create terminal, enable ANSI and our style table
+ tty = new Fl_Simple_Terminal(..);
+ tty->ansi(true); // enable ANSI codes
+ tty->style_table(&mystyle[0], sizeof(mystyle), 0); // use our custom style table
+ :
+ // Now write to terminal, with ANSI that uses our style table
+ tty->printf("\033[0mNormal Text\033[1mRed Courier Text\n");
+ tty->append("\033[2mWhite Helvetica\033[0mBack to normal.\n");
+ :
+ \endcode
+
+ \note Changing the style table clear()s the terminal.
+ \note You currently can't control /background/ color of text,
+ a limitation of Fl_Text_Display's current implementation.
+ \note The caller is responsible for managing the memory of the style table.
+ \note Until STR#3412 is repaired, Fl_Text_Display has scrolling bug if the
+ style table's font size != textsize()
+
+ \param stable - the style table, an array of structs of the type
+ Fl_Text_Display::Style_Table_Entry
+ \param stable_size - the sizeof() the style table (in bytes)
+ \param normal_style_index - the style table index# used when the special
+ ANSI sequence "\033[0m" is encountered.
+ Normally use 0 so that sequence selects the
+ first item in the table. Only use different
+ values if a different entry in the table
+ should be the default. This value should
+ not be larger than the number of items in
+ the table, or it will be clamped with a
+ modulus operation.
+*/
+void Fl_Simple_Terminal::style_table(Fl_Text_Display::Style_Table_Entry *stable,
+ int stable_size, int normal_style_index) {
+ // Wrap index to ensure it's never larger than table
+ normal_style_index = abs(normal_style_index) % (stable_size/STE_SIZE);
+
+ if ( stable_ == 0 ) {
+ // User wants built-in style table?
+ stable_ = &builtin_stable[0];
+ stable_size_ = builtin_stable_size;
+ normal_style_index_ = builtin_normal_index; // set the index used by \033[0m
+ current_style_index_ = builtin_normal_index; // set the index used for drawing new text
+ } else {
+ // User supplying custom style table
+ stable_ = stable;
+ stable_size_ = stable_size;
+ normal_style_index_ = normal_style_index; // set the index used by \033[0m
+ current_style_index_ = normal_style_index; // set the index used for drawing new text
+ }
+ clear(); // don't take any chances with old style info
+ highlight_data(sbuf, stable_, stable_size/STE_SIZE, 'A', 0, 0);
+}
+
+/**
+ Scroll to last line unless someone has manually scrolled
+ the vertical scrollbar away from the bottom.
+
+ This is a protected member called automatically by the public API functions.
+ Only internal methods or subclasses adjusting the internal buffer directly
+ should need to call this.
+*/
+void Fl_Simple_Terminal::enforce_stay_at_bottom() {
+ if ( stay_at_bottom_ && buffer() && !scrollaway ) {
+ scroll(mNBufferLines, 0);
+ }
+}
+
+/**
+ Enforce 'history_lines' limit on the history buffer by trimming off
+ lines from the top of the buffer.
+
+ This is a protected member called automatically by the public API functions.
+ Only internal methods or subclasses adjusting the internal buffer directly
+ should need to call this.
+*/
+void Fl_Simple_Terminal::enforce_history_lines() {
+ if ( history_lines() > -1 && lines > history_lines() ) {
+ int trimlines = lines - history_lines();
+ remove_lines(0, trimlines); // remove lines from top
+ }
+}
+
+/**
+ Appends new string 's' to terminal.
+
+ The string can contain UTF-8, crlf's, and ANSI sequences are
+ also supported when ansi(bool) is set to 'true'.
+
+ \param s string to append.
+
+ \param len optional length of string can be specified if known
+ to save the internals from having to call strlen()
+
+ \see printf(), vprintf(), text(), clear()
+*/
+void Fl_Simple_Terminal::append(const char *s, int len) {
+ // Remove ansi codes and adjust style buffer accordingly.
+ if ( ansi() ) {
+ int nstyles = stable_size_ / STE_SIZE;
+ if ( len < 0 ) len = strlen(s);
+ // New text buffer (after ansi codes parsed+removed)
+ char *ntm = (char*)malloc(len+1); // new text memory
+ char *ntp = ntm;
+ char *nsm = (char*)malloc(len+1); // new style memory
+ char *nsp = nsm;
+ // ANSI values
+ char astyle = 'A'+current_style_index_; // the running style index
+ const char *esc = 0;
+ const char *sp = s;
+ // Walk user's string looking for codes, modify new text/style text as needed
+ while ( *sp ) {
+ if ( *sp == 033 ) { // "\033.."
+ esc = sp++;
+ switch (*sp) {
+ case 0: // "\033<NUL>"? stop
+ continue;
+ case '[': { // "\033[.."
+ ++sp;
+ int vals[4], tv=0, seqdone=0;
+ while ( *sp && !seqdone && isdigit(*sp) ) { // "\033[#;#.."
+ char *newsp;
+ long a = strtol(sp, &newsp, 10);
+ sp = newsp;
+ vals[tv++] = (a<0) ? 0 : a; // prevent negative values
+ if ( tv >= 4 ) // too many #'s specified? abort sequence
+ { seqdone = 1; sp = esc+1; continue; }
+ switch(*sp) {
+ case ';': // numeric separator
+ ++sp;
+ continue;
+ case 'J': // erase in display
+ switch (vals[0]) {
+ case 0: // \033[0J -- clear to eol
+ // unsupported
+ break;
+ case 1: // \033[1J -- clear to sol
+ // unsupported
+ break;
+ case 2: // \033[2J -- clear entire screen
+ clear(); // clear text buffer
+ ntp = ntm; // clear text contents accumulated so far
+ nsp = nsm; // clear style contents ""
+ break;
+ }
+ ++sp;
+ seqdone = 1;
+ continue;
+ case 'm': // set color
+ if ( tv > 0 ) { // at least one value parsed?
+ current_style_index_ = (vals[0] == 0) // "reset"?
+ ? normal_style_index_ // use normal color for "reset"
+ : (vals[0] % nstyles); // use user's value, wrapped to ensure not larger than table
+ astyle = 'A' + current_style_index_; // convert index -> style buffer char
+ }
+ ++sp;
+ seqdone = 1;
+ continue;
+ case '\0': // EOS in middle of sequence?
+ *ntp = 0; // end of text
+ *nsp = 0; // end of style
+ seqdone = 1;
+ continue;
+ default: // un-supported cmd?
+ seqdone = 1;
+ sp = esc+1; // continue parsing just past esc
+ break;
+ } // switch
+ } // while
+ } // case '['
+ } // switch
+ } // \033
+ else {
+ // Non-ANSI character?
+ if ( *sp == '\n' ) ++lines; // keep track of #lines
+ *ntp++ = *sp++; // pass char thru
+ *nsp++ = astyle; // use current style
+ }
+ } // while
+ *ntp = 0;
+ *nsp = 0;
+ //::printf(" RESULT: ntm='%s'\n", ntm);
+ //::printf(" RESULT: nsm='%s'\n", nsm);
+ buf->append(ntm); // new text memory
+ sbuf->append(nsm); // new style memory
+ free(ntm);
+ free(nsm);
+ } else {
+ // non-ansi buffer
+ buf->append(s);
+ lines += ::strcnt(s, '\n'); // count total line feeds in string added
+ }
+ enforce_history_lines();
+ enforce_stay_at_bottom();
+}
+
+/**
+ Replaces the terminal with new text content in string 's'.
+
+ The string can contain UTF-8, crlf's, and ANSI sequences are
+ also supported when ansi(bool) is set to 'true'.
+
+ Old terminal content is completely cleared.
+
+ \param s string to append.
+
+ \param len optional length of string can be specified if known
+ to save the internals from having to call strlen()
+
+ \see append(), printf(), vprintf(), clear()
+
+*/
+void Fl_Simple_Terminal::text(const char *s, int len) {
+ clear();
+ append(s, len);
+}
+
+/**
+ Returns entire text content of the terminal as a single string.
+
+ This includes the screen history, as well as the visible
+ onscreen content.
+*/
+const char* Fl_Simple_Terminal::text() const {
+ return buf->text();
+}
+
+/**
+ Appends printf formatted messages to the terminal.
+
+ The string can contain UTF-8, crlf's, and ANSI sequences are
+ also supported when ansi(bool) is set to 'true'.
+
+ Example:
+ \code
+ #include <FL/Fl_Simple_Terminal.H>
+ int main(..) {
+ :
+ // Create a simple terminal, and append some messages to it
+ Fl_Simple_Terminal *tty = new Fl_Simple_Terminal(..);
+ :
+ // Append three lines of formatted text to the buffer
+ tty->printf("The current date is: %s.\nThe time is: %s\n", date_str, time_str);
+ tty->printf("The current PID is %ld.\n", (long)getpid());
+ :
+ \endcode
+ \note See Fl_Text_Buffer::vprintf() for limitations.
+ \param[in] fmt is a printf format string for the message text.
+*/
+void Fl_Simple_Terminal::printf(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ Fl_Simple_Terminal::vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/**
+ Appends printf formatted messages to the terminal.
+
+ Subclasses can use this to implement their own printf()
+ functionality.
+
+ The string can contain UTF-8, crlf's, and ANSI sequences are
+ also supported when ansi(bool) is set to 'true'.
+
+ \note The expanded string is currently limited to 1024 characters.
+ \param fmt is a printf format string for the message text.
+ \param ap is a va_list created by va_start() and closed with va_end(),
+ which the caller is responsible for handling.
+*/
+void Fl_Simple_Terminal::vprintf(const char *fmt, va_list ap) {
+ char buffer[1024]; // XXX: should be user configurable..
+ ::vsnprintf(buffer, 1024, fmt, ap);
+ buffer[1024-1] = 0; // XXX: MICROSOFT
+ append(buffer);
+ enforce_history_lines();
+}
+
+/**
+ Clears the terminal's screen and history. Cursor moves to top of window.
+*/
+void Fl_Simple_Terminal::clear() {
+ buf->text("");
+ sbuf->text("");
+ lines = 0;
+}
+
+/**
+ Remove the specified range of lines from the terminal, starting
+ with line 'start' and removing 'count' lines.
+
+ This method is used to enforce the history limit.
+
+ \param start -- starting line to remove
+ \param count -- number of lines to remove
+*/
+void Fl_Simple_Terminal::remove_lines(int start, int count) {
+ int spos = skip_lines(0, start, true);
+ int epos = skip_lines(spos, count, true);
+ if ( ansi() ) {
+ buf->remove(spos, epos);
+ sbuf->remove(spos, epos);
+ } else {
+ buf->remove(spos, epos);
+ }
+ lines -= count;
+ if ( lines < 0 ) lines = 0;
+}
+
+/**
+ Draws the widget, including a cursor at the end of the buffer.
+ This is needed since currently Fl_Text_Display doesn't provide
+ a reliable way to always do this.
+*/
+void Fl_Simple_Terminal::draw() {
+ // XXX: To do this right, we have to steal some of Fl_Text_Display's internal
+ // magic numbers to do it right, e.g. LEFT_MARGIN, RIGHT_MARGIN.. :/
+ //
+#define LEFT_MARGIN 3
+#define RIGHT_MARGIN 3
+ int buflen = buf->length();
+ // Force cursor to EOF so it doesn't draw at user's last left-click
+ insert_position(buflen);
+ // Let widget draw itself
+ Fl_Text_Display::draw();
+ // Now draw cursor at the end of the buffer
+ fl_push_clip(text_area.x-LEFT_MARGIN,
+ text_area.y,
+ text_area.w+LEFT_MARGIN+RIGHT_MARGIN,
+ text_area.h);
+ int X = 0, Y = 0;
+ if (position_to_xy(buflen, &X, &Y)) draw_cursor(X, Y);
+ fl_pop_clip();
+}
diff --git a/src/Makefile b/src/Makefile
index 5803413b3..b943f813c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -78,6 +78,7 @@ CPPFILES = \
Fl_Scroll.cxx \
Fl_Scrollbar.cxx \
Fl_Shared_Image.cxx \
+ Fl_Simple_Terminal.cxx \
Fl_Single_Window.cxx \
Fl_Slider.cxx \
Fl_Spinner.cxx \
diff --git a/test/Makefile b/test/Makefile
index 5a9c5d16d..803094340 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -286,7 +286,7 @@ unittests$(EXEEXT): unittests.o
unittests.o: unittests.cxx unittest_about.cxx unittest_points.cxx unittest_lines.cxx unittest_circles.cxx \
unittest_rects.cxx unittest_text.cxx unittest_symbol.cxx unittest_viewport.cxx unittest_images.cxx \
- unittest_schemes.cxx
+ unittest_schemes.cxx unittest_simple_terminal.cxx
adjuster$(EXEEXT): adjuster.o
diff --git a/test/browser.cxx b/test/browser.cxx
index 8a303ce4b..48edd302b 100644
--- a/test/browser.cxx
+++ b/test/browser.cxx
@@ -58,6 +58,7 @@ That was a blank line above this.
#include <FL/Fl_Button.H>
#include <FL/Fl_Int_Input.H>
#include <FL/Fl_Choice.H>
+#include <FL/Fl_Simple_Terminal.H>
#include <FL/fl_ask.H>
#include <stdio.h>
#include <string.h>
@@ -74,6 +75,7 @@ Fl_Button *top,
Fl_Choice *btype;
Fl_Choice *wtype;
Fl_Int_Input *field;
+Fl_Simple_Terminal *tty = 0;
typedef struct {
const char *name;
@@ -95,7 +97,7 @@ WhenItem when_items[] = {
};
void b_cb(Fl_Widget* o, void*) {
- printf("callback, selection = %d, event_clicks = %d\n",
+ tty->printf("callback, selection = \033[31m%d\033[0m, event_clicks = \033[32m%d\033[0m\n",
((Fl_Browser*)o)->value(), Fl::event_clicks());
}
@@ -154,7 +156,7 @@ int main(int argc, char **argv) {
int i;
if (!Fl::args(argc,argv,i)) Fl::fatal(Fl::help);
const char* fname = (i < argc) ? argv[i] : "browser.cxx";
- Fl_Double_Window window(720,400,fname);
+ Fl_Double_Window window(720,520,fname);
browser = new Fl_Select_Browser(0,0,window.w(),350,0);
browser->type(FL_MULTI_BROWSER);
//browser->type(FL_HOLD_BROWSER);
@@ -232,6 +234,11 @@ int main(int argc, char **argv) {
wtype->callback(wtype_cb);
wtype->value(4); // FL_WHEN_RELEASE_ALWAYS is Fl_Browser's default
+ // Small terminal window for callback messages
+ tty = new Fl_Simple_Terminal(0,400,720,120);
+ tty->history_lines(50);
+ tty->ansi(true);
+
window.resizable(browser);
window.show(argc,argv);
return Fl::run();
diff --git a/test/file_chooser.cxx b/test/file_chooser.cxx
index 12f43f0c1..1a6b0da36 100644
--- a/test/file_chooser.cxx
+++ b/test/file_chooser.cxx
@@ -41,8 +41,12 @@
#include <FL/Fl_PNM_Image.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Simple_Terminal.H>
#include <string.h>
+#define TERMINAL_HEIGHT 120
+#define TERMINAL_GREEN "\033[32m"
+#define TERMINAL_NORMAL "\033[0m"
//
// Globals...
@@ -52,6 +56,7 @@ Fl_Input *filter;
Fl_File_Browser *files;
Fl_File_Chooser *fc;
Fl_Shared_Image *image = 0;
+Fl_Simple_Terminal *tty = 0;
// for choosing extra groups
Fl_Choice *ch_extra;
@@ -101,7 +106,10 @@ main(int argc, // I - Number of command-line arguments
Fl_Shared_Image::add_handler(ps_check);
// Make the main window...
- window = new Fl_Double_Window(400, 215, "File Chooser Test");
+ window = new Fl_Double_Window(400, 215+TERMINAL_HEIGHT, "File Chooser Test");
+
+ tty = new Fl_Simple_Terminal(0,215,window->w(),TERMINAL_HEIGHT);
+ tty->ansi(true);
filter = new Fl_Input(50, 10, 315, 25, "Filter:");
// Process standard arguments and find filter argument if present
@@ -221,11 +229,11 @@ fc_callback(Fl_File_Chooser *fc, // I - File chooser
const char *filename; // Current filename
- printf("fc_callback(fc = %p, data = %p)\n", fc, data);
+ tty->printf("fc_callback(fc = %p, data = %p)\n", fc, data);
filename = fc->value();
- printf(" filename = \"%s\"\n", filename ? filename : "(null)");
+ tty->printf(" filename = \"%s\"\n", filename ? filename : "(null)");
}
@@ -365,6 +373,8 @@ show_callback(void)
if (!fc->value(i))
break;
+ tty->printf("%d/%d) %sPicked: '%s'%s\n", i, count, TERMINAL_GREEN, fc->value(i), TERMINAL_NORMAL);
+
fl_filename_relative(relative, sizeof(relative), fc->value(i));
files->add(relative,
diff --git a/test/input.cxx b/test/input.cxx
index 33f8abc81..3b8db7380 100644
--- a/test/input.cxx
+++ b/test/input.cxx
@@ -28,9 +28,15 @@
#include <FL/Fl_Toggle_Button.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Color_Chooser.H>
+#include <FL/Fl_Simple_Terminal.H>
+
+#define TERMINAL_HEIGHT 120
+
+// Globals
+Fl_Simple_Terminal *G_tty = 0;
void cb(Fl_Widget *ob) {
- printf("Callback for %s '%s'\n",ob->label(),((Fl_Input*)ob)->value());
+ G_tty->printf("Callback for %s '%s'\n",ob->label(),((Fl_Input*)ob)->value());
}
int when = 0;
@@ -43,11 +49,11 @@ void toggle_cb(Fl_Widget *o, long v) {
void test(Fl_Input *i) {
if (i->changed()) {
- i->clear_changed(); printf("%s '%s'\n",i->label(),i->value());
+ i->clear_changed(); G_tty->printf("%s '%s'\n",i->label(),i->value());
char utf8buf[10];
int last = fl_utf8encode(i->index(i->position()), utf8buf);
utf8buf[last] = 0;
- printf("Symbol at cursor position: %s\n", utf8buf);
+ G_tty->printf("Symbol at cursor position: %s\n", utf8buf);
}
}
@@ -86,7 +92,8 @@ int main(int argc, char **argv) {
// calling fl_contrast below will return good results
Fl::args(argc, argv);
Fl::get_system_colors();
- Fl_Window *window = new Fl_Window(400,420);
+ Fl_Window *window = new Fl_Window(400,420+TERMINAL_HEIGHT);
+ G_tty = new Fl_Simple_Terminal(0,420,window->w(),TERMINAL_HEIGHT);
int y = 10;
input[0] = new Fl_Input(70,y,300,30,"Normal:"); y += 35;
@@ -153,6 +160,7 @@ int main(int argc, char **argv) {
b->tooltip("Color of the text");
window->end();
+ window->resizable(G_tty);
window->show(argc,argv);
return Fl::run();
}
diff --git a/test/input_choice.cxx b/test/input_choice.cxx
index b38441090..4e4d16109 100644
--- a/test/input_choice.cxx
+++ b/test/input_choice.cxx
@@ -20,6 +20,12 @@
#include <FL/Fl_Button.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Input_Choice.H>
+#include <FL/Fl_Simple_Terminal.H>
+
+#define TERMINAL_HEIGHT 120
+
+// Globals
+Fl_Simple_Terminal *G_tty = 0;
void buttcb(Fl_Widget*,void*data) {
Fl_Input_Choice *in=(Fl_Input_Choice *)data;
@@ -28,18 +34,19 @@ void buttcb(Fl_Widget*,void*data) {
if ( flag ) in->activate();
else in->deactivate();
if (in->changed()) {
- printf("Callback: changed() is set\n");
+ G_tty->printf("Callback: changed() is set\n");
in->clear_changed();
}
}
void input_choice_cb(Fl_Widget*,void*data) {
Fl_Input_Choice *in=(Fl_Input_Choice *)data;
- fprintf(stderr, "Value='%s'\n", (const char*)in->value());
+ G_tty->printf("Value='%s'\n", (const char*)in->value());
}
int main(int argc, char **argv) {
- Fl_Double_Window win(300, 200);
+ Fl_Double_Window win(300, 200+TERMINAL_HEIGHT);
+ G_tty = new Fl_Simple_Terminal(0,200,win.w(),TERMINAL_HEIGHT);
Fl_Input_Choice in(40,40,100,28,"Test");
in.callback(input_choice_cb, (void*)&in);
diff --git a/test/menubar.cxx b/test/menubar.cxx
index 67312bcd6..6062db889 100644
--- a/test/menubar.cxx
+++ b/test/menubar.cxx
@@ -30,9 +30,15 @@
#include <stdlib.h>
#include "../src/flstring.h"
#include <FL/fl_draw.H>
+#include <FL/Fl_Simple_Terminal.H>
+
+#define TERMINAL_HEIGHT 120
+
+// Globals
+Fl_Simple_Terminal *G_tty = 0;
void window_cb(Fl_Widget* w, void*) {
- puts("window callback called");
+ puts("window callback called"); // end of program, so stdout instead of G_tty
((Fl_Double_Window *)w)->hide();
}
@@ -40,11 +46,11 @@ void test_cb(Fl_Widget* w, void*) {
Fl_Menu_* mw = (Fl_Menu_*)w;
const Fl_Menu_Item* m = mw->mvalue();
if (!m)
- printf("NULL\n");
+ G_tty->printf("NULL\n");
else if (m->shortcut())
- printf("%s - %s\n", m->label(), fl_shortcut_label(m->shortcut()));
+ G_tty->printf("%s - %s\n", m->label(), fl_shortcut_label(m->shortcut()));
else
- printf("%s\n", m->label());
+ G_tty->printf("%s\n", m->label());
}
void quit_cb(Fl_Widget*, void*) {exit(0);}
@@ -211,7 +217,9 @@ int main(int argc, char **argv) {
sprintf(buf,"item %d",i);
hugemenu[i].text = strdup(buf);
}
- Fl_Double_Window window(WIDTH,400);
+ Fl_Double_Window window(WIDTH,400+TERMINAL_HEIGHT);
+ G_tty = new Fl_Simple_Terminal(0,400,WIDTH,TERMINAL_HEIGHT);
+
window.callback(window_cb);
Fl_Menu_Bar menubar(0,0,WIDTH,30); menubar.menu(menutable);
menubar.callback(test_cb);
diff --git a/test/native-filechooser.cxx b/test/native-filechooser.cxx
index 81086047f..834ac3d44 100644
--- a/test/native-filechooser.cxx
+++ b/test/native-filechooser.cxx
@@ -27,10 +27,14 @@
#include <FL/Fl_Box.H>
#include <FL/Fl_Native_File_Chooser.H>
#include <FL/Fl_Help_View.H>
+#include <FL/Fl_Simple_Terminal.H>
+
+#define TERMINAL_HEIGHT 120
// GLOBALS
Fl_Input *G_filename = NULL;
Fl_Multiline_Input *G_filter = NULL;
+Fl_Simple_Terminal *G_tty = NULL;
void PickFile_CB(Fl_Widget*, void*) {
// Create native chooser
@@ -41,13 +45,15 @@ void PickFile_CB(Fl_Widget*, void*) {
native.preset_file(G_filename->value());
// Show native chooser
switch ( native.show() ) {
- case -1: fprintf(stderr, "ERROR: %s\n", native.errmsg()); break; // ERROR
- case 1: fprintf(stderr, "*** CANCEL\n"); fl_beep(); break; // CANCEL
+ case -1: G_tty->printf("ERROR: %s\n", native.errmsg()); break; // ERROR
+ case 1: G_tty->printf("*** CANCEL\n"); fl_beep(); break; // CANCEL
default: // PICKED FILE
if ( native.filename() ) {
G_filename->value(native.filename());
+ G_tty->printf("filename='%s'\n", native.filename());
} else {
G_filename->value("NULL");
+ G_tty->printf("filename='(null)'\n");
}
break;
}
@@ -61,13 +67,15 @@ void PickDir_CB(Fl_Widget*, void*) {
native.type(Fl_Native_File_Chooser::BROWSE_DIRECTORY);
// Show native chooser
switch ( native.show() ) {
- case -1: fprintf(stderr, "ERROR: %s\n", native.errmsg()); break; // ERROR
- case 1: fprintf(stderr, "*** CANCEL\n"); fl_beep(); break; // CANCEL
+ case -1: G_tty->printf("ERROR: %s\n", native.errmsg()); break; // ERROR
+ case 1: G_tty->printf("*** CANCEL\n"); fl_beep(); break; // CANCEL
default: // PICKED DIR
if ( native.filename() ) {
G_filename->value(native.filename());
+ G_tty->printf("dirname='%s'\n", native.filename());
} else {
G_filename->value("NULL");
+ G_tty->printf("dirname='(null)'\n");
}
break;
}
@@ -91,10 +99,12 @@ int main(int argc, char **argv) {
argn++;
#endif
- Fl_Window *win = new Fl_Window(640, 400, "Native File Chooser Test");
+ Fl_Window *win = new Fl_Window(640, 400+TERMINAL_HEIGHT, "Native File Chooser Test");
win->size_range(win->w(), win->h(), 0, 0);
win->begin();
{
+ G_tty = new Fl_Simple_Terminal(0,400,win->w(),TERMINAL_HEIGHT);
+
int x = 80, y = 10;
G_filename = new Fl_Input(x, y, win->w()-80-10, 25, "Filename");
G_filename->value(argc <= argn ? "." : argv[argn]);
@@ -131,10 +141,10 @@ int main(int argc, char **argv) {
" Apps<font color=#55f>&lt;Ctrl-I&gt;</font>*.app\n"
"</pre>\n");
- Fl_Button *but = new Fl_Button(win->w()-x-10, win->h()-25-10, 80, 25, "Pick File");
+ Fl_Button *but = new Fl_Button(win->w()-x-10, win->h()-TERMINAL_HEIGHT-25-10, 80, 25, "Pick File");
but->callback(PickFile_CB);
- Fl_Button *butdir = new Fl_Button(but->x()-x-10, win->h()-25-10, 80, 25, "Pick Dir");
+ Fl_Button *butdir = new Fl_Button(but->x()-x-10, win->h()-TERMINAL_HEIGHT-25-10, 80, 25, "Pick Dir");
butdir->callback(PickDir_CB);
win->resizable(G_filter);
diff --git a/test/table.cxx b/test/table.cxx
index c36713ef5..2890895f8 100644
--- a/test/table.cxx
+++ b/test/table.cxx
@@ -16,6 +16,12 @@
#include <FL/fl_draw.H>
#include <FL/fl_ask.H>
#include <FL/Fl_Table_Row.H>
+#include <FL/Fl_Simple_Terminal.H>
+
+#define TERMINAL_HEIGHT 120
+
+// Globals
+Fl_Simple_Terminal *G_tty = 0;
// Simple demonstration class to derive from Fl_Table_Row
class DemoTable : public Fl_Table_Row
@@ -99,7 +105,7 @@ void DemoTable::draw_cell(TableContext context,
}
case CONTEXT_TABLE:
- fprintf(stderr, "TABLE CONTEXT CALLED\n");
+ G_tty->printf("TABLE CONTEXT CALLED\n");
return;
case CONTEXT_ENDPAGE:
@@ -121,9 +127,9 @@ void DemoTable::event_callback2()
int R = callback_row(),
C = callback_col();
TableContext context = callback_context();
- printf("'%s' callback: ", (label() ? label() : "?"));
- printf("Row=%d Col=%d Context=%d Event=%d InteractiveResize? %d\n",
- R, C, (int)context, (int)Fl::event(), (int)is_interactive_resize());
+ const char *name = label() ? label() : "?";
+ G_tty->printf("'%s' callback: Row=%d Col=%d Context=%d Event=%d InteractiveResize? %d\n",
+ name, R, C, (int)context, (int)Fl::event(), (int)is_interactive_resize());
}
// GLOBAL TABLE WIDGET
@@ -339,7 +345,9 @@ Fl_Menu_Item type_choices[] = {
int main(int argc, char **argv)
{
- Fl_Window win(900, 730);
+ Fl_Window win(900, 730+TERMINAL_HEIGHT);
+
+ G_tty = new Fl_Simple_Terminal(0,730,win.w(),TERMINAL_HEIGHT);
G_table = new DemoTable(20, 20, 860, 460, "Demo");
G_table->selection_color(FL_YELLOW);
diff --git a/test/tree.fl b/test/tree.fl
index fcd342ef8..919393f73 100644
--- a/test/tree.fl
+++ b/test/tree.fl
@@ -38,6 +38,9 @@ decl {\#include <FL/Fl_Color_Chooser.H>} {public global
decl {\#include <FL/Fl_Text_Display.H>} {public global
}
+decl {\#include <FL/Fl_Simple_Terminal.H>} {public global
+}
+
decl {int G_cb_counter = 0;} {
comment {// Global callback event counter} private local
}
@@ -74,7 +77,7 @@ height += 10;
if ( height > 50 ) height = 20;
cw->resize(cw->x(), cw->y(), cw->w(), height);
tree->redraw(); // adjusted height
-fprintf(stderr, "'%s' button pushed (height=%d)\\n", w->label(), height);} {}
+tty->printf("'%s' button pushed (height=%d)\\n", w->label(), height);} {}
}
Function {AssignUserIcons()} {
@@ -144,8 +147,7 @@ for ( Fl_Tree_Item *item = tree->first(); item; item=item->next()) {
item->userdeicon(0);
}
}
-tree->redraw();} {selected
- }
+tree->redraw();} {}
}
Function {RebuildTree()} {
@@ -360,7 +362,7 @@ Function {} {open
} {
Fl_Window window {
label tree open
- xywh {600 253 1045 580} type Double hide
+ xywh {539 25 1045 730} type Double visible
} {
Fl_Group tree {
label Tree
@@ -368,7 +370,7 @@ Function {} {open
callback {G_cb_counter++; // Increment callback counter whenever tree callback is invoked
Fl_Tree_Item *item = tree->callback_item();
if ( item ) {
- fprintf(stderr, "TREE CALLBACK: label='%s' userdata=%ld reason=%s, changed=%d",
+ tty->printf("TREE CALLBACK: label='%s' userdata=%ld reason=%s, changed=%d",
item->label(),
(long)(fl_intptr_t)tree->user_data(),
reason_as_name(tree->callback_reason()),
@@ -377,12 +379,12 @@ if ( item ) {
// Should only happen if reason==FL_TREE_REASON_RESELECTED.
//
if ( Fl::event_clicks() > 0 ) {
- fprintf(stderr, ", clicks=%d\\n", (Fl::event_clicks()+1));
+ tty->printf(", clicks=%d\\n", (Fl::event_clicks()+1));
} else {
- fprintf(stderr, "\\n");
+ tty->printf("\\n");
}
} else {
- fprintf(stderr, "TREE CALLBACK: reason=%s, changed=%d, item=(no item -- probably multiple items were changed at once)\\n",
+ tty->printf("TREE CALLBACK: reason=%s, changed=%d, item=(no item -- probably multiple items were changed at once)\\n",
reason_as_name(tree->callback_reason()),
tree->changed() ? 1 : 0);
}
@@ -896,7 +898,7 @@ Clears all items} xywh {570 471 95 16} labelsize 9
Fl_Button testcallbackflag_button {
label {Test Callback Flag}
callback {Fl_Tree_Item *root = tree->root();
-fprintf(stderr, "--- Checking docallback off\\n");
+tty->printf("--- Checking docallback off\\n");
if (!root) return;
//// "OFF" TEST
@@ -961,7 +963,7 @@ fl_alert("TEST COMPLETED\\n If you didn't see any error dialogs, test PASSED.");
Fl_Button testrootshowself_button {
label {Root Show Self}
callback {Fl_Tree_Item *root = tree->root();
-fprintf(stderr, "--- Show Tree\\n");
+tty->printf("--- Show Tree\\n");
if (root) root->show_self();}
tooltip {Test the root->'show_self() method to show the entire tree on stdout} xywh {570 511 95 16} labelsize 9
}
@@ -1231,11 +1233,11 @@ If nothing selected, all are changed} xywh {758 174 100 16} selection_color 1 la
}
Fl_Button showselected_button {
label {Show Selected}
- callback {fprintf(stderr, "--- SELECTED ITEMS\\n");
+ callback {tty->printf("--- SELECTED ITEMS\\n");
for ( Fl_Tree_Item *item = tree->first_selected_item();
item;
item = tree->next_selected_item(item) ) {
- fprintf(stderr, "\\t%s\\n", item->label() ? item->label() : "???");
+ tty->printf("\\t%s\\n", item->label() ? item->label() : "???");
}}
tooltip {Clears the selected items} xywh {864 134 95 16} labelsize 9
}
@@ -1298,15 +1300,15 @@ tree->redraw();}
}
Fl_Button nextselected_button {
label {next_selected()}
- callback {printf("--- TEST next_selected():\\n");
-printf(" // Walk down the tree (forwards)\\n");
+ callback {tty->printf("--- TEST next_selected():\\n");
+tty->printf(" // Walk down the tree (forwards)\\n");
for ( Fl_Tree_Item *i=tree->first_selected_item(); i; i=tree->next_selected_item(i, FL_Down) ) {
- printf(" Selected item: %s\\n", i->label()?i->label():"<nolabel>");
+ tty->printf(" Selected item: %s\\n", i->label()?i->label():"<nolabel>");
}
-printf(" // Walk up the tree (backwards)\\n");
+tty->printf(" // Walk up the tree (backwards)\\n");
for ( Fl_Tree_Item *i=tree->last_selected_item(); i; i=tree->next_selected_item(i, FL_Up) ) {
- printf(" Selected item: %s\\n", i->label()?i->label():"<nolabel>");
+ tty->printf(" Selected item: %s\\n", i->label()?i->label():"<nolabel>");
}}
tooltip {Tests the Fl_Tree::next_selected() function} xywh {713 239 95 16} labelsize 9
}
@@ -1703,7 +1705,7 @@ if ( !helpwin ) {
}
helpwin->resizable(helpdisp);
helpwin->show();}
- tooltip {Suggestions on how to do tests} xywh {935 554 95 16} labelsize 9
+ tooltip {Suggestions on how to do tests} xywh {935 545 95 16} labelsize 9
}
Fl_Value_Slider tree_scrollbar_size_slider {
label {Fl_Tree::scrollbar_size()}
@@ -1730,6 +1732,11 @@ tree->redraw();}
Fl_Box resizer_box {
xywh {0 263 15 14}
}
+ Fl_Box tty {
+ label label selected
+ xywh {16 571 1014 149} box DOWN_BOX color 0
+ class Fl_Simple_Terminal
+ }
}
code {// Initialize Tree
tree->root_label("ROOT");
diff --git a/test/unittest_simple_terminal.cxx b/test/unittest_simple_terminal.cxx
new file mode 100644
index 000000000..8d6d44f6c
--- /dev/null
+++ b/test/unittest_simple_terminal.cxx
@@ -0,0 +1,124 @@
+//
+// "$Id$"
+//
+// Unit tests for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 1998-2017 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+#include <time.h>
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Simple_Terminal.H>
+
+//
+//------- test the Fl_Simple_Terminal drawing capabilities ----------
+//
+class SimpleTerminal : public Fl_Group {
+ Fl_Simple_Terminal *tty1;
+ Fl_Simple_Terminal *tty2;
+ Fl_Simple_Terminal *tty3;
+ void AnsiTestPattern(Fl_Simple_Terminal *tty) {
+ tty->append("\033[30mBlack Courier 14\033[0m Normal text\n"
+ "\033[31mRed Courier 14\033[0m Normal text\n"
+ "\033[32mGreen Courier 14\033[0m Normal text\n"
+ "\033[33mYellow Courier 14\033[0m Normal text\n"
+ "\033[34mBlue Courier 14\033[0m Normal text\n"
+ "\033[35mMagenta Courier 14\033[0m Normal text\n"
+ "\033[36mCyan Courier 14\033[0m Normal text\n"
+ "\033[37mWhite Courier 14\033[0m Normal text\n"
+ "\033[40mBright Black Courier 14\033[0m Normal text\n"
+ "\033[41mBright Red Courier 14\033[0m Normal text\n"
+ "\033[42mBright Green Courier 14\033[0m Normal text\n"
+ "\033[43mBright Yellow Courier 14\033[0m Normal text\n"
+ "\033[44mBright Blue Courier 14\033[0m Normal text\n"
+ "\033[45mBright Magenta Courier 14\033[0m Normal text\n"
+ "\033[46mBright Cyan Courier 14\033[0m Normal text\n"
+ "\033[47mBright White Courier 14\033[0m Normal text\n"
+ "\n"
+ "\033[31mRed\033[32mGreen\033[33mYellow\033[34mBlue\033[35mMagenta\033[36mCyan\033[37mWhite\033[0m - "
+ "\033[31mX\033[32mX\033[33mX\033[34mX\033[35mX\033[36mX\033[37mX\033[0m\n"
+ "\033[41mRed\033[42mGreen\033[43mYellow\033[44mBlue\033[45mMagenta\033[46mCyan\033[47mWhite\033[0m - "
+ "\033[41mX\033[42mX\033[43mX\033[44mX\033[45mX\033[46mX\033[47mX\033[0m\n");
+ }
+ void GrayTestPattern(Fl_Simple_Terminal *tty) {
+ tty->append("Grayscale Test Pattern\n"
+ "--------------------------\n"
+ "\033[0m 100% white Courier 14\n"
+ "\033[1m 90% white Courier 14\n"
+ "\033[2m 80% white Courier 14\n"
+ "\033[3m 70% white Courier 14\n"
+ "\033[4m 60% white Courier 14\n"
+ "\033[5m 50% white Courier 14\n"
+ "\033[6m 40% white Courier 14\n"
+ "\033[7m 30% white Courier 14\n"
+ "\033[8m 20% white Courier 14\n"
+ "\033[9m 10% white Courier 14\n"
+ "\033[0m");
+ }
+ static void DateTimer_CB(void *data) {
+ Fl_Simple_Terminal *tty = (Fl_Simple_Terminal*)data;
+ time_t lt = time(NULL);
+ tty->printf("The time and date is now: %s", ctime(&lt));
+ Fl::repeat_timeout(3.0, DateTimer_CB, data);
+ }
+public:
+ static Fl_Widget *create() {
+ return new SimpleTerminal(TESTAREA_X, TESTAREA_Y, TESTAREA_W, TESTAREA_H);
+ }
+ SimpleTerminal(int x, int y, int w, int h) : Fl_Group(x, y, w, h) {
+ static Fl_Text_Display::Style_Table_Entry my_stable[] = { // 10 entry grayscale
+ // Font Color Font Face Font Size ANSI Sequence
+ // ---------- ---------------- --------- -------------
+ { 0xffffff00, FL_COURIER_BOLD, 14 }, // "\033[0m" 0 white 100%
+ { 0xe6e6e600, FL_COURIER_BOLD, 14 }, // "\033[1m" 1 white 90%
+ { 0xcccccc00, FL_COURIER_BOLD, 14 }, // "\033[2m" 2 white 80%
+ { 0xb3b3b300, FL_COURIER_BOLD, 14 }, // "\033[3m" 3 white 70%
+ { 0x99999900, FL_COURIER_BOLD, 14 }, // "\033[4m" 4 white 60%
+ { 0x80808000, FL_COURIER_BOLD, 14 }, // "\033[5m" 5 white 50% "\033[0m"
+ { 0x66666600, FL_COURIER_BOLD, 14 }, // "\033[6m" 6 white 40%
+ { 0x4d4d4d00, FL_COURIER_BOLD, 14 }, // "\033[7m" 7 white 30%
+ { 0x33333300, FL_COURIER_BOLD, 14 }, // "\033[8m" 8 white 20%
+ { 0x1a1a1a00, FL_COURIER_BOLD, 14 }, // "\033[9m" 9 white 10%
+ };
+ int tty_h = (h/3.5);
+ int tty_y1 = y+(tty_h*0)+20;
+ int tty_y2 = y+(tty_h*1)+40;
+ int tty_y3 = y+(tty_h*2)+60;
+
+ // TTY1
+ tty1 = new Fl_Simple_Terminal(x, tty_y1, w, tty_h,"Tty 1: ANSI off");
+ tty1->ansi(false);
+ Fl::add_timeout(0.5, DateTimer_CB, (void*)tty1);
+
+ // TTY2
+ tty2 = new Fl_Simple_Terminal(x, tty_y2, w, tty_h,"Tty 2: ANSI on");
+ tty2->ansi(true);
+ AnsiTestPattern(tty2);
+ Fl::add_timeout(0.5, DateTimer_CB, (void*)tty2);
+
+ // TTY3
+ tty3 = new Fl_Simple_Terminal(x, tty_y3, w, tty_h, "Tty 3: Grayscale Style Table");
+ tty3->style_table(my_stable, sizeof(my_stable), 0);
+ tty3->ansi(true);
+ GrayTestPattern(tty3);
+ Fl::add_timeout(0.5, DateTimer_CB, (void*)tty3);
+
+ end();
+ }
+};
+
+UnitTest simple_terminal("simple terminal", SimpleTerminal::create);
+
+//
+// End of "$Id$"
+//
diff --git a/test/unittests.cxx b/test/unittests.cxx
index a5c614918..a0d06b35e 100644
--- a/test/unittests.cxx
+++ b/test/unittests.cxx
@@ -153,6 +153,7 @@ public:
#include "unittest_viewport.cxx"
#include "unittest_scrollbarsize.cxx"
#include "unittest_schemes.cxx"
+#include "unittest_simple_terminal.cxx"
// callback whenever the browser value changes
void Browser_CB(Fl_Widget*, void*) {