/* * Implementation of sted.h. */ #include "sted.h" #include "browser.h" /* * Static member allocation. */ /* Dont need the staticness, we loop outside instead */ /* bool Sted::readonly; */ /* * 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, int highlight, bool readonly) { editor = new TextEditor(rows, cols, tabsize, highlight); Init(NULL, readonly); } /* * 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, int h, bool readonly) { editor = new TextEditor(name, r, c, t, h); Init(name, readonly); } #ifdef BROWSER /* * Forms 3 and 4 of the constructor Forms 1 and 2, resp., with the addition of * a parent context pointer. */ Sted::Sted(int rows, int cols, int tabsize, int highlight, bool readonly, RSLBrowser* browser) { editor = new TextEditor(rows, cols, tabsize, highlight); Init(NULL, readonly); this->browser = browser; } Sted::Sted(const char* name, int r, int c, int t, int h, bool readonly, RSLBrowser* browser) { editor = new TextEditor(name, r, c, t, h); Init(name, readonly); this->browser = browser; } #endif /* * 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, bool readonly) { /*** 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; filename = 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( new UpMover(editor, 1), new HBorder(), new VScroller(editor), new HBorder(), 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; this->readonly = readonly; 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); } /* * Save the current open file */ bool Sted::Save() { if (filename) { FILE* f = fopen(filename, "w"); if (! f) return false; const char* tempfile = text->Text(); int numofchars = text->Length(); for (int i=0; numofchars; i++, numofchars--) fputc(tempfile[i],f); fclose(f); modified = false; return true; } return false; } bool Sted::SaveAs(const char* filename) { if (this->filename) delete this->filename; this->filename = newstr(filename); if (Save()) { Edit(filename); return true; } return false; } /* * 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; } /* * Return the value of the modified flag. */ bool Sted::IsModified() { return modified; } /* * 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; if (filename) delete filename; filename = newstr(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) && (! readonly)) { if (editor->Dot() != editor->Mark()) { editor->DeleteSelection(); } else { editor->DeleteText(atoi(parameter)); } modified = true; } else if ((strcmp(operation, "insert-character") == 0) && (! readonly)) { 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, "save") == 0) { Save(); ***/ } 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) { #ifdef BROWSER browser->UpdateEdDuringEdit(filename); #endif HandleChar(e.keystring[0]); } else if (e.eventType == DownEvent) { #ifdef BROWSER browser->UpdateEdDuringEdit(filename); #endif 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); }