/* * 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 "re_data.h" #include "re_ui.h" #include "ext/socklib.h" #define LINEFORCESCROLL 1 #define COLFORCESCROLL 5 typedef struct re_t { redata_t *data; reui_t *ui; int flag_newfile; char filename[PATH_MAX]; int x, y, w, h; // contents rect long cursorpos; int originline,origincol; int curline,curcol; int maxrow,maxcol; int contentsdirty; } 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); re_t *re_init(void); void re_free(re_t *re); int re_setfilename(re_t *re, char *filename); int re_processkey(re_t *re, SDL_Event *event); int re_drawheader(re_t *re); int re_drawcontents(re_t *re); int main(int argc, char *argv[]) { re_t *re; int do_exit; SDL_Event event; sselect *ssel; int flag_had_events; if(argc!=2 || strcmp(argv[argc-1],"--help")==0) { fprintf(stderr,"Syntax: %s filename\n",argv[0]); return(1); } if((re=re_init())==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); } if((redata_load(re->data,re->filename,NULL,NULL))!=0) re->flag_newfile=1; else re->flag_newfile=0; #if 0 #warning TESTS { char buf[129]; redata_hash(re->data,buf); fprintf(stderr,"%s %s\n",buf,re->filename); } #endif reui_title(re->ui,re->filename); flag_sigint=flag_sigpipe=0; setsignal(SIGINT,sighandler_sigint); setsignal(SIGPIPE,sighandler_sigpipe); do_exit=0; if((ssel=sselect_init())==NULL) { fprintf(stderr,"ERROR: filename too long.\n"); re_free(re),re=NULL; return(2); } reui_scr2renderer(re->ui,0,0,re->ui->w,re->ui->h); 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_drawheader(re); re_drawcontents(re); flag_had_events=0; while(do_exit==0 && flag_sigint==0) { if(re->contentsdirty) re_drawcontents(re); if(re->ui->rendererdirty) reui_present(re->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) { flag_had_events=10; switch(event.type) { case SDL_QUIT: do_exit=1; break; case SDL_KEYDOWN: re_processkey(re,&event); break; case SDL_WINDOWEVENT: if(event.window.event==SDL_WINDOWEVENT_SHOWN || event.window.event==SDL_WINDOWEVENT_EXPOSED) { re->ui->rendererdirty=1; } break; default: break; } } } 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; } re_t * re_init(void) { re_t *re; if((re=malloc(sizeof(re_t)))==NULL) return(NULL); /* insuf. mem. */ memset(re,0,sizeof(re_t)); if((re->data=redata_init(NULL))==NULL) { re_free(re),re=NULL; return(NULL); /* insuf. mem. */ } if((re->ui=reui_init())==NULL) { re_free(re),re=NULL; return(NULL); /* video init error */ } return(re); } void re_free(re_t *re) { if(re==NULL) return; /* all done */ if(re->ui!=NULL) reui_free(re->ui),re->ui=NULL; if(re->data!=NULL) redata_free(re->data),re->data=NULL; free(re),re=NULL; return; } 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_processkey(re_t *re, SDL_Event *event) { long newpos,newpos2; char *ptr,*ptr2; int len; int has_nl; int maxcol; int inc; if(re==NULL || event==NULL || event->type!=SDL_KEYDOWN) return(-1); /* sanity check failed */ if(event->key.keysym.sym==SDLK_DOWN || event->key.keysym.sym==SDLK_UP) { if(redata_line_info(re->data,re->cursorpos,&newpos,&ptr,&len)==-1) return(-1); /* couldn't get current line data */ if(event->key.keysym.sym==SDLK_UP && newpos==0) return(-1); /* going up but already at top */ #if 0 maxcol=redata_generic_utf8len(ptr,len); if(maxcol<re->curcol) re->cursorpos=newpos+len; else re->cursorpos=newpos+(redata_generic_utf8col(ptr,len,re->curcol)-ptr); #endif if(redata_line_info(re->data,(event->key.keysym.sym==SDLK_DOWN)?(newpos+len):(newpos-1),&newpos2,&ptr,&len)==-1) return(-1); /* couldn't get next line data */ has_nl=((len>0 && ptr[len-1]=='\n')?1:0); maxcol=redata_generic_utf8len(ptr,len)-has_nl; if(maxcol<re->curcol) re->cursorpos=newpos2+len-has_nl; else re->cursorpos=newpos2+(redata_generic_utf8col(ptr,len,re->curcol)-ptr); re->curline+=(event->key.keysym.sym==SDLK_DOWN)?1:-1; if(re->curline<(re->originline+LINEFORCESCROLL)) { re->originline-=LINEFORCESCROLL; re->originline=(re->originline<0)?0:re->originline; } if(re->curline>(re->originline+re->maxrow-LINEFORCESCROLL)) re->originline+=LINEFORCESCROLL; re_drawheader(re); re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_LEFT || event->key.keysym.sym==SDLK_RIGHT) { if(redata_line_info(re->data,re->cursorpos,&newpos,&ptr,&len)==-1) return(-1); /* couldn't get current line data */ if(event->key.keysym.sym==SDLK_LEFT && re->cursorpos==0) return(-1); /* going left but already at leftmost char */ maxcol=redata_generic_utf8len(ptr,len); inc=(event->key.keysym.sym==SDLK_LEFT)?-1:1; has_nl=((len>0 && ptr[len-1]=='\n')?1:0); if(re->curcol<=maxcol) ptr2=redata_generic_utf8col(ptr,len,re->curcol+inc); else ptr2=NULL; if(ptr2!=NULL) re->cursorpos=newpos+(ptr2-ptr); else re->cursorpos=newpos+(len-has_nl); /* we're past the last col, set cursor to last pos in line */ if((re->curcol+inc)>=0) re->curcol+=inc; if(re->curcol<(re->origincol+COLFORCESCROLL)) { re->origincol-=COLFORCESCROLL; re->origincol=(re->origincol<0)?0:re->origincol; } if(re->curcol>(re->origincol+re->maxcol-COLFORCESCROLL)) re->origincol+=COLFORCESCROLL; re_drawheader(re); re->contentsdirty=1; } else if(event->key.keysym.sym==SDLK_a) { ; } return(-1); } int re_drawheader(re_t *re) { int line,col; if(re==NULL) return(-1); if(redata_pos2linecol(re->data,re->cursorpos,&line,&col)==-1) line=col=-1; reui_fill(re->ui,0,0,re->ui->w,re->ui->fontheight,"\x00\x00\xff\xff"); reui_printf(re->ui,0,0,"\xff\xff\x00\xff","File: %s Line:%i (%i) Col:%i (%i) Pos:%li",re->filename,re->curline,line,re->curcol,col,re->cursorpos); return(0); } int re_drawcontents(re_t *re) { long pos,newpos; char *ptr,*visibleptr; int len; int y,row; char *curptr; int curptrlen; int has_nl; if(re==NULL) return(-1); reui_fill(re->ui,re->x,re->y,re->w,re->h,"\xdf\xdf\xdf\xff"); row=re->curline-re->originline; pos=re->cursorpos; while(row>0) { if(redata_line_info(re->data,pos,&newpos,NULL,NULL)==-1) return(-1); pos=(newpos>0)?newpos-1:0; row--; } /* highlight current line */ reui_fill(re->ui,re->x,re->y+(re->curline-re->originline)*re->ui->fontheight,re->w,re->ui->fontheight+1,"\xef\xef\xef\xff"); /* draw the lines */ for(y=re->y;y<(re->y+re->h);y+=re->ui->fontheight,row++) { if(redata_line_info(re->data,pos,&newpos,&ptr,&len)==-1) break; /* couldn't get line start pos */ has_nl=((len>0 && ptr[len-1]=='\n')?1:0); visibleptr=redata_generic_utf8col((char *)ptr,len-has_nl,re->origincol); if(visibleptr!=NULL) reui_write(re->ui,re->x,y,"\x00\x00\x00\xff",visibleptr,len-has_nl-(visibleptr-ptr)); if(row==(re->curline-re->originline)) { #warning DEBUG write of current char reui_fill(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,re->ui->fontwidth,re->ui->fontheight+1,"\x00\x00\x00\xff"); #warning TODO: consider tabs curptr=redata_generic_utf8col(ptr,len-has_nl,re->curcol); curptrlen=(curptr==NULL)?0:(len-has_nl)-(curptr-ptr); reui_write(re->ui,re->x+re->ui->fontwidth*(re->curcol-re->origincol),y,"\xff\xff\xff\xff",curptr,redata_generic_utf8charlen(ptr,curptrlen)); #warning TODO: if it is one of '[','{','<','>','}',']', highlight the matching bracket/parens/anglebracket. } pos=newpos+len; } re->contentsdirty=0; re->ui->rendererdirty=1; return(0); }