/* * Implementation of fulltexteditor.h. */ #include "fulltexteditor.h" /* * Form 1 of the constructor builds the TextEditor, and calls Init to do the * rest. No file is opened initally. */ FullTextEditor::FullTextEditor( int rows, int cols, int tabsize, int highlight, bool scroll, bool cmdline, int caretstyle) { editor = new CaretModifiableTextEditor(rows, cols, tabsize, highlight); Init(NULL, scroll, cmdline, caretstyle); } /* * Form 2 of the construtor builds the TextEditor, and then calls Init to do * the rest, including opening the given file */ FullTextEditor::FullTextEditor( const char* name, int r, int c, int t, int h, bool scroll, bool cmdline, int caretstyle) { editor = new CaretModifiableTextEditor(name, r, c, t, h); Init(name, scroll, cmdline, caretstyle); } /* * Set up the event handling stuff, alloc the surrounding boxes and scrollbar, * and call Edit to open the given file in a TextBuffer. If file name is * null, an empty TextBuffer is opened. */ void FullTextEditor::Init (const char* name, bool scroll, bool cmdline, int caretstyle) { VBox* outer = new VBox; /* Contains editor and command line */ HBox* inner = new HBox; /* contains editor and scroll bar */ input = new Sensor(); input->Catch(KeyEvent); input->Catch(DownEvent); state = new ButtonState(false); state->Attach(this); command = new StringEditor(state, " "); buffer = nil; size = 0; text = nil; inner->Insert(new HGlue(5, 0, 0)); inner->Insert( new VBox( new VGlue(3, 0, 0), editor, new VGlue(3, 0, 0) ) ); if (scroll) { inner->Insert(new HGlue(5, 0, 0)); inner->Insert(new VBorder); inner->Insert( new VBox( new UpMover(editor, 1), new HBorder(), new VScroller(editor), new HBorder(), new DownMover(editor, 1) ) ); } outer->Insert(inner); if (cmdline) { outer->Insert(new HBorder); outer->Insert(new VGlue(2, 0, 0)); outer->Insert( new HBox( new HGlue(5, 0, 0), command, new HGlue(5, 0, 0) ) ); outer->Insert(new VGlue(2, 0, 0)); } Insert(outer); prefix1 = false; prefix2 = false; Edit(name); editor->CaretStyle(caretstyle); } /* * Nuke the text buffer and editor and we're outta here. */ FullTextEditor::~FullTextEditor () { delete text; delete buffer; state->Detach(this); Unref(state); } /* * Open a file by pasing the buck to Edit. */ void FullTextEditor::Open(const char* filename) { Edit(filename); } /* * Return the name of the currently open file, if any. */ const char* FullTextEditor::GetFilename () { return filename; } /* * Return a pointer to the contained TextBuffer, so the caller can get at the * text. */ TextBuffer* FullTextEditor::GetBuffer () { return text; } /* * Open the file of the given name, if any. Alloc the TextBuffer and stick the * file contents into it, via TextBuffer::Edit. */ void FullTextEditor::Edit (const char* name) { delete buffer; delete text; if (name) sprintf(filename, "%s", name); FILE* f = fopen(filename, "r"); if (f != nil) { struct stat filestats; stat(filename, &filestats); size = max(round(filestats.st_size * 1.2), MINTEXTSIZE); buffer = new char[size]; char* b = buffer; int remaining = size; while (remaining > 1 && fgets(b, remaining, f) != nil) { int l = strlen(b); remaining -= l; b += l; } fclose(f); text = new TextBuffer(buffer, b-buffer, size); command->Message(""); } else { size = MINTEXTSIZE; buffer = new char[size]; text = new TextBuffer(buffer, 0, size); command->Message("new file"); } editor->Edit(text); modified = false; SetName(filename); char* s = strrchr(filename, '/'); if (s != nil) { SetIconName(s+1); } else { SetIconName(filename); } } /* * Apparently unused specialization of Interactor::Update. */ void FullTextEditor::Update () { int value; state->GetValue(value); if (value == '\r') { Do(command->Text()); } state->SetValue('\0'); } /* * Do a specific editing command. For the most part, these are done by calling * TexeBuffer commands, through editor->. The primary call to Do comes from * HandleChar, defined below. */ void FullTextEditor::Do (const char* s) { static char operation[BUFFERSIZE]; static char parameter[BUFFERSIZE]; static char buffer[BUFFERSIZE]; strcpy(buffer, s); sscanf(buffer, "%s %s", operation, parameter); if (strcmp(operation, "backward-page") == 0) { editor->BackwardPage(atoi(parameter)); } else if (strcmp(operation, "forward-page") == 0) { editor->ForwardPage(atoi(parameter)); } else if (strcmp(operation, "backward-character") == 0) { editor->BackwardCharacter(atoi(parameter)); } else if (strcmp(operation, "forward-character") == 0) { editor->ForwardCharacter(atoi(parameter)); } else if (strcmp(operation, "backward-line") == 0) { editor->BackwardLine(atoi(parameter)); } else if (strcmp(operation, "forward-line") == 0) { editor->ForwardLine(atoi(parameter)); } else if (strcmp(operation, "beginning-of-text") == 0) { editor->BeginningOfText(); } else if (strcmp(operation, "end-of-text") == 0) { editor->EndOfText(); } else if (strcmp(operation, "beginning-of-line") == 0) { editor->BeginningOfLine(); } else if (strcmp(operation, "end-of-line") == 0) { editor->EndOfLine(); } else if (strcmp(operation, "delete-character") == 0) { if (editor->Dot() != editor->Mark()) { editor->DeleteSelection(); } else { editor->DeleteText(atoi(parameter)); } modified = true; } else if (strcmp(operation, "insert-character") == 0) { editor->DeleteSelection(); editor->InsertText(&lastchar, 1); modified = true; /*** *** Nuked from the original sted.c; reinstall if desired. *** } else if (strcmp(operation, "quit") == 0) { Quit(); } else if (strcmp(operation, "close") == 0) { Close(filename); } else if (strcmp(operation, "visit") == 0) { Open(parameter); } else if (strcmp(operation, "file") == 0) { Edit(parameter); ***/ } else if (strcmp(operation, "file") == 0) { Edit(parameter); } else if (strcmp(operation, "search") == 0) { Regexp re(parameter); if ( text->ForwardSearch(&re, editor->Dot()) >= 0 || text->ForwardSearch(&re, text->BeginningOfText()) >= 0 ) { editor->Select(re.EndOfMatch(), re.BeginningOfMatch()); } else { Complain(); } } else if (strcmp(operation, "goto") == 0) { editor->Select(text->LineIndex(atoi(parameter))); } else { Complain(); } editor->ScrollToSelection(); command->Message(""); } /* * Handle input from the one-line command window at the bottom of the edit * window. */ void FullTextEditor::Ask (const char* message, int begin, int end) { command->Edit(message, begin, end); } /* * Handle all Sted events. All keyboard events are passed on to HandleChar. * Left mouse event gets special treatment. Middle and right mouse events do * standard grab scrolling. */ void FullTextEditor::Handle (Event& e) { if (e.eventType == KeyEvent && e.len > 0) { HandleChar(e.keystring[0]); } else if (e.eventType == DownEvent) { switch (e.button) { case LEFTMOUSE: LeftMouse(e); break; case MIDDLEMOUSE: editor->GrabScroll(e); break; case RIGHTMOUSE: editor->RateScroll(e); break; } } } /* * Handle a single keystroke event. This is the function that actually * implements the emacs-like editing model. See "man sted" for details on the * key bindings of this model. */ void FullTextEditor::HandleChar (char c) { if (prefix1) { prefix1 = false; switch (c) { case 'v': Do("backward-page 1"); break; case 'x': Ask("", 0, 0); break; case '<': Do("beginning-of-text"); break; case '>': Do("end-of-text"); break; case '\r': Do(""); break; case '=': Ask("goto ", 5, 5); break; default: Do("complain"); break; } } else if (prefix2) { prefix2 = false; switch (c) { case '\006': Ask("file ", 5, 5); break; /*** Nuked from the original sted.c case '\026': Ask("visit ", 6, 6); break; case '\003': Ask("quit", 0, 4); break; case 'k': Ask("close", 0, 5); break; ***/ default: Do("complain"); break; } } else { switch (c) { case '\033': prefix1 = true; break; case '\030': prefix2 = true; break; case '\026': Do("forward-page 1"); break; case '\001': Do("beginning-of-line"); break; case '\005': Do("end-of-line"); break; case '\006': Do("forward-character 1"); break; case '\002': Do("backward-character 1"); break; case '\016': Do("forward-line 1"); break; case '\020': Do("backward-line 1"); break; case '\023': Ask("search ", 7, 7); break; case '\004': Do("delete-character 1"); break; case '\010': Do("delete-character -1"); break; case '\177': Do("delete-character -1"); break; case '\011': lastchar = '\t'; Do("insert-character"); break; case '\015': lastchar = '\n'; Do("insert-character"); break; case '\007': Do("complain"); break; default: if (!iscntrl(c)) { lastchar = c; Do("insert-character"); } else { Do("complain"); } break; } } } /* * Handle left mouse clicks and/or drags to perform buffer selections. */ void FullTextEditor::LeftMouse (Event& e) { GetRelative(e.x, e.y, editor); editor->Select(editor->Locate(e.x, e.y)); do { editor->ScrollToView(e.x, e.y); editor->SelectMore(editor->Locate(e.x, e.y)); Poll(e); GetRelative(e.x, e.y, editor); } while (e.leftmouse); } /* * Ring my chimes. */ void FullTextEditor::Complain () { GetWorld()->RingBell(1); } /* * Implementation of CaretModifiableTextEditor -- necessary nuisance that it * is. */ CaretModifiableTextEditor::CaretModifiableTextEditor( int rows, int cols, int tabsize, int highlight) : TextEditor(rows, cols, tabsize, highlight) {} CaretModifiableTextEditor::CaretModifiableTextEditor( const char* name, int rows, int cols, int tabsize, int highlight) : TextEditor(name, rows, cols, tabsize, highlight) {} void CaretModifiableTextEditor::CaretStyle(int style) { display->CaretStyle(style); }