summaryrefslogtreecommitdiff
path: root/documentation/editor.html
diff options
context:
space:
mode:
authorMichael R Sweet <michael.r.sweet@gmail.com>2002-06-09 18:28:49 +0000
committerMichael R Sweet <michael.r.sweet@gmail.com>2002-06-09 18:28:49 +0000
commitc3cde61e9879825c1547e366e2b9becedc1179e7 (patch)
tree9c4aa45caf1d1d9c670b4725b5c4bea93a01debb /documentation/editor.html
parent464c7d8ba5b57b3022609d80fb4c305ccfc1cb06 (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
Diffstat (limited to 'documentation/editor.html')
-rw-r--r--documentation/editor.html286
1 files changed, 284 insertions, 2 deletions
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>