// ------------------------------------------------------------------ // GoldED+ // Copyright (C) 1990-1999 Odinn Sorensen // Copyright (C) 1999-2000 Alexander S. Aganichev // ------------------------------------------------------------------ // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, // MA 02111-1307 USA // ------------------------------------------------------------------ // $Id$ // ------------------------------------------------------------------ // The Internal Editor (IE), part 2. // ------------------------------------------------------------------ #include #include #include #include #if defined(__USE_ALLOCA__) #include #endif // ------------------------------------------------------------------ // Constructor IEclass::IEclass(int __scol, int __ecol, int __srow, int __erow, int __border) { win_mincol = __scol; win_minrow = __srow; win_maxcol = __ecol; win_maxrow = __erow; win_border = __border; win_hasborder = (win_border == 5) ? 0 : 1; mincol = 0; minrow = 0; maxcol = win_maxcol - win_mincol - (2*win_hasborder); maxrow = win_maxrow - win_minrow - (2*win_hasborder); col = 0; row = 0; ccol = 0; crow = 0; chartyped = false; currline = NULL; done = NO; insert = YES; drawlines = 0; drawflag = true; marginquotes = 0; margintext = 0; msgmode = 0; msgptr = NULL; quitnow = NO; thisrow = 0; unfinished = "+$!$+ GoldED Internal Editor: Unfinished Message!"; blockcol = -1; selecting = NO; throw_new(Undo = new UndoStack(this)); windowopen(); } // ------------------------------------------------------------------ // Destructor IEclass::~IEclass() { throw_delete(Undo); windowclose(); } // ------------------------------------------------------------------ void IEclass::windowopen() { // Open editor window without clearing the window area #define STYLE_NOCLEAR -1 int _tmp = gwin.style; gwin.style = STYLE_NOCLEAR; editwin.open(win_minrow, win_mincol, win_maxrow, win_maxcol, 5, C_READB, C_READW, C_READPB); gwin.style = _tmp; whelppcat(H_Editor); } // ------------------------------------------------------------------ void IEclass::windowclose() { whelpop(); // Close editor window without removing the window itself editwin.unlink(); } // ------------------------------------------------------------------ void Edit__killpastebuf() { while(Edit__pastebuf) { if(Edit__pastebuf->next) { Edit__pastebuf = Edit__pastebuf->next; throw_xdelete(Edit__pastebuf->prev); } else throw_xdelete(Edit__pastebuf); } } // ------------------------------------------------------------------ void Edit__killkillbuf() { while(Edit__killbuf) { if(Edit__killbuf->prev) { Edit__killbuf = Edit__killbuf->prev; throw_xdelete(Edit__killbuf->next); } else throw_xdelete(Edit__killbuf); } } // ------------------------------------------------------------------ void IEclass::killkillbuf() { while(Edit__killbuf) { if(Undo->FixPushLine(Edit__killbuf)) { if(Edit__killbuf->prev) { Edit__killbuf = Edit__killbuf->prev; Edit__killbuf->next = NULL; } else { Edit__killbuf = NULL; } } else { if(Edit__killbuf->prev) { Edit__killbuf = Edit__killbuf->prev; throw_xdelete(Edit__killbuf->next); } else throw_xdelete(Edit__killbuf); } } } // ------------------------------------------------------------------ void FreePastebuf() { Edit__killpastebuf(); Edit__killkillbuf(); } // ------------------------------------------------------------------ void IEclass::ClearDeleteBuf() { GFTRK("EditClearDeleteBuf"); killkillbuf(); HandleGEvent(EVTT_JOBDONE); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::ClearPasteBuf() { GFTRK("EditClearPasteBuf"); killpastebuf(); HandleGEvent(EVTT_JOBDONE); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::GoBegLine() { GFTRK("EditGoBegLine"); col = mincol; if(blockcol != -1) displine(currline, row); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::DelLtWord() { GFTRK("EditDelLtWord"); if(col == 0) { DelLeft(); GFTRK(NULL); return; } int _ptr, _ptr2; _ptr = _ptr2 = col; _ptr--; // test test test test test test test test // test test test test , test test test test // test test test test ,, test test test test if(currline->txt[_ptr] == ' ') { while((currline->txt[_ptr] == ' ') and (_ptr > 0)) _ptr--; if(currline->txt[_ptr] != ' ') _ptr++; } else if(isxalnum(currline->txt[_ptr])) { while(isxalnum(currline->txt[_ptr]) and (_ptr > 0)) _ptr--; while((currline->txt[_ptr] == ' ') and (_ptr > 0)) _ptr--; if((currline->txt[_ptr] != ' ') and (_ptr > 0)) _ptr++; } else { DelLeft(); GFTRK(NULL); return; } col -= _ptr2-_ptr; Undo->PushItem(EDIT_UNDO_DEL_TEXT, currline, col, _ptr2-_ptr); currline->txt.erase(_ptr, _ptr2-_ptr); wrapdel(&currline, &col, &row); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::DelRtWord() { GFTRK("EditDelRtWord"); if((currline->txt.length() == col+1) or (currline->txt[col+1] == '\n')) { DelChar(); GFTRK(NULL); return; } int _ptr, _ptr2; _ptr = _ptr2 = col; // test test test test, test test test test // test test test test, test test test test if(currline->txt[_ptr] == ' ') { while(_ptr != currline->txt.length() and currline->txt[_ptr] == ' ') _ptr++; } else if(isxalnum(currline->txt[_ptr])) { // Delete word while(_ptr != currline->txt.length() and isxalnum(currline->txt[_ptr])) _ptr++; // Delete spaces after word while(_ptr != currline->txt.length() and currline->txt[_ptr] == ' ') _ptr++; } else { DelChar(); GFTRK(NULL); return; } Undo->PushItem(EDIT_UNDO_DEL_TEXT, currline, col, _ptr-_ptr2); currline->txt.erase(_ptr2, _ptr-_ptr2); wrapdel(&currline, &col, &row); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::GoTopMsg() { GFTRK("EditGoTopMsg"); currline = findfirstline(); col = mincol; row = minrow; thisrow = 0; refresh(currline, minrow); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::GoBotMsg() { GFTRK("EditGoBotMsg"); col = mincol; thisrow = 0; currline = findfirstline(); // Go to the last line in the msg while(currline->next) { currline = currline->next; thisrow++; } // Pointer to the line to display at the top of the window Line* _topline = currline; // The new cursor row row = MinV(thisrow, maxrow); // How many lines to go back to get the top line int _count = row; // Go back to get the top line while(_count-- and _topline->prev) _topline = _topline->prev; // Refresh the display refresh(_topline, minrow); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::GoTopLine() { GFTRK("EditGoTopLine"); int _count = row; while(_count-- and currline->prev) { currline = currline->prev; thisrow--; } col = mincol; row = minrow; if(blockcol != -1) refresh(currline, minrow); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::GoBotLine() { GFTRK("EditGoBotLine"); Line *_oldcurrline = currline; int _oldrow = row; while((row < maxrow) and currline->next) { currline = currline->next; thisrow++; row++; } col = mincol; if(blockcol != -1) refresh(_oldcurrline, _oldrow); GFTRK(NULL); } // ------------------------------------------------------------------ Line* IEclass::findanchor() { GFTRK("Editfindanchor"); // Rewind to the first line Line* _anchor = findfirstline(); // Search all lines to find the anchor (a line with a block mark) while(not (_anchor->type & GLINE_BLOK) and _anchor->next) _anchor = _anchor->next; GFTRK(NULL); // Return pointer to the anchor line or NULL if no anchor was found return (_anchor->type & GLINE_BLOK) ? _anchor : (Line*)NULL; } // ------------------------------------------------------------------ void IEclass::BlockAnchor() { GFTRK("EditBlockAnchor"); Line* _anchor = findanchor(); // Is there an anchor already? if(_anchor) { // Yes, so replace it with the current line // Remove block mark _anchor->type &= ~GLINE_BLOK; blockcol = -1; // Is the old anchor different from the current line? if(_anchor != currline) { // Set the new anchor _anchor = currline; _anchor->type |= GLINE_BLOK; blockcol = col; // Find the line at the top Line* _topline = findtopline(); // Refresh display to remove the block color on the old anchor line // Just in case the old anchor line is visible refresh(_topline, minrow); } // Remove the old contents of the paste buffer killpastebuf(); } else { for(Line* _line = findfirstline(); _line; _line = _line->next) _line->type &= ~GLINE_BLOK; // There was no anchor, so mark the current line as the new anchor currline->type |= GLINE_BLOK; blockcol = col; // Refresh the display Line* _topline = findtopline(); refresh(_topline, minrow); } displine(currline, row); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::BlockCopy() { GFTRK("EditBlockCopy"); // Find the anchor, if any Line* _anchor = findanchor(); // Did we find the anchor? if(_anchor) { Line* _firstcopyline = currline; Line* _lastcopyline = currline; int firstcol = col, lastcol = col; // Search below to find the anchor line while(_lastcopyline->next and (_lastcopyline != _anchor)) _lastcopyline = _lastcopyline->next; // Was the anchor line above or on the current line? if(_lastcopyline != _anchor) { // The last copy line is the current line _lastcopyline = currline; // Search above to find the anchor line while(_firstcopyline->prev and (_firstcopyline != _anchor)) _firstcopyline = _firstcopyline->prev; firstcol = blockcol; } else { if(currline != _anchor or blockcol > col) lastcol = blockcol; else firstcol = blockcol; } // The _firstcopyline and _lastcopyline pointers // are now pointing where they should // Remove the old paste buffer killpastebuf(); // Pointer to the previous line in the paste buffer Line* _prevline = NULL; // Copy lines to the paste buffer while(1) { // Allocate a new line Line* _copyline; // Copy text and type if(_prevline == NULL) _copyline = new Line(_firstcopyline->txt.c_str() + firstcol); else _copyline = new Line(_firstcopyline->txt.c_str()); throw_xnew(_copyline); if(_firstcopyline == _lastcopyline) { if(_prevline) _copyline->txt.erase(lastcol); else _copyline->txt.erase(lastcol-firstcol); } _copyline->type = _firstcopyline->type & ~GLINE_BLOK; // Link in the new line _copyline->prev = _prevline; if(_prevline) _prevline->next = _copyline; _copyline->next = NULL; // Point the paste buffer to the first line of the copy if(Edit__pastebuf == NULL) Edit__pastebuf = _copyline; // Break out of the loop if the last line was copied if(_firstcopyline == _lastcopyline) break; // Keep pointer to the new line _prevline = _copyline; // Continue with the next line _firstcopyline = _firstcopyline->next; } selecting = NO; blockcol = -1; for(Line* _line = findfirstline(); _line; _line = _line->next) _line->type &= ~GLINE_BLOK; // Refresh display to remove the block color Line* _topline = findtopline(); refresh(_topline, minrow); Buf2Clip(); } killpastebuf(); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::BlockDel(Line* anchor) { GFTRK("EditBlockDel"); Line* firstcutline = currline; Line* lastcutline = currline; uint firstcutlinerow = row; uint lastcutlinerow = row; int firstcol = col, lastcol = col; // Search below to find the anchor line while(lastcutline->next and (lastcutline != anchor)) { lastcutline = lastcutline->next; lastcutlinerow++; } // Was the anchor line above or on the current line? if(lastcutline != anchor) { // The last cut line is the current line lastcutline = currline; lastcutlinerow = row; // Search above to find the anchor line while(firstcutline->prev and (firstcutline != anchor)) { firstcutline = firstcutline->prev; if(firstcutlinerow) firstcutlinerow--; } firstcol = blockcol; } else { if((currline != anchor) or (blockcol > col)) lastcol = blockcol; else firstcol = blockcol; } // The firstcutline and lastcutline pointers // are now pointing where they should if(firstcutline != lastcutline) { size_t __len = firstcutline->txt.length(); firstcutline->txt += lastcutline->txt.c_str()+lastcol; // We need to set up prow to prevent cosmetic bugs uint __prow = prow; getthisrow(firstcutline); prow = thisrow; getthisrow(currline); Undo->PushItem(EDIT_UNDO_INS_TEXT, firstcutline, __len); prow = __prow; Undo->PushItem(EDIT_UNDO_DEL_TEXT|BATCH_MODE, firstcutline, firstcol, __len-firstcol); firstcutline->txt.erase(firstcol, __len-firstcol); } else { Undo->PushItem(EDIT_UNDO_DEL_TEXT, firstcutline, firstcol, lastcol-firstcol); firstcutline->txt.erase(firstcol, lastcol-firstcol); } setlinetype(firstcutline); firstcutline->type &= ~GLINE_BLOK; blockcol = -1; currline = firstcutline; row = firstcutlinerow; col = firstcol; if(firstcutline != lastcutline) { do { Undo->PushItem(EDIT_UNDO_DEL_LINE|BATCH_MODE, firstcutline = firstcutline->next); } while(firstcutline != lastcutline); currline->next = lastcutline->next; if(lastcutline->next) lastcutline->next->prev = currline; } // Refresh the display if(not RngV(row, minrow, maxrow)) { row = minrow; wrapdel(&currline, &col, &row, false); refresh(currline, minrow); } else { row = MaxV(firstcutlinerow, minrow); Line* topline = findtopline(); wrapdel(&currline, &col, &row, false); refresh(topline, minrow); } Line* line; for(line = findfirstline(); line; line = line->next) line->type &= ~GLINE_BLOK; selecting = NO; GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::BlockCut(bool just_delete) { GFTRK("EditBlockCut"); // Find the anchor, if any Line* _anchor = findanchor(); // Did we find the anchor? if(_anchor) { int _blockcol = blockcol; if(not just_delete) BlockCopy(); blockcol = _blockcol; BlockDel(_anchor); } else killpastebuf(); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::BlockPaste() { GFTRK("EditBlockPaste"); killpastebuf(); Clip2Buf(); if(Edit__pastebuf) { Line* _pasteline = Edit__pastebuf; if(not batch_mode) Undo->PushItem(EDIT_UNDO_VOID); // For each of the lines in the paste buffer while(_pasteline) { uint curlen = currline->txt.length(); uint pastelen = _pasteline->txt.length(); if(col > curlen) col = curlen; if(_pasteline->txt.find('\n') != _pasteline->txt.npos) { // append to current line Undo->PushItem(EDIT_UNDO_DEL_TEXT|BATCH_MODE, currline, col); Line* _newline = insertlinebelow(currline, currline->txt.c_str()+col, BATCH_MODE); currline->txt.erase(col); currline->txt += _pasteline->txt; Undo->PushItem(EDIT_UNDO_INS_TEXT|BATCH_MODE, currline, col, pastelen); setlinetype(currline); col = currline->txt.length(); wrapins(&currline, &col, &row, false); currline = _newline; col = 0; if(row < maxrow) row++; } else { // insert into current line currline->txt.insert(col, _pasteline->txt); Undo->PushItem(EDIT_UNDO_INS_TEXT|BATCH_MODE, currline, col, pastelen); col += pastelen; wrapins(&currline, &col, &row, false); } setlinetype(currline); // Continue with the next line in the paste buffer _pasteline = _pasteline->next; } selecting = NO; blockcol = -1; for(Line* _line = findfirstline(); _line; _line = _line->next) _line->type &= ~GLINE_BLOK; // Refresh the display Line* _topline = findtopline(); refresh(_topline, minrow); } GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::LoadFile() { GFTRK("EditLoadFile"); // Open the file to load FILE* _fp = fsopen(AddPath(CFG->goldpath, EDIT->File()), "rb", CFG->sharemode); if(_fp) { XlatName __oldxlatimport; // Pop up a wait window cursoroff(); w_info(LNG->Wait); throw_delete(Undo); // Find the first line Line* _line = findfirstline(); // Remove all lines while(_line) { Line* _nextline = _line->next; throw_xdelete(_line); _line = _nextline; } // Remove message text and reset pointers throw_release(msgptr->txt); currline = msgptr->lin = NULL; // Allocate space for new message text msgptr->txt = (char*)throw_calloc(1, (uint)(fsize(_fp)+256)); // Eat the backup marking line char _buf[EDIT_BUFLEN]; fgets(_buf, sizeof(_buf), _fp); if(not striinc(unfinished, _buf)) rewind(_fp); // Load the file and close it fread(msgptr->txt, 1, (uint)fsize(_fp), _fp); fclose(_fp); // Save current charset strcpy(__oldxlatimport, AA->Xlatimport()); AA->SetXlatimport(CFG->xlatlocalset); // Index message text msgptr->TextToLines(margintext-1, false); _line = currline = msgptr->lin; // Restore charset AA->SetXlatimport(__oldxlatimport); // Change lines to internal editor format while(_line) { strtrim(_line->txt); if(_line->type & GLINE_HARD) _line->txt += "\n"; else _line->txt += " "; _line = _line->next; } // Remove the wait window w_info(NULL); // Display the loaded message Line* l = findfirstline(); uint i; for(i=0; l->next and inext; refresh(l, minrow); for(i=0; l->next and inext; if(l->next == NULL) row = i; currline = l; col = mincol; throw_new(Undo = new UndoStack(this)); } GFTRK(NULL); } // ------------------------------------------------------------------ inline char uuencode_enc(int c) { return (char)(c ? (c & 077) + ' ': '`'); } void IEclass::editimport(Line* __line, char* __filename, bool imptxt) { XlatName __oldxlatimport; GFTRK("Editimport"); // Save the unfinished msg first of all savefile(MODE_UPDATE); update_statusline(LNG->ImportFile); // Set initial import filename or wildcards if(__filename) { AA->SetInputfile(__filename); } else { if(*AA->Inputfile() == NUL) AA->SetInputfile("*"); } strcpy(__oldxlatimport, AA->Xlatimport()); AA->SetXlatimport(CFG->xlatlocalset); strcpy(CFG->inputfile, AA->Inputfile()); int ImportMode; if(imptxt) ImportMode = 0; else { GMenuImportTxt MenuImportTxt; ImportMode = MenuImportTxt.Run(); } std::string filenamebuf; Path tmpfile; bool isPipe = NO; bool fileselected = false; // Should the imported text be quoted or uuencoded? #define quoteit (ImportMode == 1) #define uuencode (ImportMode == 2) #define base64 (ImportMode == 3) #define getclip (ImportMode == 4) #define binary (uuencode or base64) if(in_range(ImportMode, 0, 3) and edit_pathname(CFG->inputfile, sizeof(Path), LNG->ImportWhichFile, H_ImportFile)) { AA->SetInputfile(CFG->inputfile); // Pointer to the filename string filenamebuf = AA->Inputfile(); if(filenamebuf.c_str()[0] == '|'){ Path cmdline; isPipe = YES; mktemp(strxcpy(tmpfile, AddPath(CFG->temppath, "GIXXXXXX"), sizeof(Path))); strxmerge(cmdline, sizeof(Path), filenamebuf.c_str()+1, " > ", tmpfile, NULL); ShellToDos(cmdline, "", BLACK|_BLACK, NO); filenamebuf = tmpfile; fileselected = true; } else { // Check for wildcards // Is the filename a directory? if(is_dir(filenamebuf)) { // Does the filename contain wildcards? if(not strpbrk(filenamebuf.c_str(), "*?")) { // Add match-all wildcards AddBackslash(filenamebuf); filenamebuf += "*"; } } fileselected = true; // Does the filename contain wildcards? if(strpbrk(filenamebuf.c_str(), "*?")) { // Set selection window title and statusline set_title(LNG->ImportTitle, TCENTER, C_MENUT); update_statuslinef(LNG->ImportStatus, "ST_IMPORTSTATUS", filenamebuf.c_str()); // Start the file picker fileselected = wpickfile(win_minrow, win_mincol, win_maxrow, win_maxcol, W_BMENU, C_MENUB, C_MENUW, C_MENUS, NO, filenamebuf, maketitle_and_status); } } } if(fileselected or getclip) { // Open the file/clipboard FILE* fp = NULL; gclipbrd clipbrd; if(getclip) filenamebuf = CLIP_NAME; if(getclip ? clipbrd.openread() : (fp = fsopen(filenamebuf.c_str(), binary ? "rb" : "rt", CFG->sharemode))!=NULL) { if (isPipe) filenamebuf = AA->Inputfile(); const char *imp_filename = (getclip or isPipe) ? filenamebuf.c_str() : CleanFilename(filenamebuf.c_str()); // we need to truncate filename to prevent unpredictable results int delta = strlen(imp_filename) - margintext; if(delta > 0) { filenamebuf.erase(filenamebuf.length()-delta); imp_filename = (getclip or isPipe) ? filenamebuf.c_str() : CleanFilename(filenamebuf.c_str()); } update_statuslinef(LNG->ImportStatus, "ST_IMPORTSTATUS", filenamebuf.c_str()); // Allocate paragraph read buffer char* _parabuf = (char*)throw_malloc(EDIT_PARABUFLEN); if(__line->prev) Undo->PushItem(EDIT_UNDO_VOID|PREV_LINE|batch_mode, __line->prev); else Undo->PushItem(EDIT_UNDO_VOID|batch_mode, __line); batch_mode = BATCH_MODE; // Add import begin text, if any if (*CFG->importbegin) { sprintf(_parabuf, "%s\n", CFG->importbegin); strischg(_parabuf, "@file", imp_filename); TokenXlat(MODE_NEW, _parabuf, msgptr, msgptr, CurrArea); _parabuf[strlen(_parabuf)-1] = '\n'; _parabuf[margintext] = NUL; _parabuf[margintext-1] = '\n'; __line = insertlinebelow(__line, _parabuf); setlinetype(__line); } if(uuencode) { sprintf(_parabuf, "begin 644 %s\n", imp_filename); _parabuf[margintext] = NUL; _parabuf[margintext-1] = '\n'; __line = insertlinebelow(__line, _parabuf); setlinetype(__line); while(1) { char ibuf[80]; char* iptr = ibuf; char* optr = _parabuf; int n = fread(ibuf, 1, 45, fp); if(n < 45) memset(ibuf+n, 0, 45-n); *optr++ = uuencode_enc(n); for(int i=0; i> 2); *optr++ = uuencode_enc(((*iptr << 4) & 060) | ((iptr[1] >> 4) & 017)); *optr++ = uuencode_enc(((iptr[1] << 2) & 074) | ((iptr[2] >> 6) & 03)); *optr++ = uuencode_enc(iptr[2] & 077); } *optr++ = '\n'; *optr = NUL; __line = insertlinebelow(__line, _parabuf); // set type to text __line->type &= ~GLINE_ALL; if(n <= 0) break; } __line = insertlinebelow(__line, "end\n"); setlinetype(__line); } else if(base64) { base64_engine b64; sprintf(_parabuf, "Content-type: application/octet-stream; name=\"%s\"\n", imp_filename); strcpy(_parabuf+margintext-2, "\"\n"); __line = insertlinebelow(__line, _parabuf); setlinetype(__line); sprintf(_parabuf, "Content-transfer-encoding: base64\n"); __line = insertlinebelow(__line, _parabuf); setlinetype(__line); sprintf(_parabuf, "\n"); __line = insertlinebelow(__line, _parabuf); setlinetype(__line); for(;;) { char ibuf[80]; char* optr = _parabuf; int n = fread(ibuf, 1, 54, fp); optr = b64.encode(optr, ibuf, n); *optr++ = '\n'; *optr = NUL; __line = insertlinebelow(__line, _parabuf); setlinetype(__line); if(n <= 0) break; } } else { int tabsz = CFG->disptabsize ? CFG->disptabsize : 1; #if defined(__USE_ALLOCA__) char *spaces = (char*)alloca(tabsz+1); #else __extension__ char spaces[tabsz+1]; #endif memset(spaces, ' ', tabsz); spaces[tabsz] = NUL; int level = LoadCharset(AA->Xlatimport(), CFG->xlatlocalset); size_t buf_len = EDIT_PARABUFLEN; char* buf = (char*) throw_malloc(EDIT_PARABUFLEN); Line* saveline = __line->next; __line->next = NULL; // Read paragraphs while(getclip ? clipbrd.read(_parabuf, EDIT_PARABUFLEN-7) : fgets(_parabuf, EDIT_PARABUFLEN-7, fp)) { XlatStr(buf, _parabuf, level, CharTable); // Insert a quotestring if asked if(quoteit) strins(" > ", buf, 0); else { // Invalidate tearline if(not CFG->invalidate.tearline.first.empty()) doinvalidate(buf, CFG->invalidate.tearline.first.c_str(), CFG->invalidate.tearline.second.c_str(), true); // Invalidate originline if(not CFG->invalidate.origin.first.empty()) doinvalidate(buf, CFG->invalidate.origin.first.c_str(), CFG->invalidate.origin.second.c_str()); // Invalidate SEEN-BY's if(not CFG->invalidate.seenby.first.empty()) doinvalidate(buf, CFG->invalidate.seenby.first.c_str(), CFG->invalidate.seenby.second.c_str()); // Invalidate CC's if(not CFG->invalidate.cc.first.empty()) doinvalidate(buf, CFG->invalidate.cc.first.c_str(), CFG->invalidate.cc.second.c_str()); // Invalidate XC's if(not CFG->invalidate.xc.first.empty()) doinvalidate(buf, CFG->invalidate.xc.first.c_str(), CFG->invalidate.xc.second.c_str()); // Invalidate XP's if(not CFG->invalidate.xp.first.empty()) doinvalidate(buf, CFG->invalidate.xp.first.c_str(), CFG->invalidate.xp.second.c_str()); } size_t read_len = strlen(buf); // Replace tabs char *ht = buf; while((ht = strchr(ht, '\t')) != NULL) { int rposn = ht-buf; int rstart = rposn%tabsz+1; *ht = ' '; if(tabsz > rstart) { if((read_len + tabsz - rstart) >= (buf_len - 7)) { buf_len += tabsz; buf = (char*)throw_realloc(buf, buf_len); } strins(spaces+rstart, buf, rposn); } } // Copy the paragraph to the new line and retype it Line* _newline = __line = insertlinebelow(__line, buf); setlinetype(_newline); // If the paragraph is longer than one line uint _wrapmargin = (_newline->type & GLINE_QUOT) ? marginquotes : margintext; if(_newline->txt.length() >= _wrapmargin) { // Wrap it uint _tmpcol = 0; uint _tmprow = 0; _newline = wrapins(&_newline, &_tmpcol, &_tmprow, false); } __line = _newline; } while(__line->next) __line = __line->next; if(not __line->txt.empty() and (*(__line->txt.end()-1) != '\n')) { Undo->PushItem(EDIT_UNDO_INS_CHAR|BATCH_MODE, __line, __line->txt.length()); __line->txt += '\n'; // Wrap it uint _tmpcol = 0; uint _tmprow = 0; __line = wrapins(&__line, &_tmpcol, &_tmprow, false); } __line->next = saveline; if(saveline) saveline->prev = __line; throw_free(buf); } // Add import end text, if any if (*CFG->importend or *CFG->importbegin) { sprintf(_parabuf, "%s\n", *CFG->importend ? CFG->importend : CFG->importbegin); strischg(_parabuf, "@file", imp_filename); TokenXlat(MODE_NEW, _parabuf, msgptr, msgptr, CurrArea); _parabuf[strlen(_parabuf)-1] = '\n'; _parabuf[margintext] = NUL; _parabuf[margintext-1] = '\n'; __line = insertlinebelow(__line, _parabuf); setlinetype(__line); } throw_free(_parabuf); if(getclip) clipbrd.close(); else fclose(fp); } else { w_infof(LNG->CouldNotOpen, filenamebuf.c_str()); waitkeyt(10000); w_info(NULL); } if(isPipe) unlink(tmpfile); } AA->SetXlatimport(__oldxlatimport); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::imptxt(char* __filename, bool imptxt) { GFTRK("Editimptxt"); msgptr->lin = findfirstline(); editimport(currline, __filename, imptxt); refresh(currline, row); col = mincol; GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::ImportText() { GFTRK("EditImportText"); imptxt(NULL); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::ImportQuotebuf() { GFTRK("EditImportQuotebuf"); Path _quotebuf; GetCurrQuotebuf(_quotebuf); imptxt(_quotebuf, true); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::editexport(Line* __exportline, int __endat) { GFTRK("Editexport"); update_statusline(LNG->ExportFile); if(edit_string(Edit__exportfilename, sizeof(Path), LNG->ExportWhatFile, H_ExportFile)) { // Pointer to export filename char* _filenameptr = Edit__exportfilename; // Is append requested? if(*_filenameptr == '+') _filenameptr++; FILE* _fp = fsopen(_filenameptr, (*Edit__exportfilename == '+') ? "at" : "wt", CFG->sharemode); if(_fp) { update_statuslinef(LNG->ExportStatus, "ST_EXPORTSTATUS", Edit__exportfilename); fputc('\n', _fp); while((__endat ? __exportline != currline : 1) and __exportline) { fwrite(__exportline->txt.c_str(), 1, __exportline->txt.length(), _fp); if(__exportline->txt.find('\n') == __exportline->txt.npos) fputc('\n', _fp); __exportline = __exportline->next; } fclose(_fp); } } GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::SpellCheck() { GFTRK("EditSpellCheck"); char _buf[EDIT_BUFLEN]; char _buf2[EDIT_BUFLEN]; savefile(MODE_SAVE); strcpy(_buf, EDIT->SpellChecker()); strcpy(_buf2, AddPath(CFG->goldpath, EDIT->File())); strchg(_buf2, GOLD_WRONG_SLASH_CHR, GOLD_SLASH_CHR); strischg(_buf, "@file", _buf2); sprintf(_buf2, LNG->SpellChecker, _buf); ShellToDos(_buf, _buf2, LGREY|_BLACK, YES); LoadFile(); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::ExportText() { GFTRK("EditExportText"); int endat = NO; // Line* exportline = findanchor(); // if(exportline) // endat = YES; // else Line* exportline = findfirstline(); editexport(exportline, endat); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::DosShell() { GFTRK("EditDosShell"); ShellToDos(getenv(GOLD_SHELL_ENV), LNG->DOS_Shell, LGREY|_BLACK, YES); cursoron(); cursoroff(); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::dispins() { GFTRK("Editdispins"); if(insert) HeaderView->window.prints(5, MAXCOL-6, C_HEADT, LNG->Ins); else { vchar _lbuf[6]; for(int c = 0; c < 5; c++) _lbuf[c] = _box_table(W_BHEAD,1); _lbuf[5] = NUL; HeaderView->window.printvs(5, MAXCOL-6, C_HEADB|ACSET, _lbuf); } GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::dispdl() { GFTRK("Editdispdl"); if (drawlines) { const char *lng = (drawlines == 1) ? LNG->DrawSL : LNG->DrawDL; HeaderView->window.prints(5, MAXCOL-12, C_HEADT, lng); } else { vchar _lbuf[6]; for(int c = 0; c < 5; c++) _lbuf[c] = _box_table(W_BHEAD,1); _lbuf[5] = NUL; HeaderView->window.printvs(5, MAXCOL-12, C_HEADB|ACSET, _lbuf); } GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::ToggleInsert() { GFTRK("EditToggleInsert"); insert = not insert; dispins(); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::ToggleDrawLines() { GFTRK("EditToggleDrawLines"); switch (drawlines) { case 0: drawlines = 1; break; case 1: drawlines = 2; break; case 2: drawlines = 0; break; } dispdl(); GFTRK(NULL); } // ------------------------------------------------------------------ static void ChrToLines(char chr, byte lines[4]) { lines[0] = lines[1] = lines[2] = lines[3] = 0; if (chr == _box_table(0, 1)) //hs lines[0] = lines[1] = 1; else if (chr == _box_table(1, 1)) //hd lines[0] = lines[1] = 2; else if (chr == _box_table(0, 3)) //vs lines[2] = lines[3] = 1; else if (chr == _box_table(1, 3)) //vd lines[2] = lines[3] = 2; else if (chr == _box_table(0, 0)) //lsus lines[1] = lines[3] = 1; else if (chr == _box_table(1, 0)) //ldud lines[1] = lines[3] = 2; else if (chr == _box_table(0, 11)) //vsus lines[0] = lines[1] = lines[3] = 1; else if (chr == _box_table(1, 11)) //vdud lines[0] = lines[1] = lines[3] = 2; else if (chr == _box_table(0, 2)) //rsus lines[0] = lines[3] = 1; else if (chr == _box_table(1, 2)) //rdud lines[0] = lines[3] = 2; else if (chr == _box_table(0, 9)) //lshs lines[1] = lines[2] = lines[3] = 1; else if (chr == _box_table(1, 9)) //ldhd lines[1] = lines[2] = lines[3] = 2; else if (chr == _box_table(0, 8)) //vshs lines[0] = lines[1] = lines[2] = lines[3] = 1; else if (chr == _box_table(1, 8)) //vdhd lines[0] = lines[1] = lines[2] = lines[3] = 2; else if (chr == _box_table(0, 10)) //rshs lines[0] = lines[2] = lines[3] = 1; else if (chr == _box_table(1, 10)) //rdhd lines[0] = lines[2] = lines[3] = 2; else if (chr == _box_table(0, 5)) //lsds lines[1] = lines[2] = 1; else if (chr == _box_table(1, 5)) //lddd lines[1] = lines[2] = 2; else if (chr == _box_table(0, 12)) //vsds lines[0] = lines[1] = lines[2] = 1; else if (chr == _box_table(1, 12)) //vddd lines[0] = lines[1] = lines[2] = 2; else if (chr == _box_table(0, 7)) //rsds lines[0] = lines[2] = 1; else if (chr == _box_table(1, 7)) //rddd lines[0] = lines[2] = 2; else if (chr == _box_table(2, 0)) //ldus { lines[1] = 1; lines[3] = 2; } else if (chr == _box_table(3, 0)) //lsud { lines[1] = 2; lines[3] = 1; } else if (chr == _box_table(2, 11)) //vdus { lines[0] = lines[1] = 1; lines[3] = 2; } else if (chr == _box_table(3, 11)) //vsud { lines[0] = lines[1] = 2; lines[3] = 1; } else if (chr == _box_table(2, 2)) //rdus { lines[0] = 1; lines[3] = 2; } else if (chr == _box_table(3, 2)) //rsud { lines[0] = 2; lines[3] = 1; } else if (chr == _box_table(2, 9)) //ldhs { lines[1] = 1; lines[2] = lines[3] = 2; } else if (chr == _box_table(3, 9)) //lshd { lines[1] = 2; lines[2] = lines[3] = 1; } else if (chr == _box_table(2, 8)) //vdhs { lines[0] = lines[1] = 1; lines[2] = lines[3] = 2; } else if (chr == _box_table(3, 8)) //vshd { lines[0] = lines[1] = 2; lines[2] = lines[3] = 1; } else if (chr == _box_table(2, 10)) //rdhs { lines[0] = 1; lines[2] = lines[3] = 2; } else if (chr == _box_table(3, 10)) //rshd { lines[0] = 2; lines[2] = lines[3] = 1; } else if (chr == _box_table(2, 5)) //ldds { lines[1] = 1; lines[2] = 2; } else if (chr == _box_table(3, 5)) //lsdd { lines[1] = 2; lines[2] = 1; } else if (chr == _box_table(2, 12)) //vdds { lines[0] = lines[1] = 1; lines[2] = 2; } else if (chr == _box_table(3, 12)) //vsdd { lines[0] = lines[1] = 2; lines[2] = 1; } else if (chr == _box_table(2, 7)) //rdds { lines[0] = 1; lines[2] = 2; } else if (chr == _box_table(3, 7)) //rsdd { lines[0] = 2; lines[2] = 1; } } // ------------------------------------------------------------------ static char LinesToChr(byte lines[4]) { if (lines[0] == 0) { if (lines[1] == 0) { if (lines[2] == 0) { if (lines[3] == 1) return _box_table(0, 3); //vs if (lines[3] == 2) return _box_table(1, 3); //vd } else if (lines[2] == 1) { if (lines[3] == 0) return _box_table(0, 3); //vs if (lines[3] == 1) return _box_table(0, 3); //vs } else if (lines[2] == 2) { if (lines[3] == 0) return _box_table(1, 3); //vd if (lines[3] == 2) return _box_table(1, 3); //vd } } else if (lines[1] == 1) { if (lines[2] == 0) { if (lines[3] == 0) return _box_table(0, 1); //hs if (lines[3] == 1) return _box_table(0, 0); //lsus if (lines[3] == 2) return _box_table(2, 0); //ldus } else if (lines[2] == 1) { if (lines[3] == 0) return _box_table(0, 5); //lsds if (lines[3] == 1) return _box_table(0, 9); //lshs } else if (lines[2] == 2) { if (lines[3] == 0) return _box_table(2, 5); //ldds if (lines[3] == 2) return _box_table(2, 9); //ldhs } } else if (lines[1] == 2) { if (lines[2] == 0) { if (lines[3] == 0) return _box_table(1, 1); //hd if (lines[3] == 1) return _box_table(3, 0); //lsud if (lines[3] == 2) return _box_table(1, 0); //ldud } else if (lines[2] == 1) { if (lines[3] == 0) return _box_table(3, 5); //lsdd if (lines[3] == 1) return _box_table(3, 9); //lshd } else if (lines[2] == 2) { if (lines[3] == 0) return _box_table(1, 5); //lddd if (lines[3] == 2) return _box_table(1, 9); //ldhd } } } else if (lines[0] == 1) { if (lines[1] == 0) { if (lines[2] == 0) { if (lines[3] == 0) return _box_table(0, 1); //hs if (lines[3] == 1) return _box_table(0, 2); //rsus if (lines[3] == 2) return _box_table(2, 2); //rdus } else if (lines[2] == 1) { if (lines[3] == 0) return _box_table(0, 7); //rsds if (lines[3] == 1) return _box_table(0, 10); //rshs } else if (lines[2] == 2) { if (lines[3] == 0) return _box_table(2, 7); //rdds if (lines[3] == 2) return _box_table(2, 10); //rdhs } } else if (lines[1] == 1) { if (lines[2] == 0) { if (lines[3] == 0) return _box_table(0, 1); //hs if (lines[3] == 1) return _box_table(0, 11); //vsus if (lines[3] == 2) return _box_table(2, 11); //vdus } else if (lines[2] == 1) { if (lines[3] == 0) return _box_table(0, 12); //vsds if (lines[3] == 1) return _box_table(0, 8); //vshs } else if (lines[2] == 2) { if (lines[3] == 0) return _box_table(2, 12); //vdds if (lines[3] == 2) return _box_table(2, 8); //vdhs } } } else if (lines[0] == 2) { if (lines[1] == 0) { if (lines[2] == 0) { if (lines[3] == 0) return _box_table(1, 1); //hd if (lines[3] == 1) return _box_table(3, 2); //rsud if (lines[3] == 2) return _box_table(1, 2); //rdud } else if (lines[2] == 1) { if (lines[3] == 0) return _box_table(3, 7); //rsdd if (lines[3] == 1) return _box_table(3, 10); //rshd } else if (lines[2] == 2) { if (lines[3] == 0) return _box_table(1, 7); //rddd if (lines[3] == 2) return _box_table(1, 10); //rdhd } } else if (lines[1] == 2) { if (lines[2] == 0) { if (lines[3] == 0) return _box_table(1, 1); //hd if (lines[3] == 1) return _box_table(3, 11); //vsud if (lines[3] == 2) return _box_table(1, 11); //vdud } else if (lines[2] == 1) { if (lines[3] == 0) return _box_table(3, 12); //vsdd if (lines[3] == 1) return _box_table(3, 8); //vshd } else if (lines[2] == 2) { if (lines[3] == 0) return _box_table(1, 12); //vddd if (lines[3] == 2) return _box_table(1, 8); //vdhd } } } return 0x20; } // ------------------------------------------------------------------ void IEclass::DrawLines(gkey key) { GFTRK("EditDrawLines"); static byte lines[4]; static int drawx; static int drawy; byte type1 = 1; byte type2 = 1; (drawlines == 1) ? type2 = 2 : type1 = 2; //------------------------- if (drawflag || chartyped) { ChrToLines(currline->txt[col], lines); switch (key) { case KK_EditGoRight: drawx = -1; drawy = 0; if ((lines[0] == type1) || (!lines[0] && (lines[2] || lines[3]))) drawx++; else if ((lines[0] == type2) || (lines[1] == type2)) { drawx++; lines[0] = lines[1] = 0; } break; case KK_EditGoLeft: drawx = +1; drawy = 0; if ((lines[1] == type1) || (!lines[1] && (lines[2] || lines[3]))) drawx--; else if ((lines[0] == type2) || (lines[1] == type2)) { drawx--; lines[0] = lines[1] = 0; } break; case KK_EditGoDown: drawx = 0; drawy = -1; if ((lines[2] == type1) || (!lines[2] && (lines[0] || lines[1]))) drawy++; else if ((lines[2] == type2) || (lines[3] == type2)) { drawy++; lines[2] = lines[3] = 0; } break; case KK_EditGoUp: drawx = 0; drawy = +1; if ((lines[3] == type1) || (!lines[3] && (lines[0] || lines[1]))) drawy--; else if ((lines[2] == type2) || (lines[3] == type2)) { drawy--; lines[2] = lines[3] = 0; } break; } drawflag = false; } //------------------------- bool gonext = true; switch (key) { case KK_EditGoRight: if (col >= (maxcol-1)) { lines[1] = 0; gonext = false; } if (!drawx && gonext) { drawx = -1; lines[1] = type1; if (lines[0] == type2) lines[0] = 0; } else if (drawx == 1) drawx = -1; else if (drawx == -1) { gonext = false; drawx++; lines[0] = type1; if (lines[1] == type2) lines[1] = 0; } if (gonext && drawy) { if (drawy == -1) { drawy++; lines[2] = type1; lines[0] = lines[3] = 0; } else //(drawy == 1) { drawy--; lines[3] = type1; lines[0] = lines[2] = 0; } } break; case KK_EditGoLeft: if (col <= mincol) { lines[0] = 0; gonext = false; } if (!drawx && gonext) { drawx = 1; lines[0] = type1; if (lines[1] == type2) lines[1] = 0; } else if (drawx == -1) drawx = 1; else if (drawx == 1) { gonext = false; drawx--; lines[1] = type1; if (lines[0] == type2) lines[0] = 0; } if (gonext && drawy) { if (drawy == -1) { drawy++; lines[2] = type1; lines[1] = lines[3] = 0; } else //(drawy == 1) { drawy--; lines[3] = type1; lines[1] = lines[2] = 0; } } break; case KK_EditGoDown: if (!currline->next) { lines[3] = 0; gonext = false; } if (!drawy && gonext) { drawy = -1; lines[3] = type1; if (lines[2] == type2) lines[2] = 0; } else if (drawy == 1) drawy = -1; else if (drawy == -1) { gonext = false; drawy++; lines[2] = type1; if (lines[3] == type2) lines[3] = 0; } if (gonext && drawx) { if (drawx == -1) { drawx++; lines[0] = type1; lines[1] = lines[2] = 0; } else //(drawx == 1) { drawx--; lines[1] = type1; lines[0] = lines[2] = 0; } } break; case KK_EditGoUp: if (!currline->prev) { lines[2] = 0; gonext = false; } if (!drawy && gonext) { drawy = 1; lines[2] = type1; if (lines[3] == type2) lines[3] = 0; } else if (drawy == -1) drawy = 1; else if (drawy == 1) { gonext = false; drawy--; lines[3] = type1; if (lines[2] == type2) lines[2] = 0; } if (gonext && drawx) { if (drawx == -1) { drawx++; lines[0] = type1; lines[1] = lines[3] = 0; } else //(drawx == 1) { drawx--; lines[1] = type1; lines[0] = lines[3] = 0; } } break; } //------------------------- char new_chr = LinesToChr(lines); if (new_chr != currline->txt[col]) { if (col < (currline->txt.length()-1)) { Undo->PushItem(EDIT_UNDO_OVR_CHAR); currline->txt[col] = new_chr; } else if (col < maxcol) { Undo->PushItem(EDIT_UNDO_INS_CHAR); currline->txt.insert(col, 1, new_chr); } setlinetype(currline); displine(currline, row); } //------------------------- if (gonext) { switch (key) { case KK_EditGoRight: GoRight(); break; case KK_EditGoLeft: GoLeft(); break; default: (key == KK_EditGoDown) ? GoDown() : GoUp(); if (col < pcol) { size_t len = pcol - col; Undo->PushItem(EDIT_UNDO_INS_TEXT|BATCH_MODE, currline, col, len); currline->txt.insert(col, len, ' '); GoEOL(); } break; } gotorowcol(col, row); ChrToLines(currline->txt[col], lines); switch (key) { case KK_EditGoRight: if ((drawx == -1) && (lines[0] == type1)) drawx++; break; case KK_EditGoLeft: if ((drawx == +1) && (lines[1] == type1)) drawx--; break; case KK_EditGoDown: if ((drawy == -1) && (lines[2] == type1)) drawy++; break; case KK_EditGoUp: if ((drawy == +1) && (lines[3] == type1)) drawy--; break; } } GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::Header() { GFTRK("EditHeader"); windowclose(); EditHeaderinfo(msgmode, *HeaderView); windowopen(); GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::Abort() { GFTRK("EditAbort"); cursoroff(); GMenuDropmsg MenuDropmsg; if(MenuDropmsg.Run()) { done = MODE_QUIT; } GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::AskExit() { GFTRK("EditAskExit"); cursoroff(); GMenuQuit MenuQuit; gkbd.quitall = make_bool(MenuQuit.Run()); if(gkbd.quitall) { GMenuDropmsg MenuDropmsg; if(MenuDropmsg.Run()) done = MODE_QUIT; else done = MODE_SAVE; } GFTRK(NULL); } // ------------------------------------------------------------------ void IEclass::QuitNow() { GFTRK("EditQuitNow"); quitnow = CFG->switches.get(timeoutsavemsg) ? NO : YES; done = CFG->switches.get(timeoutsavemsg) ? MODE_SAVE : MODE_QUIT; gkbd.quitall = YES; GFTRK(NULL); } // ------------------------------------------------------------------ int EditMsg(int __mode, uint* __position, GMsg* __msg) { IEclass Editor(0, MAXCOL-1, MINROW, MAXROW-2, 5); return Editor.Start(__mode, __position, __msg); } // ------------------------------------------------------------------