/*
* recenteditor.c
*
* A programmers editor
*
* Author: Dario Rodriguez dario@softhome.net
* This program is licensed under the terms of GNU GPL v2.1+
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include "re_data.h"
#include "re_plugin_unsaved.h"
#include "re_plugin_highlighter.h"
#include "re_plugin_prototypes.h"
#include "re_ui.h"
#include "ext/socklib.h"
#define LINEFORCESCROLL 1
#define COLFORCESCROLL 5
#define IDLETIMEOUTSECONDS 10
#define COMMANDBUFSIZE 1024
#define DEFAULTFONTHEIGHT 14
#define SELECTBUFBLOCK 16384
#define SIZEBLOCKPRINTS 32
#define VIEWONLYPROGNAME "review"
#define COMMAND_WARNING "(!)"
#define COMMAND_INFO "(i)"
#define COMMAND_GOTOLINE "Go to line:"
#define COMMAND_SEARCHFORWARD "Search:"
#define COMMAND_REPLACEWHAT "Search for:"
#define COMMAND_REPLACEWITH "Replace with:"
#define COMMAND_REPLACEHOW "Replace options (Igncase Selected Backwards Entire All Noconfirm):"
#define COMMAND_CONFIRMEXIT "There is unsaved data. Please confirm exit with y+ENTER:"
#define COMMAND_QUESTION "(?)"
#define COMMAND_EXIT "Exit"
/* "forest" theme */
#define COLOR_STATUSBG "\x14\x3a\xaf\xff"
#define COLOR_STATUSFG "\xe6\xdc\x5d\xff"
#define COLOR_STATUSFGLIGHT "\x6f\x73\xa3\xff"
#define COLOR_QUERYBG "\xad\x92\x5e\xff"
#define COLOR_QUERYFG "\xd0\xef\x4f\xff"
#define COLOR_WARNINGBG "\xba\x07\x07\xff"
#define COLOR_WARNINGFG "\xe6\xdc\x5d\xff"
#define COLOR_INFOBG "\x4e\x8a\x4e\xff"
#define COLOR_INFOFG "\xee\xee\x46\xff"
#define COLOR_INFOFGLIGHT "\x84\xa4\x4c\xff"
typedef struct print_t {
reui_t *ui;
redata_t *data;
int originline;
int origincol;
int dirty;
} printout_t;
typedef struct re_t {
redata_t *data;
reui_t *ui;
int viewonly;
int flag_newfile;
char filename[PATH_MAX];
int x, y, w, h; // contents rect
int fontheightpercent;
int originline,origincol;
int curline,curcol;
int maxrow,maxcol;
int headerdirty;
int contentsdirty;
int command_first_key;
int selactive;
int sellinefrom,sellineto;
int selcolfrom,selcolto;
long sizeselectbuf;
long usedselectbuf;
char *selectbuf;
char *command;
char commandbuf[COMMANDBUFSIZE];
char cachelastsearch[COMMANDBUFSIZE];
char cachelastreplacewith[COMMANDBUFSIZE];
question_t *question;
int showingwarning;
int ignorenkeys;
int sizeprints;
int usedprints;
printout_t *prints;
} re_t;
volatile int flag_sigint;
volatile int flag_sigpipe;
static int setsignal(int num, void (*sighandler)(int));
static void sighandler_sigint(int num);
static void sighandler_sigpipe(int num);
static int mystricmp(const char *s1, const char *s2);
re_t *re_init(int viewonly);
void re_free(re_t *re);
int re_setuidata(re_t *re);
int re_setfilename(re_t *re, char *filename);
int re_fixorigin(re_t *re);
int re_fixorigin_center(re_t *re);
int re_processkey_editing(re_t *re, SDL_Event *event);
int re_processkey_commandwait(re_t *re, SDL_Event *event);
int re_processkey_commanddata(re_t *re, SDL_Event *event);
int re_processcommand(re_t *re);
int re_moveupdown(re_t *re, int totalinc);
int re_moveleftright(re_t *re, int totalinc);
int re_changefontsize(re_t *re, int direction);
int re_sel_setstart(re_t *re, int line, int col);
int re_sel_setend(re_t *re, int line, int col);
int re_sel_resize(re_t *re,int oldcol,int oldline,int direction);
int re_sel_toggle(re_t *re);
int re_sel_lincolisinside(re_t *re, int line, int col);
int re_sel_lincolisbefore(re_t *re, int line, int col);
int re_sel_lincolisafter(re_t *re, int line, int col);
int re_sel_lincolisend(re_t *re, int line, int col);
int re_selectbuf_resize(re_t *re,long size);
int re_selectbuf_fill(re_t *re,long frompos,long size, int nadditionalspaces);
int re_selectbuf_replace(re_t *re,char *newdata);
int re_addprint(re_t *re);
int re_delprint(re_t *re, int nprint);
int re_rtrim(re_t *re, long curpos, int *trimmed);
long re_getmatchingbracket(re_t *re,long posini, char originalchar, char *matchingchar);
int re_drawheader_editing(re_t *re);
int re_drawheader_command(re_t *re);
int re_drawcontents(re_t *re, printout_t *printout);
int
main(int argc, char *argv[])
{
re_t *re;
int do_exit;
SDL_Event event;
sselect *ssel;
int flag_had_events;
time_t lastidle,now;
int load_pending;
int viewonly;
int i,l;
if(argc!=2 || strcmp(argv[argc-1],"--help")==0) {
fprintf(stderr,"Syntax: %s filename\n",argv[0]);
return(1);
}
viewonly=0;
if((i=strlen(argv[0]))>=(l=strlen(VIEWONLYPROGNAME)) && strcmp(argv[0]+i-l,VIEWONLYPROGNAME)==0)
viewonly=1;
if((re=re_init(viewonly))==NULL) {
fprintf(stderr,"ERROR: couldn't init structs.\n");
return(2);
}
if((ssel=sselect_init())==NULL) {
fprintf(stderr,"ERROR: couln't init internal data.\n");
re_free(re),re=NULL;
return(2);
}
if(re_setfilename(re,argv[argc-1])!=0) {
fprintf(stderr,"ERROR: filename too long.\n");
re_free(re),re=NULL;
return(2);
}
reui_title(re->ui,re->filename);
flag_sigint=flag_sigpipe=0;
setsignal(SIGINT,sighandler_sigint);
setsignal(SIGPIPE,sighandler_sigpipe);
do_exit=0;
reui_scr2renderer(re->ui,0,0,re->ui->w,re->ui->h);
re_setuidata(re);
re->headerdirty=1;
re->contentsdirty=1;
flag_had_events=0;
SDL_StartTextInput();
lastidle=0;
load_pending=1;
redata_loadquestions_setup(re->data, re->filename);
while(do_exit==0 && flag_sigint==0) {
/* load file (or ask pre-load questions), if pending */
if(load_pending && re->command==NULL) {
question_t *question;
if((question=redata_loadquestions_getnext(re->data))!=NULL) {
re->command=COMMAND_QUESTION;
re->question=question;
#if 0
fprintf(stderr,"QUESTION INIT: %s: %s\n",re->question->title,re->question->body);
#endif
} else {
long startpos;
char *startptr;
int len;
if(redata_load(re->data,re->filename,NULL)==-1)
re->flag_newfile=1;
else
re->flag_newfile=0;
re->originline=re->origincol=0;
re->curline=re->curcol=0;
load_pending=0;
re->headerdirty=1;
re->contentsdirty=1;
/* check if last character of file is '\n', if not, add it */
if(redata_getused(re->data)==0
|| (redata_line_rawinfo(re->data,redata_getused(re->data)-1,&startpos,&startptr,&len,NULL)!=-1
&& len>0 && startptr[len-1]!='\n')) {
redata_op_add(re->data,redata_getused(re->data),"\n",1,NULL);
re->command=COMMAND_WARNING;
snprintf(re->commandbuf,sizeof(re->commandbuf),"Added missing \\n at end of file");
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
}
}
}
/* workaround for some nvidia linux drivers, that show old data on partial updates */
if(re->headerdirty || re->contentsdirty || re->ui->rendererdirty) {
re->headerdirty=1;
re->contentsdirty=1;
re->ui->rendererdirty=1;
}
/* end of fix */
if(re->headerdirty) {
if(re->command==NULL) {
#if 0
fprintf(stderr,"REDRAW Header (editing)\n");
#endif
re_drawheader_editing(re);
re->showingwarning=0;
} else {
#if 0
fprintf(stderr,"REDRAW Header (command)\n");
#endif
re_drawheader_command(re);
if(strcmp(re->command,COMMAND_WARNING)==0 || strcmp(re->command,COMMAND_INFO)==0) {
/* the warnings/info only get shown once, remove it */
re->command=NULL;
re->commandbuf[0]='\0';
re->showingwarning=1;
} else {
re->showingwarning=0;
}
}
}
if(re->contentsdirty)
re_drawcontents(re,NULL);
if(re->ui->rendererdirty) {
#if 0
fprintf(stderr,"RENDER\n");
#endif
reui_present(re->ui);
}
for(i=0;i<re->sizeprints;i++) {
if(re->prints[i].ui==NULL)
continue;
if(re->prints[i].dirty)
re_drawcontents(re,re->prints+i);
if(re->prints[i].ui->rendererdirty)
reui_present(re->prints[i].ui);
}
sselect_wait(ssel,(flag_had_events)?10:100);
flag_had_events=(flag_had_events>0)?flag_had_events-1:0;
SDL_PumpEvents();
while(SDL_PeepEvents(&event,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)>0) {
Uint32 windowID;
flag_had_events=10;
/* Process printout events */
if((event.type==SDL_WINDOWEVENT && (windowID=event.window.windowID)!=SDL_GetWindowID(re->ui->win))
|| (event.type==SDL_KEYDOWN && (windowID=event.key.windowID)!=SDL_GetWindowID(re->ui->win))
|| (event.type==SDL_TEXTEDITING && (windowID=event.edit.windowID)!=SDL_GetWindowID(re->ui->win))
|| (event.type==SDL_TEXTINPUT && (windowID=event.text.windowID)!=SDL_GetWindowID(re->ui->win))
) {
printout_t *printout;
for(i=0;i<re->sizeprints;i++) {
if(re->prints[i].ui!=NULL
&& windowID==SDL_GetWindowID(re->prints[i].ui->win)) {
break;
}
}
if(i>=re->sizeprints)
continue; /* unknown window */
printout=re->prints+i;
switch(event.type) {
case SDL_WINDOWEVENT:
if(event.window.event==SDL_WINDOWEVENT_SHOWN
|| event.window.event==SDL_WINDOWEVENT_EXPOSED) {
printout->dirty=1;
} else if((event.window.event==SDL_WINDOWEVENT_RESIZED
|| event.window.event==SDL_WINDOWEVENT_SIZE_CHANGED)
&& (event.window.data1!=printout->ui->w || event.window.data2!=printout->ui->h)) {
reui_resize(printout->ui,event.window.data1,event.window.data2);
printout->dirty=1;
} else if(event.window.event==SDL_WINDOWEVENT_CLOSE) {
re_delprint(re,i);
}
break;
case SDL_KEYDOWN:
if(event.key.keysym.sym==SDLK_ESCAPE) {
re_delprint(re,i);
} else if(event.key.keysym.sym==SDLK_DOWN || event.key.keysym.sym==SDLK_UP) {
if(event.key.keysym.sym==SDLK_UP && printout->originline==0)
break; /* nothing to do, at top */
printout->originline+=((event.key.keysym.sym==SDLK_UP)?-1:1);
printout->dirty=1;
} else if(event.key.keysym.sym==SDLK_LEFT || event.key.keysym.sym==SDLK_RIGHT) {
if(event.key.keysym.sym==SDLK_LEFT && printout->origincol==0)
break; /* nothing to do, at top */
printout->origincol+=((event.key.keysym.sym==SDLK_LEFT)?-1:1)*8;
printout->origincol=(printout->origincol<0)?0:printout->origincol;
printout->dirty=1;
}
break;
}
continue; /* only want window events from printouts */
}
/* process main window events */
switch(event.type) {
case SDL_QUIT:
if(redata_needs_saving(re->data)) {
re->command=COMMAND_CONFIRMEXIT;
re->commandbuf[0]='\0';
re->headerdirty=1;
} else {
do_exit=1;
}
break;
case SDL_KEYDOWN:
case SDL_TEXTINPUT:
case SDL_TEXTEDITING:
if(re->viewonly && (event.type==SDL_TEXTINPUT || event.type==SDL_TEXTEDITING))
break;
if(re->viewonly && event.type==SDL_KEYDOWN) {
if(event.key.keysym.sym==SDLK_ESCAPE) {
do_exit=1;
} if(event.key.keysym.sym==SDLK_DOWN || event.key.keysym.sym==SDLK_UP) {
if(event.key.keysym.sym==SDLK_UP && re->originline==0)
break; /* nothing to do, at top */
re->originline+=((event.key.keysym.sym==SDLK_UP)?-1:1);
re->curline+=((event.key.keysym.sym==SDLK_UP)?-1:1);
re->headerdirty=1;
re->contentsdirty=1;
} else if(event.key.keysym.sym==SDLK_LEFT || event.key.keysym.sym==SDLK_RIGHT) {
if(event.key.keysym.sym==SDLK_LEFT && re->origincol==0)
break; /* nothing to do, at top */
re->origincol+=((event.key.keysym.sym==SDLK_LEFT)?-1:1)*8;
re->origincol=(re->origincol<0)?0:re->origincol;
re->headerdirty=1;
re->contentsdirty=1;
}
break;
}
if(re->command==NULL || strcmp(re->command,COMMAND_WARNING)==0 || strcmp(re->command,COMMAND_INFO)==0)
re_processkey_editing(re,&event);
else if(re->command[0]=='\0')
re_processkey_commandwait(re,&event);
else
re_processkey_commanddata(re,&event);
break;
case SDL_WINDOWEVENT:
#if 0
fprintf(stderr,"WINDOWEVENT: %s(%li) data1:%li data2:%li\n",
(event.window.event==SDL_WINDOWEVENT_NONE)?"NONE":
(event.window.event==SDL_WINDOWEVENT_SHOWN)?"SHOWN":
(event.window.event==SDL_WINDOWEVENT_HIDDEN)?"HIDDEN":
(event.window.event==SDL_WINDOWEVENT_EXPOSED)?"EXPOSED":
(event.window.event==SDL_WINDOWEVENT_MOVED)?"MOVED":
(event.window.event==SDL_WINDOWEVENT_RESIZED)?"RESIZED":
(event.window.event==SDL_WINDOWEVENT_SIZE_CHANGED)?"SIZE_CHANGED":
(event.window.event==SDL_WINDOWEVENT_MINIMIZED)?"MINIMIZED":
(event.window.event==SDL_WINDOWEVENT_MAXIMIZED)?"MAXIMIZED":
(event.window.event==SDL_WINDOWEVENT_RESTORED)?"RESTORED":
(event.window.event==SDL_WINDOWEVENT_FOCUS_GAINED)?"FOCUS_GAINED":
(event.window.event==SDL_WINDOWEVENT_FOCUS_LOST)?"FOCUS_LOST":
(event.window.event==SDL_WINDOWEVENT_CLOSE)?"CLOSE":
(event.window.event==SDL_WINDOWEVENT_TAKE_FOCUS)?"TAKE_FOCUS":
"UNKNOWN",
(long) event.window.event,(long)event.window.data1,(long)event.window.data2);
#endif
if(event.window.event==SDL_WINDOWEVENT_SHOWN
|| event.window.event==SDL_WINDOWEVENT_EXPOSED) {
if(!re->showingwarning)
re->headerdirty=1;
re->contentsdirty=1;
} else if((event.window.event==SDL_WINDOWEVENT_RESIZED
|| event.window.event==SDL_WINDOWEVENT_SIZE_CHANGED)
&& (event.window.data1!=re->ui->w || event.window.data2!=re->ui->h)) {
#if 0
fprintf(stderr,"Resizing from %ix%i to %ix%i...\n",re->ui->w,re->ui->h,(int)event.window.data1,(int)event.window.data2);
#endif
reui_resize(re->ui,event.window.data1,event.window.data2);
re_setuidata(re);
re->headerdirty=1;
re->contentsdirty=1;
}
break;
default:
break;
}
}
/* additional processing of global commands */
if(re->command!=NULL && strcmp(re->command,COMMAND_EXIT)==0)
do_exit=1;
/* timeouts */
now=time(NULL);
if((lastidle+IDLETIMEOUTSECONDS)<now || (lastidle-IDLETIMEOUTSECONDS)>now) {
lastidle=now;
redata_idleproc(re->data,re->filename);
}
}
SDL_StopTextInput();
sselect_free(ssel),ssel=NULL;
re_free(re),re=NULL;
return(0);
}
static int
setsignal(int num, void (*sighandler)(int))
{
struct sigaction sa;
sa.sa_handler=sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags=0;
return(sigaction(num,&sa,0));
}
static void
sighandler_sigint(int num)
{
flag_sigint=1;
}
static void
sighandler_sigpipe(int num)
{
flag_sigpipe=1;
}
static int
mystricmp(const char *s1, const char *s2)
{
int c1,c2;
for(;*s1!='\0' && *s2!='\0';s1++,s2++) {
if(*s1==*s2)
continue;
c1=*((unsigned char *)s1);
c2=*((unsigned char *)s2);
c1=(c1>='A' && c1<='Z')?c1-'A'+'a':c1;
c2=(c2>='A' && c2<='Z')?c2-'A'+'a':c2;
if(c1==c2)
continue;
return(c1-c2);
}
return(((int) (*((unsigned char *)s1)))-((int) (*((unsigned char *)s2))));
}
re_t *
re_init(int viewonly)
{
re_t *re;
if((re=malloc(sizeof(re_t)))==NULL)
return(NULL); /* insuf. mem. */
memset(re,0,sizeof(re_t));
if(viewonly==0) {
re->data=redata_init(
redata_unsaved_register,
redata_highlighter_register,
redata_prototypes_register,
NULL);
} else { /* viewonly */
re->data=redata_init(
redata_highlighter_register,
NULL);
}
if(re->data==NULL) {
re_free(re),re=NULL;
return(NULL); /* insuf. mem. */
}
re->fontheightpercent=100;
if((re->ui=reui_init(DEFAULTFONTHEIGHT*re->fontheightpercent/100,NULL))==NULL) {
re_free(re),re=NULL;
return(NULL); /* video init error */
}
re->viewonly=(viewonly!=0)?1:0;
return(re);
}
void
re_free(re_t *re)
{
int i;
if(re==NULL)
return; /* all done */
for(i=0;i<re->sizeprints;i++) {
if(re->prints[i].ui!=NULL)
re_delprint(re,i);
}
if(re->prints!=NULL)
free(re->prints),re->prints=NULL,re->sizeprints=re->usedprints=0;
if(re->ui!=NULL)
reui_free(re->ui),re->ui=NULL;
if(re->data!=NULL)
redata_free(re->data),re->data=NULL;
if(re->selectbuf!=NULL)
free(re->selectbuf),re->selectbuf=NULL,re->usedselectbuf=re->sizeselectbuf=0;
free(re),re=NULL;
return;
}
int
re_setuidata(re_t *re)
{
if(re==NULL)
return(-1);
re->x=0,re->y=re->ui->fontheight,re->w=re->ui->w,re->h=re->ui->h-re->y;
re->maxrow=re->h/re->ui->fontheight-1;
re->maxcol=re->w/re->ui->fontwidth-1;
re_fixorigin(re);
return(0);
}
int
re_setfilename(re_t *re, char *filename)
{
if(re==NULL || filename==NULL)
return(-1);
strncpy(re->filename,filename,sizeof(re->filename));
re->filename[sizeof(re->filename)-1]='\0';
if(strcmp(filename,re->filename)!=0)
return(-1); /* filename too long */
return(0);
}
int
re_fixorigin(re_t *re)
{
if(re==NULL)
return(-1);
if(re->curline<(re->originline+LINEFORCESCROLL)) {
re->originline=re->curline-LINEFORCESCROLL;
re->originline=(re->originline<0)?0:re->originline;
}
if(re->curline>(re->originline+re->maxrow-LINEFORCESCROLL)) {
re->originline=re->curline+LINEFORCESCROLL-re->maxrow;
re->originline=(re->originline<0)?0:(re->originline>re->curline)?re->curline:re->originline;
}
if(re->curcol<re->origincol) {
re->origincol=re->curcol;
re->origincol=(re->origincol<0)?0:re->origincol;
}
if(re->curcol>=(re->origincol+re->maxcol-COLFORCESCROLL)) {
int col;
col=re->curcol-(re->curcol%COLFORCESCROLL);
re->origincol=col+COLFORCESCROLL-re->maxcol;
re->origincol=(re->origincol<0)?0:re->origincol;
}
return(0);
}
int
re_fixorigin_center(re_t *re)
{
if(re==NULL)
return(-1);
if(re->curline<re->originline || re->curline>=(re->originline+re->maxrow)) {
re->originline=re->curline-(re->maxrow/2);
re->originline=(re->originline<0)?0:re->originline;
}
re->origincol=(re->curcol>(re->maxcol-COLFORCESCROLL))?(re->curcol-(re->maxcol-COLFORCESCROLL)):0;
return(0);
}
int
re_processkey_editing(re_t *re, SDL_Event *event)
{
SDL_Event fakeevent;
if(re==NULL || event==NULL)
return(-1); /* sanity check failed */
/* convert RETURN KEYDOWN to TEXTINPUT */
if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_RETURN) {
memset(&fakeevent,0,sizeof(SDL_Event));
event=&fakeevent;
event->type=SDL_TEXTINPUT;
strncpy(event->text.text,"\n",sizeof(event->text.text));
event->text.text[sizeof(event->text.text)-1]='\0';
} else if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_TAB) {
static char spaces[8]={" "};
memset(&fakeevent,0,sizeof(SDL_Event));
event=&fakeevent;
event->type=SDL_TEXTINPUT;
strncpy(event->text.text,spaces+(re->curcol%8),sizeof(event->text.text));
event->text.text[sizeof(event->text.text)-1]='\0';
} else if(event->type==SDL_KEYDOWN && event->key.keysym.sym==SDLK_DELETE) {
#if 0
fprintf(stderr,"SDL_KEYDOWN: DELETE\n");
#endif
long realend;
int at_end;
long cursorpos;
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0
|| cursorpos==redata_getused(re->data)) {
return(0); /* already at end, nothing to delete */
}
if(redata_line_realend(re->data,cursorpos,&realend)==-1)
return(-1); /* couldn't get current line info */
at_end=(cursorpos==realend)?1:0;
if(at_end && (cursorpos+1)==redata_getused(re->data))
return(-1); /* we shouldn't erase the final '\n' */
memset(&fakeevent,0,sizeof(SDL_Event));
event=&fakeevent;
event->type=SDL_KEYDOWN;
event->key.keysym.sym=SDLK_BACKSPACE;
redata_undo_groupinit(re->data,NULL);
if(at_end) {
int col;
if(redata_pos2linecol(re->data,cursorpos,NULL,&col)==-1)
return(-1); /* couldn't get line info */
if(redata_op_addn(re->data,cursorpos,' ',re->curcol-col,NULL)!=0)
return(-1); /* couldn't add spaces up to current displayed pos */
re->curline++;
re->curcol=0;
} else {
re_moveleftright(re,1);
}
#if 0
fprintf(stderr,"SDL_KEYDOWN: fake setup ok\n");
#endif
}
/* special case: text editing event */
if(event->type==SDL_TEXTINPUT) {
int len;
long realend;
int at_end;
int nspaces;
long cursorpos;
long selstart,selend;
int fixsel;
if(re->ignorenkeys>0) {
re->ignorenkeys--;
return(0); /* this is an already processed key, ignore it */
}
#if 0
fprintf(stderr,"SDL_TEXTINPUT:\"%s\"\n",event->text.text);
#endif
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0
|| redata_line_realend(re->data,cursorpos,&realend)==-1) {
return(-1); /* couldn't get current line info */
}
fixsel=0;
selstart=selend=0;
if(re->selactive
&& redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&selstart,NULL)==0
&& redata_linecol2pos(re->data,re->sellineto,re->selcolto,&selend,NULL)==0) {
fixsel=1;
}
at_end=(cursorpos==realend)?1:0;
for(nspaces=0;event->text.text[nspaces]==' ';nspaces++)
;
if(nspaces>0 && event->text.text[nspaces]=='\0' && at_end) {
/* instead of adding spaces at the end of the line, we move the cursor to the right */
re_moveleftright(re,nspaces);
return(0); /* no need to add spaces at the end of the line */
}
redata_undo_groupinit(re->data,NULL);
if(!(event->text.text[0]=='\n' && event->text.text[1]=='\0') && at_end) {
int col;
if(redata_pos2linecol(re->data,cursorpos,NULL,&col)==-1)
return(-1); /* couldn't get line info */
if(redata_op_addn(re->data,cursorpos,' ',re->curcol-col,NULL)!=0)
return(-1); /* couldn't add spaces up to current displayed pos */
/* increment cursorpos; spaces are 1 byte, so the number of columns advanced is the number of bytes advanced */
cursorpos+=re->curcol-col;
}
len=strlen(event->text.text);
if(redata_op_add(re->data,cursorpos,event->text.text,len,NULL)!=0)
return(-1); /* couldn't add requested text */
if(fixsel) {
if(cursorpos<=selstart)
redata_pos2linecol(re->data,selstart+len,&(re->sellinefrom),&(re->selcolfrom));
if(cursorpos<=selend)
redata_pos2linecol(re->data,selend+len,&(re->sellineto),&(re->selcolto));
}
cursorpos+=len;
if(event->text.text[0]=='\n' && event->text.text[1]=='\0') {
int trimmed;
if(re_rtrim(re,cursorpos-len,&trimmed))
cursorpos-=trimmed;
#if 1
#if 0
fprintf(stderr,"SDL_TEXTINPUT: Insering newline\n");
#endif
#endif
re->curline++;
re->curcol=0;
} else {
re->curcol+=redata_generic_utf8len(event->text.text,len);
}
redata_undo_groupcommit(re->data,NULL);
re_fixorigin(re);
re->headerdirty=1;
re->contentsdirty=1;
}
#if 0
else if(event->type==SDL_TEXTEDITING) {
/* NOTE: on window enter events we cam also receive this event (!) */
#warning TODO: CJK support
fprintf(stderr,"SDL_TEXTEDITING: composition:\"%s\" start:%i len:%i\n",event->edit.text,event->edit.start,event->edit.length);
}
#endif
/* default case: keydown event */
if(event->type!=SDL_KEYDOWN)
return(0); /* Ignore other possible events */
#if 0
fprintf(stderr,"SDL_KEYDOWN: sym:%i\n",event->key.keysym.sym);
#endif
if(event->key.keysym.sym==SDLK_ESCAPE) {
re->headerdirty=1;
} else if((SDL_GetModState()&KMOD_ALT)!=0 && re->selactive && (event->key.keysym.sym==SDLK_LEFT || event->key.keysym.sym==SDLK_RIGHT)) {
int l;
int is_del;
int tolinefix;
long pos,pos2;
is_del=(event->key.keysym.sym==SDLK_LEFT)?1:0;
redata_undo_groupinit(re->data,NULL);
tolinefix=(re->selcolto==0)?1:0;
for(l=re->sellinefrom;l<=(re->sellineto-tolinefix);l++) {
if(redata_linecol2pos(re->data,l,0,&pos,NULL)!=0)
continue;
if(is_del==1 && redata_linecol2pos(re->data,l,1,&pos2,NULL)!=0)
continue;
if(!is_del)
redata_op_add(re->data,pos," ",1,NULL);
else if(pos!=pos2)
redata_op_del(re->data,pos,pos2-pos,NULL);
}
redata_undo_groupcommit(re->data,NULL);
re->headerdirty=1;
re->contentsdirty=1;
} else if(event->key.keysym.sym==SDLK_DOWN || event->key.keysym.sym==SDLK_UP) {
int move_res;
int oldcol=re->curcol,oldline=re->curline;
if((SDL_GetModState()&KMOD_CTRL)!=0 && event->key.keysym.sym==SDLK_UP && re->originline==0)
return(0); /* nothing to do, already at top */
move_res=re_moveupdown(re,(event->key.keysym.sym==SDLK_UP)?-1:1);
if(move_res==0 && (SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) {
re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_UP)?-1:1);
}
if(move_res==0 && (SDL_GetModState()&KMOD_CTRL)!=0) {
if(event->key.keysym.sym==SDLK_UP && re->originline>0) {
re->originline--;
re->contentsdirty=1;
} else if(event->key.keysym.sym==SDLK_DOWN && re->originline<re->curline) {
re->originline++;
re->contentsdirty=1;
}
}
} else if((event->key.keysym.sym==SDLK_LEFT || event->key.keysym.sym==SDLK_RIGHT)) {
int oldcol=re->curcol,oldline=re->curline;
if(re_moveleftright(re,(event->key.keysym.sym==SDLK_LEFT)?-1:1)==0 && (SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) {
re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_LEFT)?-1:1);
}
} else if((SDL_GetModState()&KMOD_CTRL)!=0 && (event->key.keysym.sym==SDLK_PAGEDOWN || event->key.keysym.sym==SDLK_PAGEUP)) {
long cursorpos;
cursorpos=(event->key.keysym.sym==SDLK_PAGEDOWN)?(redata_getused(re->data)-1):0;
cursorpos=(cursorpos<0)?0:cursorpos;
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
re_fixorigin_center(re);
re->headerdirty=1;
re->contentsdirty=1;
} else if(event->key.keysym.sym==SDLK_PAGEDOWN || event->key.keysym.sym==SDLK_PAGEUP) {
re_moveupdown(re,(event->key.keysym.sym==SDLK_PAGEUP)?-(re->maxrow):re->maxrow);
} else if(event->key.keysym.sym==SDLK_HOME || event->key.keysym.sym==SDLK_END) {
long newpos;
long cursorpos;
int oldcol=re->curcol,oldline=re->curline;
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(0); /* error obtaining position */
if((event->key.keysym.sym==SDLK_HOME && redata_line_realstart(re->data,cursorpos,&newpos)==-1)
|| (event->key.keysym.sym==SDLK_END && redata_line_realend(re->data,cursorpos,&newpos)==-1)) {
return(-1); /* couldn't get destination pos data */
}
cursorpos=newpos;
if(redata_pos2linecol(re->data,cursorpos,NULL,&(re->curcol))==-1)
return(-1); /* couldn't get col of current pos */;
if((SDL_GetModState()&KMOD_SHIFT)!=0 && (re->curcol!=oldcol || re->curline!=oldline)) {
re_sel_resize(re,oldcol,oldline,(event->key.keysym.sym==SDLK_HOME)?-1:1);
}
re_fixorigin(re);
re->headerdirty=1;
re->contentsdirty=1;
} else if(event->key.keysym.sym==SDLK_BACKSPACE && (re->curline>0 || re->curcol>0)) {
int line,col;
long startpos,delpos,realstart,start;
long dellen;
char *ptr;
int len;
int trimmed;
int doneinc;
long cursorpos;
long selstart,selend;
int fixsel;
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
#if 0
fprintf(stderr,"SDL_KEYDOWN: BACKSPACE%s\n",(event==&fakeevent)?" (fake)":"");
#endif
return(0); /* error obtaining position */
if(redata_pos2linecol(re->data,cursorpos,&line,&col)==-1)
return(-1); /* couldn't get current line data */
if(col<re->curcol) {
re_moveleftright(re,-1);
return(0); /* were hovering to the right of last char, only had to move left */
}
if(event!=&fakeevent)
redata_undo_groupinit(re->data,NULL);
/* delete the last character */
fixsel=0;
selstart=selend=0;
if(re->selactive
&& redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&selstart,NULL)==0
&& redata_linecol2pos(re->data,re->sellineto,re->selcolto,&selend,NULL)==0) {
fixsel=1;
}
if(col>0) {
if(redata_line_realstart(re->data,cursorpos,&realstart)==-1)
return(-1); /* couldn't get line info */
/* get the start of the part to delete */
doneinc=0;
for(ptr=NULL,len=0,start=0,delpos=cursorpos;doneinc!=1;) {
if((delpos-1)<realstart)
break;
delpos--;
if(ptr==NULL || (start+len)<=delpos || delpos<start) {
if(redata_line_rawinfo(re->data,delpos,&start,&ptr,&len,NULL)==-1)
return(-1); /* couldn't get line data */
}
if(redata_generic_utf8isstartbyte(ptr[delpos-start]))
doneinc++;
}
/* delete */
dellen=cursorpos-delpos;
redata_op_del(re->data,delpos,dellen,NULL);
cursorpos=delpos;
} else {
/* to make the code trivial, we're being lazy on '\n' deletion: we call ...rawinfo() for each byte in the multibyte utf-8 sequence */
for(delpos=cursorpos-1
;delpos>0
&& redata_line_rawinfo(re->data,delpos,&startpos,&ptr,&len,NULL)==0;) {
if(redata_generic_utf8isstartbyte(ptr[delpos-startpos]))
break;
}
dellen=cursorpos-delpos;
redata_op_del(re->data,delpos,dellen,NULL);
cursorpos=delpos;
}
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
if(fixsel) {
if(delpos<=selstart) {
/* if we deleted the start of selection, make the start of selection the cursorpos */
if(selstart>=delpos && selstart<(delpos+dellen))
redata_pos2linecol(re->data,cursorpos,&(re->sellinefrom),&(re->selcolfrom));
else
redata_pos2linecol(re->data,selstart-dellen,&(re->sellinefrom),&(re->selcolfrom));
}
if(delpos<=selend) {
/* if we deleted the end of selection, make the start of selection the cursorpos */
if(selend>=delpos && selend<(delpos+dellen))
redata_pos2linecol(re->data,cursorpos,&(re->sellineto),&(re->selcolto));
else
redata_pos2linecol(re->data,selend-dellen,&(re->sellineto),&(re->selcolto));
}
}
/* if cursor at end of line and there are trailing spaces at the end, delete them too */
if(re_rtrim(re,cursorpos,&trimmed))
cursorpos-=trimmed;
/* end of deletion */
redata_undo_groupcommit(re->data,NULL);
re_fixorigin(re);
re->headerdirty=1;
re->contentsdirty=1;
} else if(event->key.keysym.sym==SDLK_q && (SDL_GetModState()&KMOD_CTRL)!=0) {
re->command="";
re->command_first_key='q';
re->headerdirty=1;
} else if(event->key.keysym.sym==SDLK_F2 || (event->key.keysym.sym==SDLK_s && (SDL_GetModState()&KMOD_CTRL)!=0)) {
char *errormsg=NULL;
if(redata_save(re->data,re->filename,&errormsg)!=-1)
errormsg="File saved";
re->command=COMMAND_INFO;
snprintf(re->commandbuf,sizeof(re->commandbuf),errormsg);
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
} else if(event->key.keysym.sym==SDLK_k && (SDL_GetModState()&KMOD_CTRL)!=0) {
re->command="";
re->command_first_key='k';
re->headerdirty=1;
} else if(event->key.keysym.sym==SDLK_z && (SDL_GetModState()&KMOD_CTRL)!=0) {
long newpos;
if(redata_op_undo(re->data,&newpos))
return(-1);
redata_pos2linecol(re->data,newpos,&(re->curline),&(re->curcol));
re_fixorigin(re);
re->headerdirty=1;
re->contentsdirty=1;
} else if(event->key.keysym.sym==SDLK_l && (SDL_GetModState()&KMOD_CTRL)!=0 && re->cachelastsearch[0]!='\0') {
long newpos;
long cursorpos;
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(-1); /* error obtaining position */
/* search next (forward) */
if((newpos=redata_searchforward(re->data,cursorpos+1,re->cachelastsearch,strlen(re->cachelastsearch)))==-1) {
re->command=COMMAND_WARNING;
snprintf(re->commandbuf,sizeof(re->commandbuf),"String not found");
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(-1);
}
cursorpos=newpos;
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
re_fixorigin_center(re);
re->headerdirty=1;
re->contentsdirty=1;
} else if(event->key.keysym.sym==SDLK_PLUS && (SDL_GetModState()&KMOD_CTRL)!=0) {
#warning XXX TODO: Control+shift+'+'/'-' iterates between the color permutations of the current theme in rgb, 6 in total (Control+shift+'0' resets to the default permutation)
re_changefontsize(re, 1);
re->ignorenkeys++;
} else if(event->key.keysym.sym==SDLK_MINUS && (SDL_GetModState()&KMOD_CTRL)!=0) {
re_changefontsize(re, -1);
re->ignorenkeys++;
} else if(event->key.keysym.sym==SDLK_0 && (SDL_GetModState()&KMOD_CTRL)!=0) {
re_changefontsize(re, 0);
re->ignorenkeys++;
} else if((event->key.keysym.sym==SDLK_c || event->key.keysym.sym==SDLK_x) && (SDL_GetModState()&KMOD_CTRL)!=0) {
long frompos,topos;
int coldone;
int is_cut=(event->key.keysym.sym==SDLK_x)?1:0;
/* Mac-HIG/OpenLook-style copy (ctrl+c) / cut (ctrl+x) */
if(re->selactive
&& redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0
&& redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&coldone)==0) {
re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-coldone);
SDL_SetClipboardText(re->selectbuf);
if(is_cut) {
redata_undo_groupinit(re->data,NULL);
redata_op_del(re->data,frompos,topos-frompos,NULL);
redata_undo_groupcommit(re->data,NULL);
redata_pos2linecol(re->data,frompos,&(re->curline),&(re->curcol));
re_fixorigin(re);
re->headerdirty=1;
}
re_sel_toggle(re);
re->contentsdirty=1;
}
re->ignorenkeys++;
} else if(event->key.keysym.sym==SDLK_v && (SDL_GetModState()&KMOD_CTRL)!=0) {
long cursorpos;
/* Mac-HIG/OpenLook-style paste (ctrl+v)*/
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(-1); /* error obtaining position */
if(re->selactive)
re_sel_toggle(re);
if(SDL_HasClipboardText())
re_selectbuf_replace(re,SDL_GetClipboardText());
if(re->usedselectbuf>0) {
redata_undo_groupinit(re->data,NULL);
redata_op_add(re->data,cursorpos,re->selectbuf,re->usedselectbuf,NULL);
redata_undo_groupcommit(re->data,NULL);
cursorpos+=re->usedselectbuf;
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
re_fixorigin(re);
re->headerdirty=1;
re->contentsdirty=1;
}
re->ignorenkeys++;
} else if(re->selactive && event->key.keysym.sym==SDLK_p && (SDL_GetModState()&KMOD_CTRL)!=0) {
re_addprint(re);
}
return(0);
}
int
re_processkey_commandwait(re_t *re, SDL_Event *event)
{
if(re==NULL || event==NULL)
return(-1); /* sanity check failed */
/* default case: keydown event */
if(event->type!=SDL_KEYDOWN)
return(0); /* Ignore other possible events */
if(re->command_first_key=='q' && event->key.keysym.sym==SDLK_l) {
re->command=COMMAND_GOTOLINE;
re->commandbuf[0]='\0';
re->headerdirty=1;
} else if(re->command_first_key=='q' && event->key.keysym.sym==SDLK_f) {
re->command=COMMAND_SEARCHFORWARD;
strncpy(re->commandbuf,re->cachelastsearch,sizeof(re->commandbuf));
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
} else if(re->command_first_key=='q' && event->key.keysym.sym==SDLK_a) {
re->command=COMMAND_REPLACEWHAT;
strncpy(re->commandbuf,re->cachelastsearch,sizeof(re->commandbuf));
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
} else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_q) {
if(redata_needs_saving(re->data)) {
re->command=COMMAND_CONFIRMEXIT;
re->commandbuf[0]='\0';
} else {
re->command=COMMAND_EXIT;
re->commandbuf[0]='\0';
}
re->headerdirty=1;
} else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_h) {
re_sel_toggle(re);
re->command=NULL;
re->headerdirty=1;
} else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_b) {
re_sel_setstart(re,re->curline,re->curcol);
re->command=NULL;
re->headerdirty=1;
} else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_k) {
re_sel_setend(re,re->curline,re->curcol);
re->command=NULL;
re->headerdirty=1;
} else if(re->command_first_key=='k' && event->key.keysym.sym==SDLK_y) {
long frompos,topos;
int coldone;
long cursorpos;
/* wordstar-style blockdel (ctrl+k+y) */
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(-1); /* error obtaining position */
if(re->selactive
&& redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0
&& redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&coldone)==0) {
re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-coldone);
redata_undo_groupinit(re->data,NULL);
redata_op_del(re->data,frompos,topos-frompos,NULL);
redata_undo_groupcommit(re->data,NULL);
if(cursorpos>frompos) {
cursorpos=frompos;
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
re_fixorigin(re);
}
re->headerdirty=1;
re->contentsdirty=1;
}
if(re->selactive)
re_sel_toggle(re);
re->command=NULL;
re->headerdirty=1;
} else if(re->command_first_key=='k' && (event->key.keysym.sym==SDLK_c || event->key.keysym.sym==SDLK_v)) {
long frompos,topos,insertpos;
int tocoldone,coldone;
/* wordstar-style blockcopy (ctrl+k+c) or move (ctrl+k+v)*/
int is_move=(event->key.keysym.sym==SDLK_v)?1:0;
if(re->selactive
&& redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0
&& redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&tocoldone)==0
&& redata_linecol2pos(re->data,re->curline,re->curcol,&insertpos,&coldone)==0
&& re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-tocoldone)==0
&& redata_preallocate(re->data,redata_getused(re->data)+(re->curcol-coldone)+(is_move?0:re->usedselectbuf))==0) {
if(is_move && insertpos>=frompos && insertpos<topos) {
re->curline=re->sellinefrom;
re->curcol=re->selcolfrom;
redata_linecol2pos(re->data,re->curline,re->curcol,&insertpos,&coldone);
}
redata_undo_groupinit(re->data,NULL);
if(coldone<re->curcol
&& redata_op_addn(re->data,insertpos,' ',re->curcol-coldone,NULL)==0) {
insertpos+=re->curcol-coldone;
}
if(is_move) {
long cursorpos;
redata_op_del(re->data,frompos,re->usedselectbuf,NULL);
redata_op_add(re->data,insertpos-((insertpos>frompos)?re->usedselectbuf:0),re->selectbuf,re->usedselectbuf,NULL);
cursorpos=insertpos-((insertpos>frompos)?re->usedselectbuf:0)+re->usedselectbuf;
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
redata_pos2linecol(re->data,cursorpos-re->usedselectbuf,&(re->sellinefrom),&(re->selcolfrom));
re->sellineto=re->curline;
re->selcolto=re->curcol;
} else {
long cursorpos;
redata_op_add(re->data,insertpos,re->selectbuf,re->usedselectbuf,NULL);
cursorpos=insertpos+re->usedselectbuf;
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
redata_pos2linecol(re->data,insertpos,&(re->sellinefrom),&(re->selcolfrom));
re->sellineto=re->curline;
re->selcolto=re->curcol;
}
redata_undo_groupcommit(re->data,NULL);
re_fixorigin(re);
re->headerdirty=1;
re->contentsdirty=1;
}
re->command=NULL;
re->headerdirty=1;
} else {
re->command=NULL;
re->headerdirty=1;
}
if(!(SDL_GetModState()&KMOD_CTRL))
re->ignorenkeys=1; /* control was not pressed, we will receive again this key as SDL_TEXTINPUT */
return(0);
}
int
re_processkey_commanddata(re_t *re, SDL_Event *event)
{
if(re==NULL || event==NULL)
return(-1); /* sanity check failed */
/* special case: text editing event */
if(event->type==SDL_TEXTINPUT) {
int len;
if(re->ignorenkeys>0) {
re->ignorenkeys--;
return(0); /* this is an already processed key, ignore it */
}
len=strlen(event->text.text);
if((strlen(re->commandbuf)+len+1)>=sizeof(re->commandbuf))
return(-1); /* No space for text */
memcpy(re->commandbuf+strlen(re->commandbuf),event->text.text,len+1);
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
}
/* default case: keydown event */
if(event->type!=SDL_KEYDOWN)
return(0); /* Ignore other possible events */
if(event->key.keysym.sym==SDLK_ESCAPE) {
re->command=NULL;
re->commandbuf[0]='\0';
re->headerdirty=1;
#warning XXX: TODO: re->question: Make cursor change option, make numbers change option
} else if(event->key.keysym.sym==SDLK_BACKSPACE && re->commandbuf[0]!='\0') {
int nchar;
char *ptr;
if((nchar=redata_generic_utf8len(re->commandbuf,strlen(re->commandbuf)))<=0)
return(-1); /* error parsing commandbuf */
if((ptr=redata_generic_utf8col(re->commandbuf,strlen(re->commandbuf),nchar-1))==NULL)
return(-1); /* error positioning in commandbuf */
*ptr='\0';
re->headerdirty=1;
} else if(event->key.keysym.sym==SDLK_RETURN) {
re_processcommand(re);
}
return(0);
}
int
re_processcommand(re_t *re)
{
if(re==NULL || re->command==NULL)
return(-1);
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
if(strcmp(re->command,COMMAND_CONFIRMEXIT)==0) {
if(strcmp(re->commandbuf,"y")==0) {
re->command=COMMAND_EXIT;
re->commandbuf[0]='\0';
re->headerdirty=1;
return(0);
}
re->headerdirty=1;
} else if(strcmp(re->command,COMMAND_GOTOLINE)==0) {
int line;
long pos;
line=atoi(re->commandbuf)-1;
line=(line<0)?0:line;
if(redata_linecol2pos(re->data,line,re->curcol,&pos,NULL)==-1) {
re->command=COMMAND_WARNING;
snprintf(re->commandbuf,sizeof(re->commandbuf),"Unknown line");
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(-1);
}
re->curline=line;
/* position the line in the center of viewport */
re->originline=line-(re->maxrow/2);
re->originline=(re->originline<0)?0:re->originline;
re->headerdirty=1;
re->contentsdirty=1;
} else if(strcmp(re->command,COMMAND_SEARCHFORWARD)==0) {
long newpos;
long cursorpos;
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(-1); /* error obtaining position */
if((newpos=redata_searchforward(re->data,cursorpos,re->commandbuf,strlen(re->commandbuf)))==-1) {
re->command=COMMAND_WARNING;
snprintf(re->commandbuf,sizeof(re->commandbuf),"String not found");
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(-1);
}
strncpy(re->cachelastsearch,re->commandbuf,sizeof(re->cachelastsearch));
re->cachelastsearch[sizeof(re->cachelastsearch)-1]='\0';
cursorpos=newpos;
redata_pos2linecol(re->data,cursorpos,&(re->curline),&(re->curcol));
re_fixorigin_center(re);
re->headerdirty=1;
re->contentsdirty=1;
} else if(strcmp(re->command,COMMAND_REPLACEWHAT)==0) {
re->command=COMMAND_REPLACEWITH;
strncpy(re->cachelastsearch,re->commandbuf,sizeof(re->cachelastsearch));
re->cachelastsearch[sizeof(re->cachelastsearch)-1]='\0';
strncpy(re->commandbuf,re->cachelastreplacewith,sizeof(re->commandbuf));
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(0);
} else if(strcmp(re->command,COMMAND_REPLACEWITH)==0) {
re->command=COMMAND_REPLACEHOW;
strncpy(re->cachelastreplacewith,re->commandbuf,sizeof(re->cachelastreplacewith));
re->cachelastreplacewith[sizeof(re->cachelastreplacewith)-1]='\0';
re->commandbuf[0]='\0';
re->headerdirty=1;
return(0);
} else if(strcmp(re->command,COMMAND_REPLACEHOW)==0) {
int is_all;
int slen,rlen;
long oldpos,newpos,endpos;
long total;
is_all=(mystricmp(re->commandbuf,"ean")==0)?1:(mystricmp(re->commandbuf,"san")==0)?0:-1;
if(is_all==-1) {
re->command=COMMAND_WARNING;
snprintf(re->commandbuf,sizeof(re->commandbuf),"Sorry, unimplemented. For now use only EAN or SAN options.");
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(-1);
}
if(is_all==0 && !re->selactive) {
re->command=COMMAND_WARNING;
snprintf(re->commandbuf,sizeof(re->commandbuf),"No active selection, nothing changed.");
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(-1);
}
oldpos=0;
endpos=redata_getused(re->data);
if(!is_all
&& (redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&oldpos,NULL)!=0
|| redata_linecol2pos(re->data,re->sellineto,re->selcolto,&endpos,NULL)!=0)) {
re->command=COMMAND_WARNING;
snprintf(re->commandbuf,sizeof(re->commandbuf),"Invalid selection.");
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(-1);
}
slen=strlen(re->cachelastsearch);
rlen=strlen(re->cachelastreplacewith);
redata_undo_groupinit(re->data,NULL);
for(total=0
;(oldpos+slen)<endpos && (newpos=redata_searchforward(re->data,oldpos,re->cachelastsearch,slen))!=-1
&& (newpos+slen)<=endpos
;total++,oldpos=newpos+rlen) {
redata_op_del(re->data,newpos,slen,NULL);
redata_op_add(re->data,newpos,re->cachelastreplacewith,rlen,NULL);
endpos=endpos-slen+rlen;
}
redata_undo_groupcommit(re->data,NULL);
redata_pos2linecol(re->data,oldpos,&(re->curline),&(re->curcol));
re_fixorigin_center(re);
re->headerdirty=1;
re->contentsdirty=1;
re->command=COMMAND_INFO;
snprintf(re->commandbuf,sizeof(re->commandbuf),"%li subst",total);
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
re->headerdirty=1;
return(0);
} else if(strcmp(re->command,COMMAND_QUESTION)==0) {
/* validate reply */
if(!(atoi(re->commandbuf)>0 && atoi(re->commandbuf)<=re->question->nopts)) {
/* invalid reply: reset buf */
re->commandbuf[0]=0;
re->headerdirty=1;
return(-1);
}
/* send reply */
redata_loadquestions_reply(re->data,re->question,atoi(re->commandbuf)-1);
re->headerdirty=1;
}
re->command=NULL;
re->commandbuf[0]='\0';
return(0);
}
int
re_moveupdown(re_t *re, int totalinc)
{
long newpos,newpos2;
int inc,doneinc;
long realstart;
int needs_inccol;
long cursorpos;
if(re==NULL)
return(-1); /* sanity check failed */
if(totalinc==0)
return(0); /* nothing to do */
inc=(totalinc<0)?-1:1;
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(-1); /* error obtaining position */
newpos=cursorpos; /* get rid of compiler warning (will be overwitten in the loop as totalinc never is 0) */
needs_inccol=0;
for(doneinc=0;doneinc!=totalinc;doneinc+=inc) {
#if 0
fprintf(stderr,"MOVING from cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol);
#endif
if((inc==-1 && redata_line_prevrealstart(re->data,cursorpos,&realstart)==-1)
|| (inc==1 && redata_line_nextrealstart(re->data,cursorpos,&realstart)==-1)) {
break; /* couldn't get current line data, we are at start/end */
}
cursorpos=realstart;
needs_inccol=1;
re->curline+=inc;
#if 0
fprintf(stderr,"MOVING to cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol);
#endif
}
if(!needs_inccol)
return(-1);
if(redata_line_inccol(re->data,cursorpos,re->curcol,&newpos2,NULL)==-1) {
/* error advancing cursor, "emergency" repositioning */
#if 0
fprintf(stderr,"SETTING COLUMN ERROR\n");
#endif
re->curcol=0;
newpos2=newpos;
}
#if 0
fprintf(stderr,"COLUMN from cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol);
#endif
cursorpos=newpos2;
#if 0
fprintf(stderr,"COLUMN to cursorpos:%li line:%i col:%i\n",cursorpos,re->curline,re->curcol);
#endif
re_fixorigin(re);
re->contentsdirty=1;
re->headerdirty=1;
return(0);
}
int
re_moveleftright(re_t *re, int totalinc)
{
long newpos,realstart;
int inc,doneinc;
char *ptr;
int len;
long start;
int tmpcol,oldcol;
long cursorpos;
if(re==NULL)
return(-1); /* sanity check failed */
if(totalinc==0)
return(0); /* nothing to do */
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(-1); /* error obtaining position */
oldcol=re->curcol;
if(totalinc<0 && (re->curcol+totalinc)<=0) {
/* we'll land on the start of the line -- do it trivially */
if(redata_line_realstart(re->data,cursorpos,&realstart)==-1)
return(-1); /* couldn't get current pos */
re->curcol=0;
cursorpos=realstart;
} else {
/* move a char at a time */
if(redata_line_realstart(re->data,cursorpos,&realstart)==-1)
return(-1); /* couldn't get current pos */
inc=(totalinc<0)?-1:1;
doneinc=0;
if(redata_pos2linecol(re->data,cursorpos,NULL,&tmpcol)==-1)
return(-1); /* couldn't get current pos */
/* special case: we're just over the '\n' going right, we have to move 1 to enable the generic case to work */
if(tmpcol==re->curcol && inc>0) {
if(redata_line_rawinfo(re->data,cursorpos,&start,&ptr,&len,NULL)==-1)
return(-1); /* couldn't get line data */
if(ptr[cursorpos-start]=='\n') {
tmpcol++;
totalinc--;
re->curcol++;
}
}
/* generic case: cursor after the \n ("floating") */
if(tmpcol<re->curcol) {
int avail;
if(inc>0) {
doneinc=totalinc;
} else {
avail=re->curcol-tmpcol;
doneinc=(avail>=totalinc)?totalinc:avail;
}
}
/* generic case: move one char at a time */
for(ptr=NULL,len=0,start=0,newpos=cursorpos;doneinc!=totalinc;) {
if((newpos+inc)<realstart)
break;
newpos+=inc;
if(ptr==NULL || (start+len)<=newpos || newpos<start) {
if(redata_line_rawinfo(re->data,newpos,&start,&ptr,&len,NULL)==-1)
return(-1); /* couldn't get line data */
}
if(redata_generic_utf8isstartbyte(ptr[newpos-start]))
doneinc+=inc;
if(ptr[newpos-start]=='\n')
break;
}
cursorpos=newpos;
re->curcol+=doneinc;
if(inc>0 && doneinc<totalinc && ptr!=NULL && ptr[newpos-start]=='\n')
re->curcol+=(totalinc-doneinc);
}
if(re->curcol!=oldcol) {
re->contentsdirty=1;
re->headerdirty=1;
if(re->curcol<(re->origincol+COLFORCESCROLL) && re->origincol>0) {
re->origincol=(re->curcol-COLFORCESCROLL)/COLFORCESCROLL;
re->origincol=re->origincol*COLFORCESCROLL;
re->origincol=(re->origincol<0)?0:re->origincol;
}
if(re->curcol>(re->origincol+re->maxcol-COLFORCESCROLL)) {
re->origincol=re->curcol+COLFORCESCROLL-re->maxcol-(re->curcol%COLFORCESCROLL);
re->origincol=(re->origincol<0)?0:re->origincol;
}
}
return(0);
}
int
re_changefontsize(re_t *re, int direction)
{
int validpercent[]={50,67,90,100,110,120,133,150,170,200,240,300};
int newpercent;
int i;
if(re==NULL)
return(-1); /* sanity check failed */
if(direction<0) {
newpercent=validpercent[0];
for(i=0;i<(sizeof(validpercent)/sizeof(validpercent[0]));i++) {
if(validpercent[i]<re->fontheightpercent)
newpercent=validpercent[i];
else
break;
}
} else if(direction>0) {
newpercent=validpercent[(sizeof(validpercent)/sizeof(validpercent[0]))-1];
for(i=(sizeof(validpercent)/sizeof(validpercent[0]))-1;i>=0;i--) {
if(validpercent[i]>re->fontheightpercent)
newpercent=validpercent[i];
else
break;
}
} else {
newpercent=100;
}
if(reui_setfontheight(re->ui,DEFAULTFONTHEIGHT*newpercent/100)==-1)
return(-1); /* couldn't setup new font size */
re->fontheightpercent=newpercent;
re_setuidata(re);
re_fixorigin(re);
re->contentsdirty=1;
re->headerdirty=1;
return(0);
}
int
re_sel_setstart(re_t *re, int line, int col)
{
if(re==NULL || line<0 || col<0)
return(-1);
re->sellinefrom=line;
re->selcolfrom=col;
if(re->selactive==0
|| (re->sellineto<re->sellinefrom)
|| (re->sellineto==re->sellinefrom && re->selcolto<re->selcolfrom)) {
re->sellineto=re->sellinefrom;
re->selcolto=re->selcolfrom;
}
re->selactive=1;
re->contentsdirty=1;
return(0);
}
int
re_sel_setend(re_t *re, int line, int col)
{
if(re==NULL || line<0 || col<0)
return(-1);
re->sellineto=line;
re->selcolto=col;
if(re->selactive==0
|| (re->sellineto<re->sellinefrom)
|| (re->sellineto==re->sellinefrom && re->selcolto<re->selcolfrom)) {
re->sellinefrom=re->sellineto;
re->selcolfrom=re->selcolto;
}
re->selactive=1;
re->contentsdirty=1;
return(0);
}
int
re_sel_resize(re_t *re,int oldcol,int oldline,int direction)
{
if(re==NULL || oldcol<0 || oldline<0 || (direction!=-1 && direction!=1))
return(-1);
if(direction==-1) {
if(re->selactive==0) {
re_sel_setstart(re,re->curline,re->curcol);
re_sel_setend(re,oldline,oldcol);
} else if(re_sel_lincolisafter(re,re->curline,re->curcol) || re_sel_lincolisinside(re,re->curline,re->curcol)) {
re_sel_setend(re,re->curline,re->curcol);
} else {
re_sel_setstart(re,re->curline,re->curcol);
}
} else {
if(re->selactive==0 || re_sel_lincolisbefore(re,oldline,oldcol)) {
re_sel_setstart(re,oldline,oldcol);
re_sel_setend(re,re->curline,re->curcol);
} else if(re_sel_lincolisinside(re,oldline,oldcol)) {
re_sel_setstart(re,re->curline,re->curcol);
} else {
re_sel_setend(re,re->curline,re->curcol);
}
}
if(re->sellinefrom==re->sellineto && re->selcolfrom==re->selcolto)
re->selactive=0;
return(0);
}
int
re_sel_toggle(re_t *re)
{
if(re==NULL)
return(-1);
re->selactive=1-re->selactive;
re->contentsdirty=1;
return(0);
}
int
re_sel_lincolisinside(re_t *re, int line, int col)
{
if(re==NULL || line<0 || col<0)
return(0);
if(line==re->sellinefrom && line==re->sellineto) {
if(col>=re->selcolfrom && col<re->selcolto)
return(1);
return(0);
} else if(line==re->sellinefrom) {
if(col>=re->selcolfrom)
return(1);
return(0);
} else if(line>re->sellinefrom && line<re->sellineto) {
return(1);
} else if(line==re->sellineto) {
if(col<re->selcolto)
return(1);
return(0);
}
return(0);
}
int
re_sel_lincolisbefore(re_t *re, int line, int col)
{
if(re==NULL || line<0 || col<0)
return(0);
if(line<re->sellinefrom || (line==re->sellinefrom && col<re->selcolfrom))
return(1);
return(0);
}
int
re_sel_lincolisafter(re_t *re, int line, int col)
{
if(re==NULL || line<0 || col<0)
return(0);
if(line>re->sellineto || (line==re->sellineto && col>=re->selcolto))
return(1);
return(0);
}
int
re_sel_lincolisend(re_t *re, int line, int col)
{
if(re==NULL || line<0 || col<0)
return(0);
if(line==re->sellineto && col==re->selcolto)
return(1);
return(0);
}
int
re_selectbuf_resize(re_t *re,long size)
{
long newsize;
char *newptr;
if(re==NULL || size<0)
return(-1); /* sanity check failed */
if(size==0)
return(0); /* nothing to do */
newsize=(size+SELECTBUFBLOCK-1)/SELECTBUFBLOCK;
newsize*=SELECTBUFBLOCK;
if((newptr=realloc(re->selectbuf,newsize))==NULL)
return(-1);
re->selectbuf=newptr;
re->sizeselectbuf=newsize;
return(0);
}
int
re_selectbuf_fill(re_t *re,long frompos,long size, int nadditionalspaces)
{
int nchunk,off,avail;
long n;
if(re==NULL || size<0 || nadditionalspaces<0 || (frompos+size)>redata_getused(re->data) || redata_getposptr(re->data,frompos,&nchunk,&off)!=0)
return(-1); /* sanity check failed */
re->usedselectbuf=0;
if((size+nadditionalspaces+1)>re->sizeselectbuf
&& re_selectbuf_resize(re,size+nadditionalspaces+1)!=0) {
return(-1); /* insuf. mem. */
}
for(n=0;n<size && nchunk<re->data->sizechunks;nchunk++,off=0) {
avail=re->data->chunks[nchunk]->useddata-off;
if(avail<=0)
continue;
if(avail>(size-n))
avail=size-n;
memcpy(re->selectbuf+n,re->data->chunks[nchunk]->data+off,avail);
n+=avail;
}
re->usedselectbuf=n;
if(nadditionalspaces>0) {
memset(re->selectbuf+re->usedselectbuf,' ',nadditionalspaces);
re->usedselectbuf+=nadditionalspaces;
}
re->selectbuf[re->usedselectbuf]='\0';
return(0);
}
int
re_selectbuf_replace(re_t *re,char *newdata)
{
long size;
if(re==NULL || newdata==NULL)
return(-1);
size=strlen(newdata);
if((size+1)>re->sizeselectbuf
&& re_selectbuf_resize(re,size+1)!=0) {
return(-1); /* insuf. mem. */
}
memcpy(re->selectbuf,newdata,size);
re->usedselectbuf=size;
re->selectbuf[re->usedselectbuf]='\0';
return(0);
}
int
re_addprint(re_t *re)
{
int i;
long frompos,topos;
int coldone;
if(re==NULL || re->selactive==0)
return(-1);
/* ensure space for the new printout */
if(re->usedprints==re->sizeprints) {
printout_t *newprints;
if((newprints=realloc(re->prints,sizeof(printout_t)*(re->sizeprints+SIZEBLOCKPRINTS)))==NULL)
return(-1); /* insuf. mem. */
re->prints=newprints;
memset(re->prints+re->sizeprints,0,sizeof(printout_t)*SIZEBLOCKPRINTS);
re->sizeprints+=SIZEBLOCKPRINTS;
}
for(i=0;i<re->sizeprints;i++) {
if(re->prints[i].ui==NULL)
break;
}
if(i>=re->sizeprints)
return(-1); /* INTERNAL ERROR */
/* setup window */
if((re->prints[i].ui=reui_init(DEFAULTFONTHEIGHT*re->fontheightpercent/100,re->ui))==NULL)
return(-1); /* couldn't init window */
if((re->prints[i].data=redata_init(redata_highlighter_register,NULL))==NULL) {
reui_free(re->prints[i].ui),re->prints[i].ui=NULL;
return(-1); /* couldn't init data store */
}
re->usedprints++;
/* copy contents (and set clipboard contents and unselect)*/
if(redata_linecol2pos(re->data,re->sellinefrom,re->selcolfrom,&frompos,NULL)==0
&& redata_linecol2pos(re->data,re->sellineto,re->selcolto,&topos,&coldone)==0) {
re_selectbuf_fill(re,frompos,topos-frompos,re->selcolto-coldone);
redata_op_add(re->prints[i].data,0,re->selectbuf,strlen(re->selectbuf),NULL);
SDL_SetClipboardText(re->selectbuf);
re_sel_toggle(re);
re->contentsdirty=1;
}
re->prints[i].dirty=1;
return(0);
}
int
re_delprint(re_t *re, int nprint)
{
if(re==NULL || nprint<0 || nprint>re->sizeprints || re->prints[nprint].ui==NULL)
return(-1);
reui_free(re->prints[nprint].ui),re->prints[nprint].ui=NULL;
if(re->prints[nprint].data!=NULL)
redata_free(re->prints[nprint].data),re->prints[nprint].data=NULL;
re->usedprints--;
return(0);
}
int
re_rtrim(re_t *re, long curpos, int *trimmed)
{
long startpos;
char *start;
int len;
int n;
if(re==NULL || curpos<0 || curpos>redata_getused(re->data))
return(-1);
if(trimmed!=NULL)
*trimmed=0;
if(redata_line_rawinfo(re->data,curpos,&startpos,&start,&len,NULL)==0 &&
len>1 && start[len-1]=='\n' && start[len-2]==' ' && curpos==(startpos+len-1)) {
/* count the number of spaces at end of line */
n=0;
while((len-1-n-1)>=0 && start[len-1-n-1]==' ')
n++;
/* delete the counted number of spaces at end of line */
redata_op_del(re->data,startpos+len-1-n,n,NULL);
if(trimmed!=NULL)
*trimmed=n;
}
return(0);
}
long
re_getmatchingbracket(re_t *re,long posini, char originalchar, char *matchingchar)
{
char *ori,*dest;
char *pairs="[]{}<>()";
int is_backwards;
int colorindex,newcolor;
long realstart;
int line;
long pos;
long posnextori,posnextdest;
long posnext;
int counter;
if(re==NULL)
return(-1); /* sanity check failed */
if((ori=strchr(pairs,originalchar))==NULL)
return(-1); /* unknown char */
is_backwards=(ori-pairs)%2;
dest=ori+((is_backwards)?-1:1);
if(redata_pos2linecol(re->data,posini,&line,NULL)==-1
|| redata_line_realstart(re->data,posini,&realstart)==-1) {
return(-1); /* couldn't get line number or startpos */
}
colorindex=redata_highlighter_getcolorindex(re->data,line,posini-realstart);
pos=posini;
counter=1;
while(1) {
/* get the next pos */
if(!is_backwards) {
posnextori=redata_searchforward(re->data,pos+1,ori,1);
posnextdest=redata_searchforward(re->data,pos+1,dest,1);
posnext=(posnextori==-1)?posnextdest:(posnextdest==-1)?posnextori:(posnextori<posnextdest)?posnextori:posnextdest;
} else {
posnextori=redata_searchbackwards(re->data,pos-1,ori,1);
posnextdest=redata_searchbackwards(re->data,pos-1,dest,1);
posnext=(posnextori==-1)?posnextdest:(posnextdest==-1)?posnextori:(posnextori>posnextdest)?posnextori:posnextdest;
}
if(posnext==-1)
break; /* search ended and couln't get the counter to zero */
/* check that the color index is ok */
if(redata_pos2linecol(re->data,posnext,&line,NULL)==-1
|| redata_line_realstart(re->data,posnext,&realstart)==-1) {
return(-1); /* couldn't get line number or startpos */
}
newcolor=redata_highlighter_getcolorindex(re->data,line,posnext-realstart);
if(colorindex!=newcolor) {
pos=posnext;
continue; /* it doesn't have the same color */
}
/* do the math */
if(posnext==posnextori)
counter++;
if(posnext==posnextdest)
counter--;
pos=posnext;
if(counter==0) {
if(matchingchar!=NULL)
*matchingchar=*dest;
return(posnext); /* found matching bracket */
}
}
return(-1);
}
int
re_drawheader_editing(re_t *re)
{
long cursorpos;
char linebuf[32],colbuf[32],posbuf[32],sizebuf[32];
char *spaceslinebuf,*spacescolbuf,*spacesposbuf,*spacessizebuf;
char spaces[128];
int lenfilename;
char *filename;
char *spacesfilename;
if(re==NULL)
return(-1);
if(redata_linecol2pos(re->data,re->curline,re->curcol,&cursorpos,NULL)!=0)
return(0); /* error obtaining position */
reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,redata_needs_saving(re->data)?COLOR_STATUSBG:COLOR_INFOBG);
/* for the user, lines start at 0 (internally they start at 0) */
memset(spaces,' ',sizeof(spaces));
spaces[sizeof(spaces)-1]='\0';
snprintf(linebuf,sizeof(linebuf),"%i",re->curline+1),linebuf[sizeof(linebuf)-1]='\0';
spaceslinebuf=spaces+sizeof(spaces)-1-strlen(linebuf);
snprintf(colbuf,sizeof(colbuf),"%i",re->curcol+1),colbuf[sizeof(colbuf)-1]='\0';
spacescolbuf=spaces+sizeof(spaces)-1-strlen(colbuf);
snprintf(posbuf,sizeof(posbuf),"%li",cursorpos),posbuf[sizeof(posbuf)-1]='\0';
spacesposbuf=spaces+sizeof(spaces)-1-strlen(posbuf);
snprintf(sizebuf,sizeof(sizebuf),"%li",redata_getused(re->data)),sizebuf[sizeof(sizebuf)-1]='\0';
spacessizebuf=spaces+sizeof(spaces)-1-strlen(sizebuf);
lenfilename=strlen(re->filename);
filename=((lenfilename)>(sizeof(spaces)-1))?(re->filename+lenfilename-sizeof(spaces)-1):re->filename;
spacesfilename=spaces+sizeof(spaces)-1-strlen(filename);
reui_printf(re->ui,0,0,redata_needs_saving(re->data)?COLOR_STATUSFGLIGHT:COLOR_INFOFGLIGHT
,"File:%s%s Line:%s Col:%s Pos:%s Size:%s"
,spacesfilename,redata_needs_saving(re->data)?"*":" ",spaceslinebuf,spacescolbuf,spacesposbuf,spacessizebuf);
reui_printf(re->ui,0,0,redata_needs_saving(re->data)?COLOR_STATUSFG:COLOR_INFOFG
," %s%s %s %s %s %s"
,filename," ",linebuf,colbuf,posbuf,sizebuf);
re->headerdirty=0;
re->ui->rendererdirty=1;
return(0);
}
int
re_drawheader_command(re_t *re)
{
if(re==NULL)
return(-1);
if(re->command==NULL)
return(-1);
if(re->command[0]=='\0') {
reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG);
reui_printf(re->ui,0,0,COLOR_QUERYFG,"Command:");
} else if(strcmp(re->command,COMMAND_WARNING)==0 || strcmp(re->command,COMMAND_CONFIRMEXIT)==0) {
reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_WARNINGBG);
reui_printf(re->ui,0,0,COLOR_WARNINGFG,"%s %s",re->command,re->commandbuf);
} else if(strcmp(re->command,COMMAND_INFO)==0) {
reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_INFOBG);
reui_printf(re->ui,0,0,COLOR_INFOFG,"%s %s",re->command,re->commandbuf);
} else if(strcmp(re->command,COMMAND_QUESTION)==0) {
question_t *q;
reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG);
#warning XXX TODO: suppont arbitrary number of options, highlight current option
q=re->question;
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
reui_printf(re->ui,0,0,COLOR_QUERYFG,"%s %s: %s %s%s%s%s%s%s%s%s: %s"
,re->command
,(q->titleshort!=NULL)?q->titleshort:q->title
,(q->bodyshort!=NULL)?q->bodyshort:q->body
,(q->nopts>0)?"1.":"",(q->nopts>0)?(q->optsshort!=NULL)?q->optsshort[0]:q->opts[0]:""
,(q->nopts>1)?" 2.":"",(q->nopts>1)?(q->optsshort!=NULL)?q->optsshort[1]:q->opts[1]:""
,(q->nopts>2)?" 3.":"",(q->nopts>2)?(q->optsshort!=NULL)?q->optsshort[2]:q->opts[2]:""
,(q->nopts>3)?" 4.":"",(q->nopts>4)?(q->optsshort!=NULL)?q->optsshort[3]:q->opts[3]:""
,re->commandbuf);
} else {
reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,COLOR_QUERYBG);
re->commandbuf[sizeof(re->commandbuf)-1]='\0';
reui_printf(re->ui,0,0,COLOR_QUERYFG,"%s %s",re->command,re->commandbuf);
}
re->headerdirty=0;
re->ui->rendererdirty=1;
return(0);
}
int
re_drawcontents(re_t *re, printout_t *printout)
{
reui_t *ui;
redata_t *data;
int x0,y0,w,h;
int originline,origincol;
int curline,curcol;
int maxcol,maxrow;
long pos,newpos;
int y,row,tmprow;
long cursorpos;
int is_continuation;
printout_t fakeprintout;
hcolor_t *colors,fakecolor;
int ncolors;
const char selcolornormal[]={"\xde\xcf\x7f\xff"};
const char selcolorcurline[]={"\xf0\xea\xc9\xff"};
long realstart,realend;
linecolor_t *linecolors,fakelinecolors;
int nlinecolors;
int drawn_cursor;
int matchingpos;
char matchingchar;
int mline,mcol;
if(re==NULL || (printout!=NULL && (printout->ui==NULL || printout->data==NULL)))
return(-1);
/* init vars and clear screen */
ui=re->ui;
data=re->data;
x0=re->x;
y0=re->y;
w=re->w;
h=re->h;
originline=re->originline;
origincol=re->origincol;
curline=re->curline;
curcol=re->curcol;
maxcol=re->maxcol;
maxrow=re->maxrow;
if(printout==NULL && re->viewonly==1) {
memset(&fakeprintout,0,sizeof(printout_t));
fakeprintout.ui=re->ui;
fakeprintout.data=re->data;
fakeprintout.originline=re->originline;
fakeprintout.origincol=re->origincol;
printout=&fakeprintout;
} else if(printout!=NULL) {
ui=printout->ui;
data=printout->data;
x0=0;
y0=0;
w=ui->w;
h=ui->h;
originline=curline=printout->originline;
origincol=curcol=printout->origincol;
maxcol=w/ui->fontwidth-1;
maxrow=h/ui->fontheight-1;
}
if(redata_linecol2pos(data,curline,curcol,&cursorpos,NULL)!=0)
return(0); /* error obtaining position */
reui_fill(ui,x0,y0,w,h,"\xdf\xdf\xdf\xff");
row=curline-originline;
pos=cursorpos;
/* get position/row/col of character at top left of screen */
while(row>0 && pos>0) {
if(redata_line_rawinfo(data,pos,&newpos,NULL,NULL,&is_continuation)==-1)
return(-1);
pos=(newpos>0)?newpos-1:0;
if(!is_continuation)
row--;
}
/* highlight current line (in printouts, highlight alternating lines) */
if(printout==NULL) {
reui_fill(ui,x0,y0+(curline-originline)*ui->fontheight,w,ui->fontheight+1,"\xef\xef\xef\xff");
} else {
for(y=y0+(printout->originline%2)*ui->fontheight;y<(y0+h);y+=ui->fontheight*2)
reui_fill(ui,x0,y,w,ui->fontheight+1,"\xef\xef\xef\xff");
}
/* highlight the selection */
if(printout==NULL && re->selactive) {
const char *selcolor;
tmprow=row;
for(y=y0;y<(y0+h);y+=ui->fontheight,row++) {
selcolor=(row==(curline-originline))?selcolorcurline:selcolornormal;
if((originline+row)==re->sellinefrom && (originline+row)==re->sellineto) {
reui_fill(ui,x0+(re->selcolfrom-origincol)*ui->fontwidth,y0+row*ui->fontheight,(re->selcolto-re->selcolfrom)*ui->fontwidth,ui->fontheight+1,selcolor);
} else if((originline+row)==re->sellinefrom) {
int x1;
x1=x0+(re->selcolfrom-origincol)*ui->fontwidth;
if(x1<(x0+w))
reui_fill(ui,x1,y0+row*ui->fontheight,w-x1,ui->fontheight+1,selcolor);
} else if((originline+row)>re->sellinefrom && (originline+row)<re->sellineto) {
reui_fill(ui,x0,y0+row*ui->fontheight,w,ui->fontheight+1,selcolor);
} else if((originline+row)==re->sellineto) {
int x2;
x2=x0+(re->selcolto-origincol)*ui->fontwidth;
if(x2>(x0))
reui_fill(ui,x0,y0+row*ui->fontheight,x2-x0,ui->fontheight+1,selcolor);
}
}
row=tmprow;
}
/* draw the lines */
if((colors=redata_highlighter_getcolors(data,&ncolors))==NULL) {
colors=&fakecolor;
ncolors=1;
memcpy(fakecolor.rgba,"\x00\x00\x00\xff",5);
}
drawn_cursor=0;
matchingpos=-1;
matchingchar='\0';
for(y=y0;y<(y0+h);y+=ui->fontheight,row++) {
int in_error;
int availcol,tmpcol,len;
char *lastcolor;
/* definition of vars for tracking linecolor usage */
int curlinecolor; /* current linecolor */
int usedlenlinecolor; /* number of bytes of current linecolor already drawn */
/* get start/end of line */
if(redata_line_realstart(data,pos,&realstart)==-1 || redata_line_realend(data,pos,&realend)==-1)
break; /* couldn't get real start/end */
/* setup colors */
if(colors==(&fakecolor) || (linecolors=redata_highlighter_getline(data,originline+row,&nlinecolors))==NULL) {
linecolors=&fakelinecolors;
fakelinecolors.len=1;
fakelinecolors.color=0;
}
curlinecolor=0;
usedlenlinecolor=0;
lastcolor="\x00\x00\x00\xff";
in_error=0;
/* draw each part of this line */
for(tmpcol=0,pos=realstart,availcol=0;tmpcol<(origincol+maxcol) && pos<=realend;pos=newpos+len,tmpcol+=availcol) {
int has_nl;
char *ptr;
int incompletestart,incompleteend,endreq;
int used,usedcol; /* number of bytes/columns used of redata chunk (those are already drawn) */
if(redata_line_rawinfo(data,pos,&newpos,&ptr,&len,&is_continuation)==-1) {
in_error=1;
break; /* couldn't get this line part info */
}
has_nl=((len>0 && ptr[len-1]=='\n')?1:0);
availcol=redata_generic_utf8lenincomplete(ptr,len-has_nl,&incompletestart,&incompleteend,&endreq);
/* display the line */
used=incompletestart;
usedcol=0;
/* while the avail text is larger than the linecolor len */
while(curlinecolor<nlinecolors && (len-has_nl-incompleteend-used)>=(linecolors[curlinecolor].len-usedlenlinecolor)) {
lastcolor=colors[linecolors[curlinecolor].color].rgba;
reui_write(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,lastcolor,ptr+used,linecolors[curlinecolor].len-usedlenlinecolor);
usedcol+=redata_generic_utf8len(ptr+used,linecolors[curlinecolor].len-usedlenlinecolor);
used+=linecolors[curlinecolor].len-usedlenlinecolor;
curlinecolor++;
usedlenlinecolor=0;
}
/* for the last bytes of avail text, after writing them we save how many bytes we have processed of that linecolor to be able to continue later */
if((len-has_nl-incompleteend-used)>0) {
if(curlinecolor<nlinecolors && (linecolors[curlinecolor].len-usedlenlinecolor)>(len-has_nl-incompleteend-used)) {
lastcolor=colors[linecolors[curlinecolor].color].rgba;
usedlenlinecolor+=(len-has_nl-incompleteend-used);
if(usedlenlinecolor==linecolors[curlinecolor].len) {
curlinecolor++;
usedlenlinecolor=0;
}
}
reui_write(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,lastcolor,ptr+used,(len-has_nl-incompleteend-used));
usedcol+=redata_generic_utf8len(ptr+used,(len-has_nl-incompleteend-used));
used+=(len-has_nl-incompleteend-used);
}
/* if the last utf-8 char is broken into several blocks */
if(incompleteend>0) {
char broken[7];
int usedbroken;
usedbroken=0;
redata_getutf8char(re->data,newpos+len-has_nl-incompleteend,broken,sizeof(broken),&usedbroken);
/* write the just-recomposer character in the correct color */
if(curlinecolor<nlinecolors && (linecolors[curlinecolor].len-usedlenlinecolor)>=1) {
lastcolor=colors[linecolors[curlinecolor].color].rgba;
usedlenlinecolor+=1;
if(usedlenlinecolor==linecolors[curlinecolor].len) {
curlinecolor++;
usedlenlinecolor=0;
}
}
reui_write(ui,x0+(tmpcol-origincol+usedcol)*ui->fontwidth,y,lastcolor,broken,usedbroken);
usedcol++;
availcol++;
}
/* draw cursor if applicable */
if(printout==NULL && row==(curline-originline) && curcol>=tmpcol && curcol<(tmpcol+availcol)) {
char cursorchar[7];
int usedcursorchar;
/* draw cursor */
reui_fill(ui,x0+ui->fontwidth*(curcol-origincol),y,ui->fontwidth,ui->fontheight+1,"\x00\x00\x00\xff");
drawn_cursor=1;
usedcursorchar=0;
redata_getutf8char(re->data,cursorpos,cursorchar,sizeof(cursorchar),&usedcursorchar);
reui_write(ui,x0+ui->fontwidth*(curcol-origincol),y,"\xff\xff\xff\xff",cursorchar,usedcursorchar);
/* get matching braces/parens/anglebracket/curlybraces for highlighting */
if(usedcursorchar==1 && strchr("[]{}<>()",*cursorchar)!=NULL)
matchingpos=re_getmatchingbracket(re,cursorpos,*cursorchar,&matchingchar);
}
}
if(printout==NULL && row==(curline-originline) && !drawn_cursor)
reui_fill(ui,x0+ui->fontwidth*(curcol-origincol),y,ui->fontwidth,ui->fontheight+1,"\x00\x00\x00\xff");
if(in_error)
break;
pos=realend+1;
}
/* highlight matching parens/brace/... if applicable */
if(printout==NULL && matchingpos!=-1 && redata_pos2linecol(data,matchingpos,&mline,&mcol)!=-1) {
int x,y;
char *fg="\xff\x00\x00\x80";
char *bg="\xff\xff\xff\xaf";
x=x0+(mcol-origincol)*ui->fontwidth+(ui->fontwidth/2);
x=(x<x0)?x0:(x>=(x0+w))?(x0+w-1):x;
y=y0+(mline-originline)*ui->fontheight+(ui->fontheight/2);
y=(y<y0)?y0:(y>=(y0+h))?(y0+h-1):y;
if(mline<originline)
reui_balloon(ui, 'n', x, y0, fg, bg, &matchingchar,1);
else if(mline>=(originline+maxrow))
reui_balloon(ui, 's', x, y0+h-1, fg, bg, &matchingchar,1);
else if(mcol<origincol)
reui_balloon(ui, 'w', x0, y, fg, bg, &matchingchar,1);
else if(mcol>=(origincol+maxcol))
reui_balloon(ui, 'e', x0+w-1,y, fg, bg, &matchingchar,1);
else
reui_balloon(ui, '\0', x,y, fg, bg, &matchingchar,1);
}
if(printout==NULL) {
const char *hint;
hint=redata_prototypes_get(data, cursorpos);
if(hint!=NULL) {
if((curline-originline)>=3)
reui_balloon(ui, '\0', x0+w/2, y0+ui->fontheight*3/2, "\x80\x80\x80\xff", "\xff\xff\xff\xcf",(char *) hint,strlen(hint));
else
reui_balloon(ui, '\0', x0+w/2, y0+ui->fontheight*((curline-originline)*2+5)/2, "\x80\x80\x80\xff", "\xff\xff\xff\xcf",(char *) hint,strlen(hint));
}
}
/* all done */
if(printout==NULL || printout==&fakeprintout) {
re->contentsdirty=0;
ui->rendererdirty=1;
} else {
printout->dirty=0;
printout->ui->rendererdirty=1;
}
return(0);
}