1453 lines
34 KiB
C++
1453 lines
34 KiB
C++
|
|
// ------------------------------------------------------------------
|
|
// 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 <golded.h>
|
|
#include <geedit.h>
|
|
#include <gutlcode.h>
|
|
#include <gutlclip.h>
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// 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;
|
|
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;
|
|
Undo->PushItem(EDIT_UNDO_INS_TEXT, firstcutline, __len);
|
|
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);
|
|
_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 i<thisrow-row; i++)
|
|
l = l->next;
|
|
|
|
refresh(l, minrow);
|
|
|
|
for(i=0; l->next and i<row; i++)
|
|
l = l->next;
|
|
|
|
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->goldpath, "GIXXXXXX"), sizeof(Path)));
|
|
strxmerge(cmdline, sizeof(Path), filenamebuf.c_str()+1, " > ", tmpfile, NULL);
|
|
ShellToDos(cmdline, "", NO, 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, 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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, 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);
|
|
_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<n; i+=3,iptr+=3) {
|
|
*optr++ = uuencode_enc(*iptr >> 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;
|
|
__extension__ char spaces[tabsz+1];
|
|
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);
|
|
_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, 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::ToggleInsert() {
|
|
|
|
GFTRK("EditToggleInsert");
|
|
|
|
insert = not insert;
|
|
dispins();
|
|
|
|
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 = 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);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|