fltk/fluid/CodeEditor.cxx
Ian MacArthur 6f4c59079f This change to fluid's CodeEditor ought to make it honour textsize changes
that are applied at text buffer creation.

How effective this will be for etxtsize changes that occur later is
another question, but this corrects a problem identified in the
thread "Re: [fltk.general] FLUID fonts earlier



git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9341 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
2012-04-13 14:45:18 +00:00

421 lines
10 KiB
C++

//
// "$Id$"
//
// Code editor widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2010 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 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"
};
// attempt to make the fluid code editor widget honour textsize setting
void CodeEditor::textsize(Fl_Fontsize s) {
Fl_Text_Editor::textsize(s); // call base class method
// now attempt to update our styletable to honour the new size...
int entries = sizeof(styletable) / sizeof(styletable[0]);
for(int iter = 0; iter < entries; iter++) {
styletable[iter].size = s;
}
} // textsize
// '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$".
//