| 
							- //
 - // "$Id: CodeEditor.cxx 7903 2010-11-28 21:06:39Z matt $"
 - //
 - // Code editor widget for the Fast Light Tool Kit (FLTK).
 - //
 - // Copyright 1998-2010 by Bill Spitzak and others.
 - //
 - // This library is free software; you can redistribute it and/or
 - // modify it under the terms of the GNU Library General Public
 - // License as published by the Free Software Foundation; either
 - // version 2 of the License, or (at your option) any later version.
 - //
 - // This library is distributed in the hope that it will be useful,
 - // but WITHOUT ANY WARRANTY; without even the implied warranty of
 - // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - // Library General Public License for more details.
 - //
 - // You should have received a copy of the GNU Library General Public
 - // License along with this library; if not, write to the Free Software
 - // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 - // USA.
 - //
 - // Please report all bugs and problems on the following page:
 - //
 - //     http://www.fltk.org/str.php
 - //
 - 
 - //
 - // Include necessary headers...
 - //
 - 
 - #include <stdio.h>
 - #include <stdlib.h>
 - #include <string.h>
 - #include <ctype.h>
 - #include "CodeEditor.h"
 - 
 - 
 - Fl_Text_Display::Style_Table_Entry CodeEditor::
 - 		styletable[] = {	// Style table
 - 		  { FL_FOREGROUND_COLOR, FL_COURIER,        11 }, // A - Plain
 - 		  { FL_DARK_GREEN,       FL_COURIER_ITALIC, 11 }, // B - Line comments
 - 		  { FL_DARK_GREEN,       FL_COURIER_ITALIC, 11 }, // C - Block comments
 - 		  { FL_BLUE,             FL_COURIER,        11 }, // D - Strings
 - 		  { FL_DARK_RED,         FL_COURIER,        11 }, // E - Directives
 - 		  { FL_DARK_RED,         FL_COURIER_BOLD,   11 }, // F - Types
 - 		  { FL_BLUE,             FL_COURIER_BOLD,   11 }  // G - Keywords
 - 		};
 - const char * const CodeEditor::
 - 		code_keywords[] = {	// Sorted list of C/C++ keywords...
 - 		  "and",
 - 		  "and_eq",
 - 		  "asm",
 - 		  "bitand",
 - 		  "bitor",
 - 		  "break",
 - 		  "case",
 - 		  "catch",
 - 		  "compl",
 - 		  "continue",
 - 		  "default",
 - 		  "delete",
 - 		  "do",
 - 		  "else",
 - 		  "false",
 - 		  "for",
 - 		  "goto",
 - 		  "if",
 - 		  "new",
 - 		  "not",
 - 		  "not_eq",
 - 		  "operator",
 - 		  "or",
 - 		  "or_eq",
 - 		  "return",
 - 		  "switch",
 - 		  "template",
 - 		  "this",
 - 		  "throw",
 - 		  "true",
 - 		  "try",
 - 		  "while",
 - 		  "xor",
 - 		  "xor_eq"
 - 		};
 - const char * const CodeEditor::
 - 		code_types[] = {	// Sorted list of C/C++ types...
 - 		  "auto",
 - 		  "bool",
 - 		  "char",
 - 		  "class",
 - 		  "const",
 - 		  "const_cast",
 - 		  "double",
 - 		  "dynamic_cast",
 - 		  "enum",
 - 		  "explicit",
 - 		  "extern",
 - 		  "float",
 - 		  "friend",
 - 		  "inline",
 - 		  "int",
 - 		  "long",
 - 		  "mutable",
 - 		  "namespace",
 - 		  "private",
 - 		  "protected",
 - 		  "public",
 - 		  "register",
 - 		  "short",
 - 		  "signed",
 - 		  "sizeof",
 - 		  "static",
 - 		  "static_cast",
 - 		  "struct",
 - 		  "template",
 - 		  "typedef",
 - 		  "typename",
 - 		  "union",
 - 		  "unsigned",
 - 		  "virtual",
 - 		  "void",
 - 		  "volatile"
 - 		};
 - 
 - 
 - // 'compare_keywords()' - Compare two keywords...
 - int CodeEditor::compare_keywords(const void *a, const void *b) {
 -   return (strcmp(*((const char **)a), *((const char **)b)));
 - }
 - 
 - // 'style_parse()' - Parse text and produce style data.
 - void CodeEditor::style_parse(const char *text, char *style, int length) {
 -   char		current;
 -   int		col;
 -   int		last;
 -   char		buf[255],
 - 		*bufptr;
 -   const char	*temp;
 - 
 -   // Style letters:
 -   //
 -   // A - Plain
 -   // B - Line comments
 -   // C - Block comments
 -   // D - Strings
 -   // E - Directives
 -   // F - Types
 -   // G - Keywords
 - 
 -   for (current = *style, col = 0, last = 0; length > 0; length --, text ++) {
 -     if (current == 'B' || current == 'F' || current == 'G') current = 'A';
 -     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';
 - 	for (; length > 0 && *text != '\n'; length --, text ++) *style++ = 'B';
 - 
 -         if (length == 0) break;
 -       } 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) || *text == '_')) {
 -         // Might be a keyword...
 - 	for (temp = text, bufptr = buf;
 - 	     (islower(*temp) || *temp == '_') && bufptr < (buf + sizeof(buf) - 1);
 - 	     *bufptr++ = *temp++);
 - 
 -         if (!islower(*temp) && *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 == '_' || *text == '.';
 - 
 -     if (*text == '\n') {
 -       // Reset column and possibly reset the style
 -       col = 0;
 -       if (current == 'B' || current == 'E') current = 'A';
 -     }
 -   }
 - }
 - 
 - // 'style_unfinished_cb()' - Update unfinished styles.
 - void CodeEditor::style_unfinished_cb(int, void*) { }
 - 
 - // 'style_update()' - Update the style buffer...
 - void CodeEditor::style_update(int pos, int nInserted, int nDeleted,
 -                               int /*nRestyled*/, const char * /*deletedText*/,
 -                               void *cbArg) {
 -   CodeEditor	*editor = (CodeEditor *)cbArg;
 -   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) {
 -     editor->mStyleBuffer->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';
 - 
 -     editor->mStyleBuffer->replace(pos, pos + nDeleted, style);
 -     delete[] style;
 -   } else {
 -     // Just delete characters in the style buffer...
 -     editor->mStyleBuffer->remove(pos, pos + nDeleted);
 -   }
 - 
 -   // Select the area that was just updated to avoid unnecessary
 -   // callbacks...
 -   editor->mStyleBuffer->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 = editor->mBuffer->line_start(pos);
 -   end   = editor->mBuffer->line_end(pos + nInserted);
 -   text  = editor->mBuffer->text_range(start, end);
 -   style = editor->mStyleBuffer->text_range(start, end);
 -   if (start==end)
 -     last = 0;
 -   else
 -     last  = style[end - start - 1];
 - 
 -   style_parse(text, style, end - start);
 - 
 -   editor->mStyleBuffer->replace(start, end, style);
 -   editor->redisplay_range(start, end);
 - 
 -   if (start==end || 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   = editor->mBuffer->length();
 -     text  = editor->mBuffer->text_range(start, end);
 -     style = editor->mStyleBuffer->text_range(start, end);
 - 
 -     style_parse(text, style, end - start);
 - 
 -     editor->mStyleBuffer->replace(start, end, style);
 -     editor->redisplay_range(start, end);
 -   }
 - 
 -   free(text);
 -   free(style);
 - }
 - 
 - int CodeEditor::auto_indent(int, CodeEditor* e) {
 -   if (e->buffer()->selected()) {
 -     e->insert_position(e->buffer()->primary_selection()->start());
 -     e->buffer()->remove_selection();
 -   }
 - 
 -   int pos = e->insert_position();
 -   int start = e->line_start(pos);
 -   char *text = e->buffer()->text_range(start, pos);
 -   char *ptr;
 - 
 -   for (ptr = text; isspace(*ptr); ptr ++);
 -   *ptr = '\0';  
 -   if (*text) {
 -     // use only a single 'insert' call to avoid redraw issues
 -     int n = strlen(text);
 -     char *b = (char*)malloc(n+2);
 -     *b = '\n';
 -     strcpy(b+1, text);
 -     e->insert(b);
 -     free(b);
 -   } else {
 -     e->insert("\n");
 -   }
 -   e->show_insert_position();
 -   e->set_changed();
 -   if (e->when()&FL_WHEN_CHANGED) e->do_callback();
 - 
 -   free(text);
 - 
 -   return 1;
 - }
 - 
 - // Create a CodeEditor widget...
 - CodeEditor::CodeEditor(int X, int Y, int W, int H, const char *L) :
 -   Fl_Text_Editor(X, Y, W, H, L) {
 -   buffer(new Fl_Text_Buffer);
 - 
 -   char *style = new char[mBuffer->length() + 1];
 -   char *text = mBuffer->text();
 - 
 -   memset(style, 'A', mBuffer->length());
 -   style[mBuffer->length()] = '\0';
 - 
 -   highlight_data(new Fl_Text_Buffer(mBuffer->length()), styletable,
 -                  sizeof(styletable) / sizeof(styletable[0]),
 - 		 'A', style_unfinished_cb, this);
 - 
 -   style_parse(text, style, mBuffer->length());
 - 
 -   mStyleBuffer->text(style);
 -   delete[] style;
 -   free(text);
 - 
 -   mBuffer->add_modify_callback(style_update, this);
 -   add_key_binding(FL_Enter, FL_TEXT_EDITOR_ANY_STATE,
 -                   (Fl_Text_Editor::Key_Func)auto_indent);
 - }
 - 
 - // Destroy a CodeEditor widget...
 - CodeEditor::~CodeEditor() {
 -   Fl_Text_Buffer *buf = mStyleBuffer;
 -   mStyleBuffer = 0;
 -   delete buf;
 - 
 -   buf = mBuffer;
 -   buffer(0);
 -   delete buf;
 - }
 - 
 - 
 - CodeViewer::CodeViewer(int X, int Y, int W, int H, const char *L)
 - : CodeEditor(X, Y, W, H, L) 
 - {
 -   default_key_function(kf_ignore);  
 -   remove_all_key_bindings(&key_bindings);
 -   cursor_style(CARET_CURSOR);
 - }
 - 
 - //
 - // End of "$Id: CodeEditor.cxx 7903 2010-11-28 21:06:39Z matt $".
 - //
 
 
  |