summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FL/Fl_Text_Display.H18
-rw-r--r--documentation/editor.html286
-rw-r--r--src/Fl_Text_Display.cxx18
-rw-r--r--test/editor.cxx13
4 files changed, 315 insertions, 20 deletions
diff --git a/FL/Fl_Text_Display.H b/FL/Fl_Text_Display.H
index 754cc10cb..575748758 100644
--- a/FL/Fl_Text_Display.H
+++ b/FL/Fl_Text_Display.H
@@ -1,5 +1,5 @@
//
-// "$Id: Fl_Text_Display.H,v 1.4.2.7 2002/06/08 12:51:38 easysw Exp $"
+// "$Id: Fl_Text_Display.H,v 1.4.2.8 2002/06/09 18:28:48 easysw Exp $"
//
// Header file for Fl_Text_Display class.
//
@@ -55,10 +55,18 @@ class Fl_Text_Display: public Fl_Group {
typedef void (*Unfinished_Style_Cb)();
+ // style attributes - currently not implemented!
+ enum {
+ ATTR_NONE = 0,
+ ATTR_UNDERLINE = 1,
+ ATTR_HIDDEN = 2
+ };
+
struct FL_EXPORT Style_Table_Entry {
- Fl_Color color;
- Fl_Font font;
- int size;
+ Fl_Color color;
+ Fl_Font font;
+ int size;
+ unsigned attr;
};
FL_EXPORT Fl_Text_Display(int X, int Y, int W, int H, const char *l = 0);
@@ -234,5 +242,5 @@ class Fl_Text_Display: public Fl_Group {
#endif
//
-// End of "$Id: Fl_Text_Display.H,v 1.4.2.7 2002/06/08 12:51:38 easysw Exp $".
+// End of "$Id: Fl_Text_Display.H,v 1.4.2.8 2002/06/09 18:28:48 easysw Exp $".
//
diff --git a/documentation/editor.html b/documentation/editor.html
index 3fb5c43b0..56de1ad71 100644
--- a/documentation/editor.html
+++ b/documentation/editor.html
@@ -105,7 +105,7 @@ Fl_Menu_Item menuitems[] = {
<TT>Fl_Menu_Bar</TT> widget and assign the menus to it with:</P>
<UL><PRE>
-Fl_Menu_Bar *m = new Fl_Menu_Bar(0, 0, 512, 30);
+Fl_Menu_Bar *m = new Fl_Menu_Bar(0, 0, 640, 30);
m-&gt;copy(menuitems);
</PRE></UL>
@@ -118,7 +118,7 @@ m-&gt;copy(menuitems);
widget to edit the text:
<UL><PRE>
-w->editor = new Fl_Text_Editor(0, 30, 512, 354);
+w->editor = new Fl_Text_Editor(0, 30, 640, 370);
w->editor->buffer(textbuf);
</PRE></UL>
@@ -598,5 +598,287 @@ The final editor window should look like the image in Figure 4-2.
<P ALIGN="CENTER"><IMG src="editor.gif" ALT="The completed editor window."><BR>
<I>Figure 4-2: The completed editor window</I></P>
+<H2>Advanced Features</H2>
+
+<P>Now that we've implemented the basic functionality, it is
+time to show off some of the advanced features of the
+<CODE>Fl_Text_Editor</CODE> widget.
+
+<H3>Syntax Highlighting</H3>
+
+<P>The <CODE>Fl_Text_Editor</CODE> widget supports highlighting
+of text with different fonts, colors, and sizes. The
+implementation is based on the excellent <A
+HREF="http://www.nedit.org/">NEdit</A> text editor core, which
+uses a parallel "style" buffer which tracks the font, color, and
+size of the text that is drawn.
+
+<P>Styles are defined using the
+<CODE>Fl_Text_Display::Style_Table_Entry</CODE> structure
+defined in <CODE>&lt;FL/Fl_Text_Display.H></CODE>:
+
+<UL><PRE>
+struct Style_Table_Entry {
+ Fl_Color color;
+ Fl_Font font;
+ int size;
+ unsigned attr;
+};
+</PRE></UL>
+
+<P>The <CODE>color</CODE> member sets the color for the text,
+the <CODE>font</CODE> member sets the FLTK font index to use,
+and the <CODE>size</CODE> member sets the pixel size of the
+text. The <CODE>attr</CODE> member is currently not used.
+
+<P>For our text editor we'll define 7 styles for plain code,
+comments, keywords, and preprocessor directives:
+
+<UL><PRE>
+Fl_Text_Display::Style_Table_Entry styletable[] = { // Style table
+ { FL_BLACK, FL_COURIER, FL_NORMAL_SIZE }, // A - Plain
+ { FL_DARK_GREEN, FL_COURIER_ITALIC, FL_NORMAL_SIZE }, // B - Line comments
+ { FL_DARK_GREEN, FL_COURIER_ITALIC, FL_NORMAL_SIZE }, // C - Block comments
+ { FL_BLUE, FL_COURIER, FL_NORMAL_SIZE }, // D - Strings
+ { FL_DARK_RED, FL_COURIER, FL_NORMAL_SIZE }, // E - Directives
+ { FL_DARK_RED, FL_COURIER_BOLD, FL_NORMAL_SIZE }, // F - Types
+ { FL_BLUE, FL_COURIER_BOLD, FL_NORMAL_SIZE } // G - Keywords
+};
+</PRE></UL>
+
+<P>You'll notice that the comments show a letter next to each
+style - each style in the style buffer is referenced using a
+character starting with the letter 'A'.
+
+<P>You call the <CODE>highlight_data()</CODE> method to associate the
+style data and buffer with the text editor widget:
+
+<UL><PRE>
+Fl_Text_Buffer *stylebuf;
+
+w->editor->highlight_data(stylebuf, styletable,
+ sizeof(styletable) / sizeof(styletable[0]),
+ 'A', style_unfinished_cb, 0);
+</PRE></UL>
+
+<P>Finally, you need to add a callback to the main text buffer so
+that changes to the text buffer are mirrored in the style buffer:
+
+<UL><PRE>
+textbuf->add_modify_callback(style_update, w->editor);
+</PRE></UL>
+
+<P>The <CODE>style_update()</CODE> function, like the <CODE>change_cb()</CODE>
+function described earlier, is called whenever text is added or removed from
+the text buffer. It mirrors the changes in the style buffer and then updates
+the style data as necessary:
+
+<UL><PRE>
+//
+// 'style_update()' - Update the style buffer...
+//
+
+void
+style_update(int pos, // I - Position of update
+ int nInserted, // I - Number of inserted chars
+ int nDeleted, // I - Number of deleted chars
+ int nRestyled, // I - Number of restyled chars
+ const char *deletedText, // I - Text that was deleted
+ void *cbArg) { // I - Callback data
+ int start, // Start of text
+ end; // End of text
+ char last, // Last style on line
+ *style, // Style data
+ *text; // Text data
+
+
+ // If this is just a selection change, just unselect the style buffer...
+ if (nInserted == 0 &amp;&amp; nDeleted == 0) {
+ stylebuf->unselect();
+ return;
+ }
+
+ // Track changes in the text buffer...
+ if (nInserted > 0) {
+ // Insert characters into the style buffer...
+ style = new char[nInserted + 1];
+ memset(style, 'A', nInserted);
+ style[nInserted] = '\0';
+
+ stylebuf->replace(pos, pos + nDeleted, style);
+ delete[] style;
+ } else {
+ // Just delete characters in the style buffer...
+ stylebuf->remove(pos, pos + nDeleted);
+ }
+
+ // Select the area that was just updated to avoid unnecessary
+ // callbacks...
+ stylebuf->select(pos, pos + nInserted - nDeleted);
+
+ // Re-parse the changed region; we do this by parsing from the
+ // beginning of the line of the changed region to the end of
+ // the line of the changed region... Then we check the last
+ // style character and keep updating if we have a multi-line
+ // comment character...
+ start = textbuf->line_start(pos);
+ end = textbuf->line_end(pos + nInserted - nDeleted);
+ text = textbuf->text_range(start, end);
+ style = stylebuf->text_range(start, end);
+ last = style[end - start - 1];
+
+ style_parse(text, style, end - start);
+
+ stylebuf->replace(start, end, style);
+ ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
+
+ if (last != style[end - start - 1]) {
+ // The last character on the line changed styles, so reparse the
+ // remainder of the buffer...
+ free(text);
+ free(style);
+
+ end = textbuf->length();
+ text = textbuf->text_range(start, end);
+ style = stylebuf->text_range(start, end);
+
+ style_parse(text, style, end - start);
+
+ stylebuf->replace(start, end, style);
+ ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
+ }
+
+ free(text);
+ free(style);
+}
+</PRE></UL>
+
+<P>The <CODE>style_parse()</CODE> function scans a copy of the
+text in the buffer and generates the necessary style characters
+for display. It assumes that parsing begins at the start of a line:
+
+<UL><PRE>
+//
+// 'style_parse()' - Parse text and produce style data.
+//
+
+void
+style_parse(const char *text,
+ char *style,
+ int length) {
+ char current;
+ int col;
+ int last;
+ char buf[255],
+ *bufptr;
+ const char *temp;
+
+ for (current = *style, col = 0, last = 0; length > 0; length --, text ++) {
+ if (current == 'A') {
+ // Check for directives, comments, strings, and keywords...
+ if (col == 0 &amp;&amp; *text == '#') {
+ // Set style to directive
+ current = 'E';
+ } else if (strncmp(text, "//", 2) == 0) {
+ current = 'B';
+ } else if (strncmp(text, "/*", 2) == 0) {
+ current = 'C';
+ } else if (strncmp(text, "\\\"", 2) == 0) {
+ // Quoted quote...
+ *style++ = current;
+ *style++ = current;
+ text ++;
+ length --;
+ col += 2;
+ continue;
+ } else if (*text == '\"') {
+ current = 'D';
+ } else if (!last &amp;&amp; islower(*text)) {
+ // Might be a keyword...
+ for (temp = text, bufptr = buf;
+ islower(*temp) &amp;&amp; bufptr &lt; (buf + sizeof(buf) - 1);
+ *bufptr++ = *temp++);
+
+ if (!islower(*temp)) {
+ *bufptr = '\0';
+
+ bufptr = buf;
+
+ if (bsearch(&amp;bufptr, code_types,
+ sizeof(code_types) / sizeof(code_types[0]),
+ sizeof(code_types[0]), compare_keywords)) {
+ while (text &lt; temp) {
+ *style++ = 'F';
+ text ++;
+ length --;
+ col ++;
+ }
+
+ text --;
+ length ++;
+ last = 1;
+ continue;
+ } else if (bsearch(&amp;bufptr, code_keywords,
+ sizeof(code_keywords) / sizeof(code_keywords[0]),
+ sizeof(code_keywords[0]), compare_keywords)) {
+ while (text &lt; temp) {
+ *style++ = 'G';
+ text ++;
+ length --;
+ col ++;
+ }
+
+ text --;
+ length ++;
+ last = 1;
+ continue;
+ }
+ }
+ }
+ } else if (current == 'C' &amp;&amp; strncmp(text, "*/", 2) == 0) {
+ // Close a C comment...
+ *style++ = current;
+ *style++ = current;
+ text ++;
+ length --;
+ current = 'A';
+ col += 2;
+ continue;
+ } else if (current == 'D') {
+ // Continuing in string...
+ if (strncmp(text, "\\\"", 2) == 0) {
+ // Quoted end quote...
+ *style++ = current;
+ *style++ = current;
+ text ++;
+ length --;
+ col += 2;
+ continue;
+ } else if (*text == '\"') {
+ // End quote...
+ *style++ = current;
+ col ++;
+ current = 'A';
+ continue;
+ }
+ }
+
+ // Copy style info...
+ if (current == 'A' &amp;&amp; (*text == '{' || *text == '}')) *style++ = 'G';
+ else *style++ = current;
+ col ++;
+
+ last = isalnum(*text) || *text == '.';
+
+ if (*text == '\n') {
+ // Reset column and possibly reset the style
+ col = 0;
+ if (current == 'B' || current == 'E') current = 'A';
+ }
+ }
+}
+</PRE></UL>
+
+
</BODY>
</HTML>
diff --git a/src/Fl_Text_Display.cxx b/src/Fl_Text_Display.cxx
index 8a4c85075..89011d023 100644
--- a/src/Fl_Text_Display.cxx
+++ b/src/Fl_Text_Display.cxx
@@ -1,5 +1,5 @@
//
-// "$Id: Fl_Text_Display.cxx,v 1.12.2.17 2002/06/09 13:35:49 easysw Exp $"
+// "$Id: Fl_Text_Display.cxx,v 1.12.2.18 2002/06/09 18:28:49 easysw Exp $"
//
// Copyright 2001-2002 by Bill Spitzak and others.
// Original code Copyright Mark Edel. Permission to distribute under
@@ -1045,7 +1045,11 @@ void Fl_Text_Display::draw_string( int style, int X, int Y, int toX,
Fl_Color background;
if ( style & STYLE_LOOKUP_MASK ) {
- styleRec = &mStyleTable[ ( style & STYLE_LOOKUP_MASK ) - 'A' ];
+ int si = (style & STYLE_LOOKUP_MASK) - 'A';
+ if (si < 0) si = 0;
+ else if (si >= mNStyles) si = mNStyles - 1;
+
+ styleRec = mStyleTable + si;
font = styleRec->font;
size = styleRec->size;
@@ -1234,8 +1238,12 @@ int Fl_Text_Display::string_width( const char *string, int length, int style ) {
int size;
if ( style & STYLE_LOOKUP_MASK ) {
- font = mStyleTable[ ( style & STYLE_LOOKUP_MASK ) - 'A' ].font;
- size = mStyleTable[ ( style & STYLE_LOOKUP_MASK ) - 'A' ].size;
+ int si = (style & STYLE_LOOKUP_MASK) - 'A';
+ if (si < 0) si = 0;
+ else if (si >= mNStyles) si = mNStyles - 1;
+
+ font = mStyleTable[si].font;
+ size = mStyleTable[si].size;
} else {
font = textfont();
size = textsize();
@@ -1951,5 +1959,5 @@ int Fl_Text_Display::handle(int event) {
//
-// End of "$Id: Fl_Text_Display.cxx,v 1.12.2.17 2002/06/09 13:35:49 easysw Exp $".
+// End of "$Id: Fl_Text_Display.cxx,v 1.12.2.18 2002/06/09 18:28:49 easysw Exp $".
//
diff --git a/test/editor.cxx b/test/editor.cxx
index faed3942a..fd1f54a25 100644
--- a/test/editor.cxx
+++ b/test/editor.cxx
@@ -1,5 +1,5 @@
//
-// "$Id: editor.cxx,v 1.2.2.3.2.8 2002/06/09 13:35:49 easysw Exp $"
+// "$Id: editor.cxx,v 1.2.2.3.2.9 2002/06/09 18:28:49 easysw Exp $"
//
// A simple text editor program for the Fast Light Tool Kit (FLTK).
//
@@ -57,8 +57,7 @@ Fl_Text_Buffer *textbuf = 0;
// Syntax highlighting stuff...
Fl_Text_Buffer *stylebuf = 0;
Fl_Text_Display::Style_Table_Entry
- styletable[] =
- {
+ styletable[] = { // Style table
{ FL_BLACK, FL_COURIER, FL_NORMAL_SIZE }, // A - Plain
{ FL_DARK_GREEN, FL_COURIER_ITALIC, FL_NORMAL_SIZE }, // B - Line comments
{ FL_DARK_GREEN, FL_COURIER_ITALIC, FL_NORMAL_SIZE }, // C - Block comments
@@ -67,8 +66,7 @@ Fl_Text_Display::Style_Table_Entry
{ FL_DARK_RED, FL_COURIER_BOLD, FL_NORMAL_SIZE }, // F - Types
{ FL_BLUE, FL_COURIER_BOLD, FL_NORMAL_SIZE } // G - Keywords
};
-const char *code_keywords[] = // List of known C/C++ keywords...
- {
+const char *code_keywords[] = { // List of known C/C++ keywords...
"and",
"and_eq",
"asm",
@@ -104,8 +102,7 @@ const char *code_keywords[] = // List of known C/C++ keywords...
"xor",
"xor_eq"
};
-const char *code_types[] = // List of known C/C++ types...
- {
+const char *code_types[] = { // List of known C/C++ types...
"auto",
"bool",
"char",
@@ -764,5 +761,5 @@ int main(int argc, char **argv) {
}
//
-// End of "$Id: editor.cxx,v 1.2.2.3.2.8 2002/06/09 13:35:49 easysw Exp $".
+// End of "$Id: editor.cxx,v 1.2.2.3.2.9 2002/06/09 18:28:49 easysw Exp $".
//