/* * Implementation of sted.h. */ #include "sted.h" /* * Form 1 of the constructor builds the TextEditor, and calls Init to do the * rest. No file is opened initally. */ Sted::Sted(int rows, int cols, int tabsize = 8, int highlight = Reversed) { editor = new TextEditor(rows, cols, tabsize, Reversed); Init(NULL); } /* * Form 2 of the construtor builds the TextEditor, and then calls Init to do * the rest, including opening the given file */ Sted::Sted(const char* name, int r, int c, int t = 8, int h = Reversed) { editor = new TextEditor(name, r, c, t, Reversed); Init(name); } /* * 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 Sted::Init (const char* name) { /*** sted = s; ***/ 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; Insert( new VBox( new HBox( new HGlue(5, 0, 0), new VBox( new VGlue(3, 0, 0), editor, new VGlue(3, 0, 0) ), new HGlue(5, 0, 0), new VBorder, new VBox( upmover = new UpMover(editor, 1), new HBorder(), scroller = new VScroller(editor), new HBorder(), downmover = new DownMover(editor, 1) ) ), new HBorder, new VGlue(2, 0, 0), new HBox( new HGlue(5, 0, 0), command, new HGlue(5, 0, 0) ), new VGlue(2, 0, 0) ) ); prefix1 = false; prefix2 = false; Edit(name); } /* * Nuke the text buffer and editor and we're outta here. */ Sted::~Sted () { /*** sted->Close(filename); ***/ delete text; delete buffer; state->Detach(this); Unref(state); } /* * Open a file by pasing the buck to Edit. */ void Sted::Open(const char* filename) { Edit(filename); } /* * Return the name of the currently open file, if any. */ const char* Sted::GetFilename () { return filename; } /* * Return a pointer to the contained TextBuffer, so the caller can get at the * text. */ TextBuffer* Sted::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 Sted::Edit (const char* name) { delete buffer; delete text; 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 Sted::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 Sted::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) { sted->Quit(); } else if (strcmp(operation, "close") == 0) { sted->Close(filename); } else if (strcmp(operation, "visit") == 0) { sted->Open(parameter); } else if (strcmp(operation, "file") == 0) { sted->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 { /*** sted-> ***/Complain(); } } else if (strcmp(operation, "goto") == 0) { editor->Select(text->LineIndex(atoi(parameter))); } else { /*** sted-> ***/Complain(); } editor->ScrollToSelection(); command->Message(""); } /* * Handle input from the one-line command window at the bottom of the edit * window. */ void Sted::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 Sted::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 Sted::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 Sted::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 Sted::Complain () { GetWorld()->RingBell(1); }