/* * 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_textinsert(re_t *re, char *text, int sizetext); 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; case SDL_MOUSEBUTTONDOWN: if(event.button.y>=re->y && event.button.x>=re->x) { int newposx,newposy; long tmppos; newposx=(event.button.x-re->x)/re->ui->fontwidth; newposy=(event.button.y-re->y)/re->ui->fontheight; if(redata_linecol2pos(re->data, re->originline+newposy, re->origincol+newposx,&tmppos,NULL)==0) { re->curline=re->originline+newposy; re->curcol=re->origincol+newposx; 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_textinsert(re_t *re, char *text, int sizetext) { long realend; int at_end; int nspaces; long cursorpos; long selstart,selend; int fixsel; char *ptr,*next; int len; int trimmed; if(re==NULL || text==NULL || sizetext<=0) return(-1); /* sanity check failed */ 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;nspaces<sizetext && text[nspaces]==' ';nspaces++) ; if(nspaces>0 && nspaces==sizetext && 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(!(text[0]=='\n' && sizetext==1) && 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; } if(redata_op_add(re->data,cursorpos,text,sizetext,NULL)!=0) return(-1); /* couldn't add requested text */ if(fixsel) { if(cursorpos<=selstart) redata_pos2linecol(re->data,selstart+sizetext,&(re->sellinefrom),&(re->selcolfrom)); if(cursorpos<=selend) redata_pos2linecol(re->data,selend+sizetext,&(re->sellineto),&(re->selcolto)); } for(ptr=text,next=memchr(text,'\n',sizetext);ptr!=NULL;ptr=(next!=NULL)?next+1:NULL,next=(ptr!=NULL)?memchr(ptr,'\n',sizetext-(ptr-text)):NULL) { len=(next!=NULL)?(next-ptr):sizetext-(ptr-text); re->curcol+=redata_generic_utf8len(ptr,len); cursorpos+=len; if(next!=NULL) { cursorpos++; if(re_rtrim(re,cursorpos-1,&trimmed)!=-1) cursorpos-=trimmed; re->curline++; re->curcol=0; } } /* trim last line inserted */ if(redata_getchar(re->data,cursorpos)=='\n' && re_rtrim(re,cursorpos,&trimmed)!=-1) cursorpos-=trimmed; redata_undo_groupcommit(re->data,NULL); re_fixorigin(re); re->headerdirty=1; re->contentsdirty=1; 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) { 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 re_textinsert(re,event->text.text,strlen(event->text.text)); } #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) { int trimmed; redata_undo_groupinit(re->data,NULL); redata_op_del(re->data,frompos,topos-frompos,NULL); redata_pos2linecol(re->data,frompos,&(re->curline),&(re->curcol)); if(redata_getchar(re->data,frompos)=='\n' && re_rtrim(re,frompos,&trimmed)!=-1) frompos-=trimmed; redata_undo_groupcommit(re->data,NULL); 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()) { char *ptr; re_selectbuf_replace(re,(ptr=SDL_GetClipboardText())); SDL_free(ptr),ptr=NULL; } if(SDL_HasClipboardText()) { char *ptr; re_selectbuf_replace(re,(ptr=SDL_GetClipboardText())); SDL_free(ptr),ptr=NULL; } if(re->usedselectbuf>0) re_textinsert(re, re->selectbuf,re->usedselectbuf); 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); }