Last active
August 29, 2015 14:14
-
-
Save ComputerNerd/411f38726467ee519afe to your computer and use it in GitHub Desktop.
Progress on the text editor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <ctype.h> | |
#include <fxcg/display.h> | |
#include <fxcg/keyboard.h> | |
#include "textEditGUI.hpp" | |
#include "graphicsProvider.hpp" | |
#define CURSOR_BLINK_RATE 128 //in ticks based on RTC_GetTicks(); | |
#define REPEAT_WAIT 128 //see comment for CURSOR_BLINK_RATE | |
struct linetxt{ | |
char*ptr;//May not be null terminated. | |
size_t len;//In bytes not characters. | |
bool newline; | |
}; | |
struct cursor{ | |
unsigned line; | |
unsigned chr;//first character on line. In bytes not elements. | |
}; | |
enum textSize{NORMAL /* PrintMint */,LARGE /* PrintCXY */,TEXTSIZES}; | |
static const int textHeight[]={18,24}; | |
static const int spaceWidth[]={7,18}; | |
static const unsigned maxLinesOnScreen[]={11,8}; | |
static unsigned tabOffset(unsigned tw,unsigned pxsz,unsigned pos){ | |
unsigned posold=pos; | |
tw*=pxsz; | |
if(!(pos%tw)) | |
pos+=pxsz; | |
pos+=tw-(pos%tw); | |
return pos-posold; | |
} | |
#define TEXT_MAX_WIDTH (384-6) //To account for the vertical scrollbar. | |
static int drawTextLine(const struct linetxt*line,unsigned offset,textSize txtSize,int y){ | |
int x=0; | |
char*ptr=line->ptr; | |
while(offset--){ | |
if(*ptr==0) | |
goto skipLoop; | |
++ptr; | |
} | |
if(*ptr==0) | |
goto skipLoop; | |
for(size_t ln=line->len;ln;){ | |
if(*ptr==0){ | |
if(x+spaceWidth[txtSize]>=TEXT_MAX_WIDTH) | |
break; | |
drawRectangle(x,y+24,spaceWidth[txtSize],textHeight[txtSize],31<<11); | |
x+=spaceWidth[txtSize]; | |
++ptr; | |
--ln; | |
}else if(*ptr=='\t'){ | |
unsigned off=tabOffset(4,spaceWidth[txtSize],x); | |
if(x+off>=TEXT_MAX_WIDTH) | |
break; | |
drawRectangle(x,y+24,off,textHeight[txtSize],0xFFFF); | |
x+=off; | |
++ptr; | |
--ln; | |
}else{ | |
switch(txtSize){ | |
case NORMAL: | |
{ | |
unsigned short width; | |
void*gl; | |
if(*ptr&128){ | |
unsigned short*p=(unsigned short*)ptr; | |
gl=GetMiniGlyphPtr(*p,&width); | |
}else | |
gl=GetMiniGlyphPtr(*ptr,&width); | |
if(x+width>=TEXT_MAX_WIDTH) | |
break; | |
else{ | |
PrintMiniGlyph(x,y,gl,0,width,0,0,0,0,0,0xFFFF,0); | |
x+=width; | |
} | |
} | |
break; | |
case LARGE: | |
if(x+18>=TEXT_MAX_WIDTH) | |
break; | |
else{ | |
PrintCXY(x,y,ptr,0,-1,0,0xFFFF,1,0); | |
x+=18; | |
} | |
break; | |
} | |
if(*ptr&128){ | |
ptr+=2; | |
if(ln>=2) | |
ln-=2; | |
else | |
break; | |
}else{ | |
++ptr; | |
--ln; | |
} | |
} | |
} | |
skipLoop: | |
if(x<TEXT_MAX_WIDTH) | |
drawRectangle(x,y+24,TEXT_MAX_WIDTH-x,textHeight[txtSize],0xFFFF); | |
return y+textHeight[txtSize]; | |
} | |
static void drawTextLines(const struct linetxt*firstLine,textSize txtSize,unsigned linesAmt){ | |
int y=0; | |
unsigned toDraw=maxLinesOnScreen[txtSize]; | |
if(toDraw>linesAmt) | |
toDraw=linesAmt; | |
while(toDraw--){ | |
y=drawTextLine(firstLine,0,txtSize,y); | |
++firstLine; | |
} | |
y+=24;//Account for the status area | |
if(y<216) | |
drawRectangle(0,y,384,216-y,0xFFFF); | |
} | |
static char*nextWord(char*start){//Returns zero if a newline character is before any words | |
char*end=start; | |
if(*end=='\r'||*end=='\n') | |
return 0; | |
for(;;){ | |
if(*end&128) | |
end+=2; | |
else if(!isspace(*end)) | |
break; | |
else | |
++end; | |
} | |
return end; | |
} | |
static const char*strnchrSkipMB(const char*str,size_t len,char chr){ | |
while(len){ | |
if(*str&128){ | |
str+=2; | |
if(len>=2) | |
len-=2; | |
else | |
return 0; | |
}else if(*str==chr) | |
return str; | |
else{ | |
++str; | |
--len; | |
} | |
} | |
return 0; | |
} | |
static unsigned insertChar(char*start,char*pos,unsigned ln,char c){ | |
memmove(pos+1,pos,ln-(pos-start)+1); | |
*pos=c; | |
return ln+1; | |
} | |
static bool chkLineAmt(struct linetxt**lines,unsigned&maxLines,unsigned&linesAmt){ | |
if(linesAmt>=(maxLines-1)){ | |
maxLines+=256; | |
*lines=(struct linetxt*)realloc(*lines,maxLines*sizeof(struct linetxt)); | |
} | |
return *lines?true:false; | |
} | |
static bool contigousToLines(char*buf,char*bufend,struct linetxt**lines,textSize txtSize,unsigned&maxLines,unsigned&linesAmt,size_t txtlen,bool wordWrap){ | |
linesAmt=0; | |
//Separate the text pointed by buf by line in order to allow for editing an existing text file. | |
for(char*ptr=buf;txtlen;){ | |
if(!chkLineAmt(lines,maxLines,linesAmt)) | |
return false; | |
lines[0][linesAmt].ptr=ptr; | |
char*ptrnew=(char*)strnchrSkipMB(ptr,txtlen,'\n'); | |
if(ptrnew){ | |
lines[0][linesAmt].len=ptrnew-ptr; | |
if(lines[0][linesAmt].ptr[lines[0][linesAmt].len-1]=='\r') | |
--lines[0][linesAmt].len; | |
txtlen-=ptrnew+1-ptr; | |
ptr=ptrnew+1; | |
lines[0][linesAmt++].newline=true; | |
}else{ | |
lines[0][linesAmt].newline=false; | |
lines[0][linesAmt++].len=txtlen; | |
break; | |
} | |
HourGlass(); | |
} | |
return true; | |
} | |
static int linesToContigous(char*buf,char*bufend,struct linetxt*lines,textSize txtSize,unsigned linesAmt){ | |
while(linesAmt--){ | |
HourGlass(); | |
} | |
return 0; | |
} | |
int textEditGUI(char*buf,size_t buflen,size_t startlen){ | |
/* The speed of random insertions and deletions in a text editor is | |
* important. This code trades off some processing time upfront for | |
* faster text editing. Each line is not contiguous in regards to the | |
* entire text file. When the users leaves the text editor the text is | |
* rearranged in place in order to form a contiguous text file. | |
* This code is designed to not rely on null termination of strings and | |
* instead stores the length of the text.*/ | |
struct linetxt*lines=(struct linetxt*)malloc(256*sizeof(struct linetxt)); | |
if(!lines) | |
return -1; | |
struct cursor cursor; | |
memset(&cursor,0,sizeof(struct cursor)); | |
unsigned maxLines=256;//How many lines before next reallocation | |
unsigned linesAmt; | |
textSize txtSize=NORMAL; | |
char*bufend=buf+buflen; | |
bool CRLF;//If a CRLF in the text file is detected when the user presses return insert a CRLF instead of a LF. | |
{ | |
char*ptr=(char*)strnchrSkipMB(buf,buflen,'\r'); | |
CRLF=ptr&&ptr[1]=='\n'; | |
} | |
if(!contigousToLines(buf,bufend,&lines,txtSize,maxLines,linesAmt,startlen)) | |
return -1;//Failure | |
bool exit=false,cancel=false; | |
struct scrollbar sb; | |
sb.I1 = 0; | |
sb.I5 = 0; | |
sb.indicatormaximum = linesAmt; | |
sb.indicatorheight = 1; | |
sb.barheight = 216; | |
sb.bartop = 0; | |
sb.barleft = 384 - 6; | |
sb.barwidth = 6; | |
for(unsigned i=192*384/2,*v=(unsigned*)0xA8004800;i--;*v++=0xFFFFFFFF); | |
while(!exit){ | |
//Draw the onscreen text. | |
if(linesAmt>=maxLinesOnScreen[txtSize]+1){ | |
if(cursor.line>(linesAmt-maxLinesOnScreen[txtSize]+1)) | |
cursor.line=linesAmt-maxLinesOnScreen[txtSize]+1; | |
}else | |
cursor.line=0; | |
sb.indicatorpos = cursor.line; | |
drawTextLines(lines+cursor.line,txtSize,linesAmt-cursor.line); | |
Scrollbar(&sb); | |
int key; | |
GetKey(&key); | |
switch(key){ | |
case KEY_CTRL_F1: | |
exit=true; | |
break; | |
case KEY_CTRL_F2: | |
case KEY_CTRL_EXIT: | |
exit=cancel=true; | |
break; | |
case KEY_CTRL_F3: | |
txtSize=(enum textSize)(int(txtSize)+1); | |
if(txtSize>LARGE) | |
txtSize=NORMAL; | |
break; | |
case KEY_CTRL_UP: | |
if(cursor.line>0) | |
--cursor.line; | |
break; | |
case KEY_CTRL_DOWN: | |
++cursor.line; | |
break; | |
} | |
} | |
//Convert the text in place to be contiguous | |
int len=linesToContigous(buf,bufend,lines,txtSize,linesAmt); | |
free(lines); | |
return cancel?-1:len; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment