diff options
| author | Michael R Sweet <michael.r.sweet@gmail.com> | 2002-06-09 18:28:49 +0000 |
|---|---|---|
| committer | Michael R Sweet <michael.r.sweet@gmail.com> | 2002-06-09 18:28:49 +0000 |
| commit | c3cde61e9879825c1547e366e2b9becedc1179e7 (patch) | |
| tree | 9c4aa45caf1d1d9c670b4725b5c4bea93a01debb | |
| parent | 464c7d8ba5b57b3022609d80fb4c305ccfc1cb06 (diff) | |
Add documentation for using styles in text editor.
Add placeholder for style attributes - hidden + underlined - for
future use.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.1@2301 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
| -rw-r--r-- | FL/Fl_Text_Display.H | 18 | ||||
| -rw-r--r-- | documentation/editor.html | 286 | ||||
| -rw-r--r-- | src/Fl_Text_Display.cxx | 18 | ||||
| -rw-r--r-- | test/editor.cxx | 13 |
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->copy(menuitems); </PRE></UL> @@ -118,7 +118,7 @@ m->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><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 && 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 && *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 && islower(*text)) { + // Might be a keyword... + for (temp = text, bufptr = buf; + islower(*temp) && bufptr < (buf + sizeof(buf) - 1); + *bufptr++ = *temp++); + + if (!islower(*temp)) { + *bufptr = '\0'; + + bufptr = buf; + + if (bsearch(&bufptr, code_types, + sizeof(code_types) / sizeof(code_types[0]), + sizeof(code_types[0]), compare_keywords)) { + while (text < temp) { + *style++ = 'F'; + text ++; + length --; + col ++; + } + + text --; + length ++; + last = 1; + continue; + } else if (bsearch(&bufptr, code_keywords, + sizeof(code_keywords) / sizeof(code_keywords[0]), + sizeof(code_keywords[0]), compare_keywords)) { + while (text < temp) { + *style++ = 'G'; + text ++; + length --; + col ++; + } + + text --; + length ++; + last = 1; + continue; + } + } + } + } else if (current == 'C' && 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' && (*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 $". // |
