|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // 00002 // "$Id: CodeEditor.cxx 7903 2010-11-28 21:06:39Z matt $" 00003 // 00004 // Code editor widget for the Fast Light Tool Kit (FLTK). 00005 // 00006 // Copyright 1998-2010 by Bill Spitzak and others. 00007 // 00008 // This library is free software; you can redistribute it and/or 00009 // modify it under the terms of the GNU Library General Public 00010 // License as published by the Free Software Foundation; either 00011 // version 2 of the License, or (at your option) any later version. 00012 // 00013 // This library is distributed in the hope that it will be useful, 00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 // Library General Public License for more details. 00017 // 00018 // You should have received a copy of the GNU Library General Public 00019 // License along with this library; if not, write to the Free Software 00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00021 // USA. 00022 // 00023 // Please report all bugs and problems on the following page: 00024 // 00025 // http://www.fltk.org/str.php 00026 // 00027 00028 // 00029 // Include necessary headers... 00030 // 00031 00032 #include <stdio.h> 00033 #include <stdlib.h> 00034 #include <string.h> 00035 #include <ctype.h> 00036 #include "CodeEditor.h" 00037 00038 00039 Fl_Text_Display::Style_Table_Entry CodeEditor:: 00040 styletable[] = { // Style table 00041 { FL_FOREGROUND_COLOR, FL_COURIER, 11 }, // A - Plain 00042 { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // B - Line comments 00043 { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // C - Block comments 00044 { FL_BLUE, FL_COURIER, 11 }, // D - Strings 00045 { FL_DARK_RED, FL_COURIER, 11 }, // E - Directives 00046 { FL_DARK_RED, FL_COURIER_BOLD, 11 }, // F - Types 00047 { FL_BLUE, FL_COURIER_BOLD, 11 } // G - Keywords 00048 }; 00049 const char * const CodeEditor:: 00050 code_keywords[] = { // Sorted list of C/C++ keywords... 00051 "and", 00052 "and_eq", 00053 "asm", 00054 "bitand", 00055 "bitor", 00056 "break", 00057 "case", 00058 "catch", 00059 "compl", 00060 "continue", 00061 "default", 00062 "delete", 00063 "do", 00064 "else", 00065 "false", 00066 "for", 00067 "goto", 00068 "if", 00069 "new", 00070 "not", 00071 "not_eq", 00072 "operator", 00073 "or", 00074 "or_eq", 00075 "return", 00076 "switch", 00077 "template", 00078 "this", 00079 "throw", 00080 "true", 00081 "try", 00082 "while", 00083 "xor", 00084 "xor_eq" 00085 }; 00086 const char * const CodeEditor:: 00087 code_types[] = { // Sorted list of C/C++ types... 00088 "auto", 00089 "bool", 00090 "char", 00091 "class", 00092 "const", 00093 "const_cast", 00094 "double", 00095 "dynamic_cast", 00096 "enum", 00097 "explicit", 00098 "extern", 00099 "float", 00100 "friend", 00101 "inline", 00102 "int", 00103 "long", 00104 "mutable", 00105 "namespace", 00106 "private", 00107 "protected", 00108 "public", 00109 "register", 00110 "short", 00111 "signed", 00112 "sizeof", 00113 "static", 00114 "static_cast", 00115 "struct", 00116 "template", 00117 "typedef", 00118 "typename", 00119 "union", 00120 "unsigned", 00121 "virtual", 00122 "void", 00123 "volatile" 00124 }; 00125 00126 00127 // 'compare_keywords()' - Compare two keywords... 00128 int CodeEditor::compare_keywords(const void *a, const void *b) { 00129 return (strcmp(*((const char **)a), *((const char **)b))); 00130 } 00131 00132 // 'style_parse()' - Parse text and produce style data. 00133 void CodeEditor::style_parse(const char *text, char *style, int length) { 00134 char current; 00135 int col; 00136 int last; 00137 char buf[255], 00138 *bufptr; 00139 const char *temp; 00140 00141 // Style letters: 00142 // 00143 // A - Plain 00144 // B - Line comments 00145 // C - Block comments 00146 // D - Strings 00147 // E - Directives 00148 // F - Types 00149 // G - Keywords 00150 00151 for (current = *style, col = 0, last = 0; length > 0; length --, text ++) { 00152 if (current == 'B' || current == 'F' || current == 'G') current = 'A'; 00153 if (current == 'A') { 00154 // Check for directives, comments, strings, and keywords... 00155 if (col == 0 && *text == '#') { 00156 // Set style to directive 00157 current = 'E'; 00158 } else if (strncmp(text, "//", 2) == 0) { 00159 current = 'B'; 00160 for (; length > 0 && *text != '\n'; length --, text ++) *style++ = 'B'; 00161 00162 if (length == 0) break; 00163 } else if (strncmp(text, "/*", 2) == 0) { 00164 current = 'C'; 00165 } else if (strncmp(text, "\\\"", 2) == 0) { 00166 // Quoted quote... 00167 *style++ = current; 00168 *style++ = current; 00169 text ++; 00170 length --; 00171 col += 2; 00172 continue; 00173 } else if (*text == '\"') { 00174 current = 'D'; 00175 } else if (!last && (islower(*text) || *text == '_')) { 00176 // Might be a keyword... 00177 for (temp = text, bufptr = buf; 00178 (islower(*temp) || *temp == '_') && bufptr < (buf + sizeof(buf) - 1); 00179 *bufptr++ = *temp++); 00180 00181 if (!islower(*temp) && *temp != '_') { 00182 *bufptr = '\0'; 00183 00184 bufptr = buf; 00185 00186 if (bsearch(&bufptr, code_types, 00187 sizeof(code_types) / sizeof(code_types[0]), 00188 sizeof(code_types[0]), compare_keywords)) { 00189 while (text < temp) { 00190 *style++ = 'F'; 00191 text ++; 00192 length --; 00193 col ++; 00194 } 00195 00196 text --; 00197 length ++; 00198 last = 1; 00199 continue; 00200 } else if (bsearch(&bufptr, code_keywords, 00201 sizeof(code_keywords) / sizeof(code_keywords[0]), 00202 sizeof(code_keywords[0]), compare_keywords)) { 00203 while (text < temp) { 00204 *style++ = 'G'; 00205 text ++; 00206 length --; 00207 col ++; 00208 } 00209 00210 text --; 00211 length ++; 00212 last = 1; 00213 continue; 00214 } 00215 } 00216 } 00217 } else if (current == 'C' && strncmp(text, "*/", 2) == 0) { 00218 // Close a C comment... 00219 *style++ = current; 00220 *style++ = current; 00221 text ++; 00222 length --; 00223 current = 'A'; 00224 col += 2; 00225 continue; 00226 } else if (current == 'D') { 00227 // Continuing in string... 00228 if (strncmp(text, "\\\"", 2) == 0) { 00229 // Quoted end quote... 00230 *style++ = current; 00231 *style++ = current; 00232 text ++; 00233 length --; 00234 col += 2; 00235 continue; 00236 } else if (*text == '\"') { 00237 // End quote... 00238 *style++ = current; 00239 col ++; 00240 current = 'A'; 00241 continue; 00242 } 00243 } 00244 00245 // Copy style info... 00246 if (current == 'A' && (*text == '{' || *text == '}')) *style++ = 'G'; 00247 else *style++ = current; 00248 col ++; 00249 00250 last = isalnum(*text) || *text == '_' || *text == '.'; 00251 00252 if (*text == '\n') { 00253 // Reset column and possibly reset the style 00254 col = 0; 00255 if (current == 'B' || current == 'E') current = 'A'; 00256 } 00257 } 00258 } 00259 00260 // 'style_unfinished_cb()' - Update unfinished styles. 00261 void CodeEditor::style_unfinished_cb(int, void*) { } 00262 00263 // 'style_update()' - Update the style buffer... 00264 void CodeEditor::style_update(int pos, int nInserted, int nDeleted, 00265 int /*nRestyled*/, const char * /*deletedText*/, 00266 void *cbArg) { 00267 CodeEditor *editor = (CodeEditor *)cbArg; 00268 int start, // Start of text 00269 end; // End of text 00270 char last, // Last style on line 00271 *style, // Style data 00272 *text; // Text data 00273 00274 00275 // If this is just a selection change, just unselect the style buffer... 00276 if (nInserted == 0 && nDeleted == 0) { 00277 editor->mStyleBuffer->unselect(); 00278 return; 00279 } 00280 00281 // Track changes in the text buffer... 00282 if (nInserted > 0) { 00283 // Insert characters into the style buffer... 00284 style = new char[nInserted + 1]; 00285 memset(style, 'A', nInserted); 00286 style[nInserted] = '\0'; 00287 00288 editor->mStyleBuffer->replace(pos, pos + nDeleted, style); 00289 delete[] style; 00290 } else { 00291 // Just delete characters in the style buffer... 00292 editor->mStyleBuffer->remove(pos, pos + nDeleted); 00293 } 00294 00295 // Select the area that was just updated to avoid unnecessary 00296 // callbacks... 00297 editor->mStyleBuffer->select(pos, pos + nInserted - nDeleted); 00298 00299 // Re-parse the changed region; we do this by parsing from the 00300 // beginning of the line of the changed region to the end of 00301 // the line of the changed region... Then we check the last 00302 // style character and keep updating if we have a multi-line 00303 // comment character... 00304 start = editor->mBuffer->line_start(pos); 00305 end = editor->mBuffer->line_end(pos + nInserted); 00306 text = editor->mBuffer->text_range(start, end); 00307 style = editor->mStyleBuffer->text_range(start, end); 00308 if (start==end) 00309 last = 0; 00310 else 00311 last = style[end - start - 1]; 00312 00313 style_parse(text, style, end - start); 00314 00315 editor->mStyleBuffer->replace(start, end, style); 00316 editor->redisplay_range(start, end); 00317 00318 if (start==end || last != style[end - start - 1]) { 00319 // The last character on the line changed styles, so reparse the 00320 // remainder of the buffer... 00321 free(text); 00322 free(style); 00323 00324 end = editor->mBuffer->length(); 00325 text = editor->mBuffer->text_range(start, end); 00326 style = editor->mStyleBuffer->text_range(start, end); 00327 00328 style_parse(text, style, end - start); 00329 00330 editor->mStyleBuffer->replace(start, end, style); 00331 editor->redisplay_range(start, end); 00332 } 00333 00334 free(text); 00335 free(style); 00336 } 00337 00338 int CodeEditor::auto_indent(int, CodeEditor* e) { 00339 if (e->buffer()->selected()) { 00340 e->insert_position(e->buffer()->primary_selection()->start()); 00341 e->buffer()->remove_selection(); 00342 } 00343 00344 int pos = e->insert_position(); 00345 int start = e->line_start(pos); 00346 char *text = e->buffer()->text_range(start, pos); 00347 char *ptr; 00348 00349 for (ptr = text; isspace(*ptr); ptr ++); 00350 *ptr = '\0'; 00351 if (*text) { 00352 // use only a single 'insert' call to avoid redraw issues 00353 int n = strlen(text); 00354 char *b = (char*)malloc(n+2); 00355 *b = '\n'; 00356 strcpy(b+1, text); 00357 e->insert(b); 00358 free(b); 00359 } else { 00360 e->insert("\n"); 00361 } 00362 e->show_insert_position(); 00363 e->set_changed(); 00364 if (e->when()&FL_WHEN_CHANGED) e->do_callback(); 00365 00366 free(text); 00367 00368 return 1; 00369 } 00370 00371 // Create a CodeEditor widget... 00372 CodeEditor::CodeEditor(int X, int Y, int W, int H, const char *L) : 00373 Fl_Text_Editor(X, Y, W, H, L) { 00374 buffer(new Fl_Text_Buffer); 00375 00376 char *style = new char[mBuffer->length() + 1]; 00377 char *text = mBuffer->text(); 00378 00379 memset(style, 'A', mBuffer->length()); 00380 style[mBuffer->length()] = '\0'; 00381 00382 highlight_data(new Fl_Text_Buffer(mBuffer->length()), styletable, 00383 sizeof(styletable) / sizeof(styletable[0]), 00384 'A', style_unfinished_cb, this); 00385 00386 style_parse(text, style, mBuffer->length()); 00387 00388 mStyleBuffer->text(style); 00389 delete[] style; 00390 free(text); 00391 00392 mBuffer->add_modify_callback(style_update, this); 00393 add_key_binding(FL_Enter, FL_TEXT_EDITOR_ANY_STATE, 00394 (Fl_Text_Editor::Key_Func)auto_indent); 00395 } 00396 00397 // Destroy a CodeEditor widget... 00398 CodeEditor::~CodeEditor() { 00399 Fl_Text_Buffer *buf = mStyleBuffer; 00400 mStyleBuffer = 0; 00401 delete buf; 00402 00403 buf = mBuffer; 00404 buffer(0); 00405 delete buf; 00406 } 00407 00408 00409 CodeViewer::CodeViewer(int X, int Y, int W, int H, const char *L) 00410 : CodeEditor(X, Y, W, H, L) 00411 { 00412 default_key_function(kf_ignore); 00413 remove_all_key_bindings(&key_bindings); 00414 cursor_style(CARET_CURSOR); 00415 } 00416 00417 // 00418 // End of "$Id: CodeEditor.cxx 7903 2010-11-28 21:06:39Z matt $". 00419 //