/** ** textarea.c ---- Mini GUI for MGRX, textarea (mini editor) ** ** Copyright (C) 2017,2019 Mariano Alvarez Fernandez ** [e-mail: malfer@telefonica.net] ** ** This file is part of the GRX graphics library. ** ** The GRX graphics library is free software; you can redistribute it ** and/or modify it under some conditions; see the "copying.grx" file ** for details. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** **/ #include #include #include #include "grguip.h" #define INITIAL_LINE_SIZE 10 #define INCR_LINE_SIZE 50 #define BLINK_TIME 500 typedef struct { unsigned short c; // glyph unsigned short u; // UCS2 char GrColor fg, bg; // colors int x; // x-pos int w; // width } TAChar; typedef struct { TAChar *cl; // char array int maxlen; // max chars (auto extended if necesary) int len; // actual number of chars long y; // y-pos int nextx; // next char x-pos } TALine; typedef struct _guiTextArea { GUIPanel *p; // panel container GrColor bgcolor; // bg color GrTextOption grt; // actual GrTextOption TALine *l; // line array int maxlines; // max lines int nlines; // actual number of lines int flscr; // first line on screen int nlscr; // lines on screen int xorg; // x origen value int xbits, ybits; // max x-bits, y-bits per char int tcstatus; // text cursor estatus (showed if >= 0) GrColor tccolor; // text cursor color int tclpos; // text cursor line pos int tccpos; // text cursor char pos int tcclast; // last text cursor char pos int tcblink; // text cursor blinking long tctime; // text cursor blink time int pressed; // left mouse pressed int full; // full, no more chars or lines can be added int fmline, fmch; // line&col first char of marked area int lmline, lmch; // line&col last char of marked area int pvmline, pvmch; // line&col pivot char to extend marked area } GUITextArea; static void move_tcursor(GUITextArea *ta, int nline, int nchar, int setmark); static void move_tcursor_rel(GUITextArea *ta, int incrl, int incrc, int setmark); static void show_tcursor(GUITextArea *ta); static void hide_tcursor(GUITextArea *ta); static void draw_tcursor(GUITextArea *ta, int show); static void blink_tcursor(GUITextArea *ta); static void redraw(GUITextArea *ta); static void redraw_line_from(GUITextArea *ta, int line, int c, int blit); static void redraw_char(GUITextArea *ta, int line, int c, int blit); static void scroll_up(GUITextArea *ta); static void scroll_down(GUITextArea *ta); static void scroll_toshow_tc(GUITextArea *ta); static void scroll_nlines_up(GUITextArea *ta, int nlines); static void scroll_nlines_down(GUITextArea *ta, int nlines); static void scroll_xorg(GUITextArea *ta, int newxorg); static int calc_line_col_ckick(GUITextArea *ta, int x, int y, int *l, int *c, int restrictxy); static int calc_maxx_in_screen(GUITextArea *ta); static void redraw_scbs(GUITextArea *ta); static void print_char(GUITextArea *ta, long ch, int chrtype, int draw); static void new_line(GUITextArea *ta, int draw); static void add_char(GUITextArea *ta, long ch, int chrtype, int draw); static void add_string(GUITextArea *ta, void *s, int len, int chrtype, int draw); static void join_lines(GUITextArea *ta, int draw); static void delete_char(GUITextArea *ta, int draw); static void erase_char(GUITextArea *ta, int draw); static int in_marqued_area(GUITextArea *ta, int line, int c); static void reset_marqued_area(GUITextArea *ta, int draw); static void expand_marqued_area(GUITextArea *ta, int draw); static int delete_marqued_area(GUITextArea *ta, int draw); static void paste_marqued_area(GUITextArea *ta, int draw); static void copy_marqued_area(GUITextArea *ta, int clear, int draw); static void nd_del_line(GUITextArea *ta, int line); static void nd_del_char_from(GUITextArea *ta, int line, int c); static void nd_del_char_to(GUITextArea *ta, int line, int c); static void nd_del_char_from_to(GUITextArea *ta, int line, int fc, int lc); static int nd_join_lines(GUITextArea *ta, int line); static unsigned short * nd_get_ucs2text(GUITextArea *ta, int fline, int fc, int lline, int lc, int *len); static void nd_put_ucs2text(GUITextArea *ta, unsigned short *buf, int len, int line, int c); static int nd_add_char(GUITextArea *ta, int line, int c, long ch, int chrtype); static int nd_new_line(GUITextArea *ta, int line, int c); /**************************************************************************/ GUITextArea * GUITACreate(GUIPanel *p, GrFont *f, int maxlines) { GUITextArea *ta; int i; if (maxlines < 1) return NULL; ta = malloc(sizeof(GUITextArea)); if (ta == NULL) return NULL; ta->l = malloc(sizeof(TALine)*maxlines); if (ta->l == NULL) { free(ta); return NULL; } ta->l[0].cl = malloc(sizeof(TAChar)*INITIAL_LINE_SIZE); if (ta->l[0].cl == NULL) { free(ta->l); free(ta); return NULL; } ta->p = p; ta->bgcolor = GrBlack(); ta->grt.txo_font = (f) ? f : GrGetDefaultFont(); ta->grt.txo_fgcolor = GrWhite(); ta->grt.txo_bgcolor = GrBlack(); ta->grt.txo_direct = GR_TEXT_RIGHT; ta->grt.txo_xalign = GR_ALIGN_LEFT; ta->grt.txo_yalign = GR_ALIGN_BOTTOM; ta->grt.txo_chrtype = GR_WORD_TEXT; ta->maxlines = maxlines; ta->nlines = 1; ta->flscr = 0; ta->xbits = ta->grt.txo_font->maxwidth; ta->ybits = ta->grt.txo_font->h.height; //ta->nlscr = (ta->p->cl->gc_ymax+1) / ta->ybits; ta->nlscr = (ta->p->cl->gc_ymax) / ta->ybits; ta->xorg = 0; ta->tcstatus = -1; ta->tccolor = GrWhite(); ta->tclpos = 0; ta->tccpos = 0; ta->tcclast = 0; ta->tcblink = 1; ta->tctime = 0; ta->pressed = 0; ta->full = 0; ta->l[0].len = 0; ta->l[0].maxlen = INITIAL_LINE_SIZE; ta->l[0].y = ta->ybits; ta->l[0].nextx = 0; for (i=1; imaxlines; i++) { ta->l[i].cl = NULL; ta->l[i].len = 0; ta->l[i].maxlen = 0; ta->l[i].y = ta->ybits * (i + 1); ta->l[i].nextx = 0; } reset_marqued_area(ta, 0); return ta; } /**************************************************************************/ void GUITASetFont(GUITextArea *ta, GrFont *f) { int i, j; long ybits; if (ta && f) { ta->grt.txo_font = f; ta->xbits = ta->grt.txo_font->maxwidth; ta->ybits = ta->grt.txo_font->h.height; //ta->nlscr = (ta->p->cl->gc_ymax+1) / ta->ybits; ta->nlscr = (ta->p->cl->gc_ymax) / ta->ybits; //GUITAClear(ta); ta->l[ta->flscr].y = ta->ybits; ybits = 0; for (i=ta->flscr-1; i>=0; i--) { ta->l[i].y = ybits; ybits -= ta->ybits; } ybits = ta->ybits * 2; for (i=ta->flscr+1; imaxlines; i++) { ta->l[i].y = ybits; ybits += ta->ybits; } for (i=0; inlines; i++) { ta->l[i].nextx = 0; for (j=0; jl[i].len; j++) { ta->l[i].cl[j].x = ta->l[i].nextx; ta->l[i].cl[j].c = GrFontCharRecode(ta->grt.txo_font, ta->l[i].cl[j].u, GR_UCS2_TEXT); ta->l[i].cl[j].w = GrFontCharWidth(ta->grt.txo_font, ta->l[i].cl[j].c); ta->l[i].nextx += ta->l[i].cl[j].w; } } } } /**************************************************************************/ void GUITASetBgColor(GUITextArea *ta, GrColor c) { if (ta == NULL) return; ta->bgcolor = c; } /**************************************************************************/ void GUITASetTextColors(GUITextArea *ta, GrColor fg, GrColor bg) { if (ta == NULL) return; ta->grt.txo_fgcolor = fg; ta->grt.txo_bgcolor = bg; } /**************************************************************************/ void GUITASetCursorColor(GUITextArea *ta, GrColor c) { if (ta == NULL) return; ta->tccolor = c; } /**************************************************************************/ void GUITADestroy(GUITextArea *ta) { int i; if (ta == NULL) return; for (i=0; imaxlines; i++) { free(ta->l[i].cl); } free(ta->l); free(ta); } /**************************************************************************/ void GUITAShowCursor(GUITextArea *ta) { GrContext ctxsave; if (ta == NULL) return; ta->tcstatus++; if (ta->tcstatus == 0) { GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); draw_tcursor(ta, 1); GrSetContext(&ctxsave); } } /**************************************************************************/ void GUITAHideCursor(GUITextArea *ta) { GrContext ctxsave; if (ta == NULL) return; ta->tcstatus--; if (ta->tcstatus == -1) { GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); draw_tcursor(ta, 0); GrSetContext(&ctxsave); } } /**************************************************************************/ void GUITAMoveCursor(GUITextArea *ta, int nline, int nchar, int setmark) { GrContext ctxsave; if (ta == NULL) return; GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); move_tcursor(ta, nline, nchar, setmark); GrSetContext(&ctxsave); } /**************************************************************************/ void GUITAMoveCursorRel(GUITextArea *ta, int incrl, int incrc, int setmark) { GrContext ctxsave; if (ta == NULL) return; GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); move_tcursor_rel(ta, incrl, incrc, setmark); GrSetContext(&ctxsave); } /**************************************************************************/ void GUITAReDraw(GUITextArea *ta) { GrContext ctxsave; if (ta == NULL) return; GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); hide_tcursor(ta); GrClearContext(ta->bgcolor); redraw(ta); show_tcursor(ta); GrSetContext(&ctxsave); } /**************************************************************************/ void GUITAClear(GUITextArea *ta) { int i; if (ta == NULL) return; ta->flscr = 0; ta->nlines = 1; for (i=0; imaxlines; i++) { ta->l[i].len = 0; ta->l[i].y = ta->ybits * (i + 1); ta->l[i].nextx = 0; } ta->xorg = 0; ta->tcstatus = -1; ta->tclpos = 0; ta->tccpos = 0; ta->tcclast = 0; ta->tcblink = 1; ta->tctime = 0; ta->pressed = 0; ta->full = 0; reset_marqued_area(ta, 0); } /**************************************************************************/ void GUITANewLine(GUITextArea *ta) { GrContext ctxsave; if (ta == NULL) return; if (ta->full) return; GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); hide_tcursor(ta); new_line(ta, 1); scroll_toshow_tc(ta); show_tcursor(ta); GrSetContext(&ctxsave); } /**************************************************************************/ void GUITAPutChar(GUITextArea *ta, long ch, int chrtype) { GrContext ctxsave; if (ta == NULL) return; if (ta->full) return; GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); hide_tcursor(ta); add_char(ta, ch, chrtype, 1); //scroll_toshow_tc(ta); show_tcursor(ta); GrSetContext(&ctxsave); } /**************************************************************************/ void GUITAPutString(GUITextArea *ta, void *s, int len, int chrtype) { GrContext ctxsave; if (ta == NULL) return; if (ta->full) return; GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); hide_tcursor(ta); add_string(ta, s, len, chrtype, 1); //scroll_toshow_tc(ta); show_tcursor(ta); GrSetContext(&ctxsave); } /**************************************************************************/ void GUITAPrintChar(GUITextArea *ta, long ch, int chrtype) { GrContext ctxsave; if (ta == NULL) return; if (ta->full) return; GrSaveContext(&ctxsave); GrSetContext(ta->p->cl); print_char(ta, ch, chrtype, 1); scroll_toshow_tc(ta); GrSetContext(&ctxsave); } /**************************************************************************/ void GUITAGetStatus(GUITextArea *ta, GUITAStatus *tast) { if (ta == NULL) return; tast->nlines = ta->nlines; tast->tclpos = ta->tclpos; tast->tccpos = ta->tccpos; tast->ncscr = (ta->p->cl->gc_xmax+1) / ta->xbits; tast->fmline = ta->fmline; tast->fmch = ta->fmch; tast->lmline = ta->lmline; tast->lmch = ta->lmch; tast->full = ta->full; } /**************************************************************************/ void *GUITAGetString(GUITextArea *ta, int nline, int chrtype) { unsigned char *s; unsigned short *h; long laux; unsigned char *cutf8; int i, j, len; if (ta == NULL) return NULL; if (nline < 0 || nline >= ta->nlines) return NULL; len = ta->l[nline].len; switch (chrtype) { case GR_BYTE_TEXT: case GR_CP437_TEXT: case GR_CP850_TEXT: case GR_CP1252_TEXT: case GR_ISO_8859_1_TEXT: s = malloc(len+1); if (s == NULL) return NULL; for (i=0; il[nline].cl[i].u, chrtype); s[len] = '\0'; return s; case GR_UTF8_TEXT: s = malloc((len*4)+1); if (s == NULL) return NULL; j = 0; cutf8 = (unsigned char *)&laux; for (i=0; il[nline].cl[i].u, chrtype); s[j++] = cutf8[0]; if (cutf8[1] != '\0') s[j++] = cutf8[1]; if (cutf8[2] != '\0') s[j++] = cutf8[2]; if (cutf8[3] != '\0') s[j++] = cutf8[3]; } s[j] = '\0'; return s; case GR_WORD_TEXT: case GR_UCS2_TEXT: h = malloc((len+1)*sizeof(unsigned short)); if (h == NULL) return NULL; for (i=0; il[nline].cl[i].u; h[len] = '\0'; return h; } return NULL; } /**************************************************************************/ int GUITAProcessEvent(GUITextArea *ta, GrEvent *ev) { // because called by panel, the context is expected to be set int l, c, range; if (ev->type == GREV_MOUSE) { if (ev->p1 == GRMOUSE_LB_PRESSED) { if (calc_line_col_ckick(ta, ev->p2, ev->p3, &l, &c, 1)){ ta->pressed = 1; move_tcursor(ta, l, c, 1); ta->tcclast = ta->tccpos; scroll_toshow_tc(ta); } return 1; } else if (ev->p1 == GRMOUSE_B4_RELEASED) { //move_tcursor_rel(ta, -3, 0, 1); scroll_nlines_up(ta, 3); //scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GRMOUSE_B5_RELEASED) { //move_tcursor_rel(ta, 3, 0, 1); scroll_nlines_down(ta, 3); //scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GRMOUSE_LB_RELEASED) { ta->pressed = 0; return 1; } } else if (ev->type == GREV_MMOVE) { if ((ev->p1 == GRMOUSE_LB_PRESSED) && ta->pressed) { if (calc_line_col_ckick(ta, ev->p2, ev->p3, &l, &c, 0)){ move_tcursor(ta, l, c, -1); ta->tcclast = ta->tccpos; scroll_toshow_tc(ta); } return 1; } } else if (ev->type == GREV_SCBVERT) { range = ta->flscr - ev->p1; if (range > 0) scroll_nlines_up(ta, range); if (range < 0) scroll_nlines_down(ta, -range); return 1; } else if (ev->type == GREV_SCBHORZ) { scroll_xorg(ta, ev->p1); return 1; } else if ((ev->type == GREV_KEY) && (ev->p2 != GRKEY_KEYCODE)) { if (ev->p1 == GrKey_Control_C) { copy_marqued_area(ta, 0, 0); return 1; } else if (ev->p1 == GrKey_Control_V) { paste_marqued_area(ta, 1); scroll_toshow_tc(ta); } else if (ev->p1 == GrKey_Control_X) { copy_marqued_area(ta, 1, 1); scroll_toshow_tc(ta); } else { print_char(ta, ev->p1, GrGetChrtypeForUserEncoding(), 1); scroll_toshow_tc(ta); } return 1; } else if ((ev->type == GREV_KEY) && (ev->p2 == GRKEY_KEYCODE)) { if (ev->p1 == GrKey_Delete) { print_char(ta, 0x7f, GrGetChrtypeForUserEncoding(), 1); scroll_toshow_tc(ta); } else if (ev->p1 == GrKey_Left) { move_tcursor_rel(ta, 0, -1, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Right) { move_tcursor_rel(ta, 0, 1, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Up) { move_tcursor_rel(ta, -1, 0, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Down) { move_tcursor_rel(ta, 1, 0, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Home) { move_tcursor_rel(ta, 0, -ta->tccpos, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_End) { move_tcursor_rel(ta, 0, ta->l[ta->tclpos].len - ta->tccpos, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Control_Home) { move_tcursor_rel(ta, -ta->tclpos, -ta->tccpos, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Control_End) { move_tcursor_rel(ta, ta->nlines - ta->tclpos - 1, ta->l[ta->nlines-1].len - ta->tccpos, 1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_PageUp) { move_tcursor_rel(ta, -(ta->nlscr-1), 0, 1); scroll_nlines_up(ta, ta->nlscr-1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_PageDown) { move_tcursor_rel(ta, ta->nlscr-1, 0, 1); scroll_nlines_down(ta, ta->nlscr-1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_Left) { move_tcursor_rel(ta, 0, -1, -1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_Right) { move_tcursor_rel(ta, 0, 1, -1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_Up) { move_tcursor_rel(ta, -1, 0, -1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_Down) { move_tcursor_rel(ta, 1, 0, -1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_Home) { move_tcursor_rel(ta, 0, -ta->tccpos, -1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_End) { move_tcursor_rel(ta, 0, ta->l[ta->tclpos].len - ta->tccpos, -1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_PageUp) { move_tcursor_rel(ta, -(ta->nlscr-1), 0, -1); scroll_nlines_up(ta, ta->nlscr-1); scroll_toshow_tc(ta); return 1; } else if (ev->p1 == GrKey_Shift_PageDown) { move_tcursor_rel(ta, ta->nlscr-1, 0, -1); scroll_nlines_down(ta, ta->nlscr-1); scroll_toshow_tc(ta); return 1; } } else if ((ev->type == GREV_NULL) && (ta->tcstatus >= 0)) { if (ta->tctime == 0) { ta->tctime = ev->time; } else { if (ev->time > ta->tctime + BLINK_TIME) { ta->tctime = ev->time; ta->tcblink ^= 1; blink_tcursor(ta); } } } return 0; } /**************************************************************************/ static void move_tcursor(GUITextArea *ta, int nline, int nchar, int setmark) { /* internal version, assume ta != NULL and context setted */ if (nline < 0) nline = 0; if (nline >= ta->nlines) nline = ta->nlines - 1; if (nchar < 0) nchar = 0; if (nchar > ta->l[nline].len) nchar = ta->l[nline].len; hide_tcursor(ta); ta->tclpos = nline; ta->tccpos = nchar; if (setmark == 1) reset_marqued_area(ta, 1); if (setmark == -1) expand_marqued_area(ta, 1); show_tcursor(ta); } /**************************************************************************/ static void move_tcursor_rel(GUITextArea *ta, int incrl, int incrc, int setmark) { int nline, nchar; int save_tcclast = 0; nline = ta->tclpos + incrl; nchar = ta->tccpos + incrc; if (incrl == 0) { if ((nchar < 0) && (nline > 0)) { nline -= 1; nchar = ta->l[nline].len; } if ((nchar > ta->l[nline].len) && (nline < ta->nlines-1)) { nline += 1; nchar = 0; } } if (incrc == 0) { nchar = ta->tcclast; } else { save_tcclast = 1; } move_tcursor(ta, nline, nchar, setmark); if (save_tcclast) ta->tcclast = ta->tccpos; } /**************************************************************************/ static void show_tcursor(GUITextArea *ta) /* internal version, assume ta != NULL and context setted */ { ta->tcstatus++; if (ta->tcstatus == 0) { draw_tcursor(ta, 1); } } /**************************************************************************/ static void hide_tcursor(GUITextArea *ta) /* internal version, assume ta != NULL and context setted */ { ta->tcstatus--; if (ta->tcstatus == -1) { draw_tcursor(ta, 0); } } /**************************************************************************/ static void draw_tcursor(GUITextArea *ta, int show) { TALine *l; int xpos_org, xpos, ypos; if (ta->tclpos < ta->flscr) return; l = &(ta->l[ta->tclpos]); ypos = l->y; if (ypos > GrMaxY()) return; xpos_org = (ta->tccpos >= l->len) ? l->nextx : l->cl[ta->tccpos].x; xpos = xpos_org - ta->xorg; if ((xpos < 0) || (xpos > GrMaxX())) return; if (show) { GrVLine(xpos, ypos, ypos-ta->ybits+1, ta->tccolor); GrVLine(xpos+1, ypos, ypos-ta->ybits+1, ta->tccolor); GUIPanelBltRectClToScreen(ta->p, xpos, ypos-ta->ybits+1, 2, ta->ybits); ta->tctime = 0; // reset blink } else if (ta->tccpos >= l->len) { GrVLine(xpos, ypos, ypos-ta->ybits+1, ta->bgcolor); GrVLine(xpos+1, ypos, ypos-ta->ybits+1, ta->bgcolor); GUIPanelBltRectClToScreen(ta->p, xpos, ypos-ta->ybits+1, 2, ta->ybits); } else { redraw_char(ta, ta->tclpos, ta->tccpos, 1); } } /**************************************************************************/ static void blink_tcursor(GUITextArea *ta) { // routine used "only" by the blink code when tc is showed TALine *l; int xpos_org, xpos, ypos; if (ta->tclpos < ta->flscr) return; l = &(ta->l[ta->tclpos]); ypos = l->y; if (ypos > GrMaxY()) return; xpos_org = (ta->tccpos >= l->len) ? l->nextx : l->cl[ta->tccpos].x; xpos = xpos_org - ta->xorg; if ((xpos < 0) || (xpos > GrMaxX())) return; if (ta->tcblink) { GrVLine(xpos, ypos, ypos-ta->ybits+1, ta->tccolor); GrVLine(xpos+1, ypos, ypos-ta->ybits+1, ta->tccolor); GUIPanelBltRectClToScreen(ta->p, xpos, ypos-ta->ybits+1, 2, ta->ybits); } else if (ta->tccpos >= l->len) { GrVLine(xpos, ypos, ypos-ta->ybits+1, ta->bgcolor); GrVLine(xpos+1, ypos, ypos-ta->ybits+1, ta->bgcolor); GUIPanelBltRectClToScreen(ta->p, xpos, ypos-ta->ybits+1, 2, ta->ybits); } else { //GrVLine(xpos, ypos, ypos-ta->ybits+1, ta->bgcolor); //GrVLine(xpos+1, ypos, ypos-ta->ybits+1, ta->bgcolor); //GUIPanelBltRectClToScreen(ta->p, xpos, ypos-ta->ybits+1, 2, ta->ybits); redraw_char(ta, ta->tclpos, ta->tccpos, 1); } } /**************************************************************************/ static void redraw(GUITextArea *ta) { int i, cl; cl = 0; for (i=ta->flscr; inlines; i++) { redraw_line_from(ta, i, 0, 0); cl++; if (ta->l[i].y > GrMaxY()) break; } i = ta->nlscr + 1 - cl; if (i > 0) GrFilledBox(0, (cl*ta->ybits)+1, GrMaxX(), (cl+i)*ta->ybits, ta->bgcolor); GUIPanelBltRectClToScreen(ta->p, 0, 0, GrSizeX(), GrSizeY()); redraw_scbs(ta); } /**************************************************************************/ static void redraw_line_from(GUITextArea *ta, int line, int c, int blit) { int j, x1; if (ta->l[line].y > (GrMaxY()+ta->ybits)) return; for (j=c; jl[line].len; j++) { redraw_char(ta, line, j, 0); } x1 = ta->l[line].nextx - ta->xorg; if (x1 < 0) x1 = 0; if (x1 < GrSizeX()) GrFilledBox(x1, ta->l[line].y-ta->ybits+1, GrMaxX(), ta->l[line].y, ta->bgcolor); x1 = (c >= ta->l[line].len) ? ta->l[line].nextx - ta->xorg : ta->l[line].cl[c].x - ta->xorg; if (x1 < 0) x1 = 0; if (x1 < GrSizeX() && blit) GUIPanelBltRectClToScreen(ta->p, x1, ta->l[line].y-ta->ybits+1, GrSizeX(), ta->ybits); } /**************************************************************************/ static void redraw_char(GUITextArea *ta, int line, int c, int blit) { GrTextOption grt2; if (ta->l[line].y > (GrMaxY()+ta->ybits)) return; if (c >= ta->l[line].len) return; grt2 = ta->grt; if (in_marqued_area(ta, line, c)) { grt2.txo_fgcolor = ta->l[line].cl[c].bg; grt2.txo_bgcolor = ta->l[line].cl[c].fg; } else { grt2.txo_fgcolor = ta->l[line].cl[c].fg; grt2.txo_bgcolor = ta->l[line].cl[c].bg; } GrDrawChar(ta->l[line].cl[c].c, ta->l[line].cl[c].x-ta->xorg, ta->l[line].y, &(grt2)); if (blit) GUIPanelBltRectClToScreen(ta->p, ta->l[line].cl[c].x-ta->xorg, ta->l[line].y-ta->ybits+1, ta->l[line].cl[c].w, ta->ybits); } /**************************************************************************/ static void scroll_up(GUITextArea *ta) { int i; if (ta->flscr == 0) return; ta->flscr -= 1; for (i=0; inlines; i++) { ta->l[i].y += ta->ybits; } } /**************************************************************************/ static void scroll_down(GUITextArea *ta) { int i; ta->flscr += 1; for (i=0; inlines; i++) { ta->l[i].y -= ta->ybits; } } /**************************************************************************/ static void scroll_toshow_tc(GUITextArea *ta) { TALine *l; int xpos_org, xpos; int need_redraw = 0; l = &(ta->l[ta->tclpos]); while (l->y > GrMaxY()) { scroll_down(ta); need_redraw = 1; } while (l->y < ta->ybits) { scroll_up(ta); need_redraw = 1; } xpos_org = (ta->tccpos >= l->len) ? l->nextx : l->cl[ta->tccpos].x; xpos = xpos_org - ta->xorg; while (xpos < 0) { ta->xorg -= ta->xbits; if (ta->xorg < 0) ta->xorg = 0; need_redraw = 1; xpos = xpos_org - ta->xorg; } while (xpos > GrMaxX()) { ta->xorg += ta->xbits; need_redraw = 1; xpos = xpos_org - ta->xorg; } if (need_redraw) { hide_tcursor(ta); redraw(ta); show_tcursor(ta); } } /**************************************************************************/ static void scroll_nlines_up(GUITextArea *ta, int nlines) { int i; if (ta->flscr - nlines < 0) nlines = ta->flscr; if (nlines <= 0) return; hide_tcursor(ta); ta->flscr -= nlines; for (i=0; inlines; i++) { ta->l[i].y += ta->ybits * nlines; } redraw(ta); show_tcursor(ta); } /**************************************************************************/ static void scroll_nlines_down(GUITextArea *ta, int nlines) { int i, llscr; llscr = ta->flscr + ta->nlscr - 1; if (llscr + nlines >= ta->nlines) { nlines = ta->nlines - 1 - llscr; } if (nlines <= 0) return; hide_tcursor(ta); ta->flscr += nlines; for (i=0; inlines; i++) { ta->l[i].y -= ta->ybits * nlines; } redraw(ta); show_tcursor(ta); } /**************************************************************************/ static void scroll_xorg(GUITextArea *ta, int newxorg) { if (newxorg < 0) newxorg = 0; if (ta->xorg != newxorg) { ta->xorg = newxorg; hide_tcursor(ta); redraw(ta); show_tcursor(ta); } } /**************************************************************************/ static int calc_line_col_ckick(GUITextArea *ta, int x, int y, int *l, int *c, int restrictxy) { TALine *tali; int i; if (restrictxy && (x < 0 || x > GrMaxX())) return 0; if (restrictxy && (y < 0 || y > GrMaxY())) return 0; *l = y / ta->ybits; if (*l > ta->nlscr) *l = ta->nlscr; *l += ta->flscr; if (*l >= ta->nlines) *l = ta->nlines - 1; if (*l < 0) *l = 0; tali = &(ta->l[*l]); *c = 0; x += ta->xorg; if (x >= tali->nextx) *c = tali->len; else { for (i=tali->len-1; i>=0; i--) { if (x > tali->cl[i].x+tali->cl[i].w/2) { *c = i+1; break; } } } return 1; } /**************************************************************************/ static int calc_maxx_in_screen(GUITextArea *ta) { int i, maxx; maxx = 0; for (i=0; inlscr; i++) { if ((i+ta->flscr) > ta->nlines) break; if (ta->l[i+ta->flscr].nextx > maxx) maxx = ta->l[i+ta->flscr].nextx; } return maxx; } /**************************************************************************/ static void redraw_scbs(GUITextArea *ta) { int max; if (ta->p->vscb) { max = ta->nlines; if (max < ta->nlscr) max = ta->nlscr; GUIScrollbarSetLimits(ta->p->vscb, 0, max, ta->flscr, ta->flscr+ta->nlscr); } if (ta->p->hscb) { max = calc_maxx_in_screen(ta); if (max < GrMaxX()) max = GrMaxX(); GUIScrollbarSetLimits(ta->p->hscb, 0, max, ta->xorg, ta->xorg+GrMaxX()); } GUIPanelRePaintScb(ta->p); } /**************************************************************************/ /* edit function from the text cursor position, and call redraw screen */ /* they assume the context is set and the tex cursor hidden */ /* excepting print_char */ /**************************************************************************/ static void print_char(GUITextArea *ta, long ch, int chrtype, int draw) { if (ta->full) return; if (draw) hide_tcursor(ta); if (ch == '\r') { new_line(ta, draw); } else if (ch == '\b') { delete_char(ta, draw); } else if (ch == 0x7f) { erase_char(ta, draw); } else { add_char(ta, ch, chrtype, draw); } if (draw) show_tcursor(ta); } /**************************************************************************/ static void new_line(GUITextArea *ta, int draw) { delete_marqued_area(ta, draw); if (!nd_new_line(ta, ta->tclpos, ta->tccpos)) return; ta->tclpos += 1; ta->tccpos = 0; ta->tcclast = ta->tccpos; reset_marqued_area(ta, 0); if (ta->l[ta->tclpos].y > GrMaxY()) scroll_down(ta); if (draw) redraw(ta); } /**************************************************************************/ static void add_char(GUITextArea *ta, long ch, int chrtype, int draw) { delete_marqued_area(ta, draw); if (!nd_add_char(ta, ta->tclpos, ta->tccpos, ch, chrtype)) return; ta->tccpos += 1; ta->tcclast = ta->tccpos; reset_marqued_area(ta, 0); if (draw) redraw_line_from(ta, ta->tclpos, ta->tccpos-1, 1); } /**************************************************************************/ static void add_string(GUITextArea *ta, void *s, int len, int chrtype, int draw) { unsigned short *text2; int i, newchrtype, ind; if (len <= 0) len = GrStrLen(s, chrtype); text2 = GrTextRecodeToUCS2(s, len, chrtype); if (text2 == NULL) return; newchrtype = GR_UCS2_TEXT; if (chrtype == GR_BYTE_TEXT || chrtype == GR_WORD_TEXT) newchrtype = GR_WORD_TEXT; // so we can draw glyphs if necesary delete_marqued_area(ta, draw); ind = 0; for (i=0; itclpos, ta->tccpos+ind, text2[i], newchrtype)) break; ind++; } free(text2); ta->tccpos += ind; ta->tcclast = ta->tccpos; reset_marqued_area(ta, 0); if (draw) redraw_line_from(ta, ta->tclpos, ta->tccpos-ind, 1); } /**************************************************************************/ static void join_lines(GUITextArea *ta, int draw) { // used only by delete_char and erase_char // so do not need delete_marqued_area by now int olen1; if (ta->tclpos < 1) return; olen1 = ta->l[ta->tclpos-1].len; if (!nd_join_lines(ta, ta->tclpos)) return; ta->tclpos -= 1; ta->tccpos = olen1; ta->tcclast = ta->tccpos; if (ta->l[ta->tclpos].y < ta->ybits) scroll_up(ta); reset_marqued_area(ta, 0); if (draw) redraw(ta); } /**************************************************************************/ static void delete_char(GUITextArea *ta, int draw) { TALine *l; int i, w; if (delete_marqued_area(ta, draw)) return; if (ta->tccpos < 1) { join_lines(ta, draw); return; } l = &(ta->l[ta->tclpos]); if (l->len < 1) return; w = l->cl[ta->tccpos-1].w; for (i=ta->tccpos-1; ilen-1; i++) { l->cl[i] = l->cl[i+1]; l->cl[i].x -= w; } l->len -= 1; l->nextx -= w; ta->tccpos -= 1; ta->tcclast = ta->tccpos; reset_marqued_area(ta, 0); if (draw) redraw_line_from(ta, ta->tclpos, ta->tccpos, 1); } /**************************************************************************/ static void erase_char(GUITextArea *ta, int draw) { TALine *l; int i, w; if (delete_marqued_area(ta, draw)) return; l = &(ta->l[ta->tclpos]); if (ta->tccpos >= l->len) { if (ta->tclpos < ta->nlines-1) { ta->tclpos += 1; ta->tccpos = 0; join_lines(ta, draw); } return; } w = l->cl[ta->tccpos].w; for (i=ta->tccpos; ilen-1; i++) { l->cl[i] = l->cl[i+1]; l->cl[i].x -= w; } l->len -= 1; l->nextx -= w; ta->tcclast = ta->tccpos; reset_marqued_area(ta, 0); if (draw) redraw_line_from(ta, ta->tclpos, ta->tccpos, 1); } /**************************************************************************/ /* marqued area functions */ /**************************************************************************/ static int in_marqued_area(GUITextArea *ta, int line, int c) { if ((line > ta->fmline) && (line < ta->lmline)) return 1; if ((line == ta->fmline) && (c < ta->fmch)) return 0; if ((line == ta->lmline) && (c >= ta->lmch)) return 0; if ((line == ta->fmline) || (line == ta->lmline)) return 1; return 0; } /**************************************************************************/ static void reset_marqued_area(GUITextArea *ta, int draw) { int fmline, lmline, fmch, lmch; int i; fmline = ta->fmline; lmline = ta->lmline; fmch = ta->fmch; lmch = ta->lmch; ta->fmline = ta->lmline = ta->pvmline = ta->tclpos; ta->fmch = ta->lmch = ta->pvmch = ta->tccpos; if (draw) { if ((fmline == lmline) && (fmch == lmch)) return; hide_tcursor(ta); for (i=fmline; i<=lmline; i++) { if (i >= ta->flscr) redraw_line_from(ta, i, 0, 1); } show_tcursor(ta); } } /**************************************************************************/ static void expand_marqued_area(GUITextArea *ta, int draw) { int fmline, lmline; int minline, maxline; int left, i; fmline = ta->fmline; lmline = ta->lmline; left = 0; if (ta->tclpos < ta->pvmline) left = 1; if ((ta->tclpos == ta->pvmline) && (ta->tccpos < ta->pvmch)) left = 1; if (left) { ta->fmline = ta->tclpos; ta->fmch = ta->tccpos; ta->lmline = ta->pvmline; ta->lmch = ta->pvmch; } else { ta->fmline = ta->pvmline; ta->fmch = ta->pvmch; ta->lmline = ta->tclpos; ta->lmch = ta->tccpos; } if (draw) { minline = (fmline < ta->fmline) ? fmline : ta->fmline; maxline = (lmline > ta->lmline) ? lmline : ta->lmline; hide_tcursor(ta); for (i=minline; i<=maxline; i++) if (i >= ta->flscr) redraw_line_from(ta, i, 0, 1); show_tcursor(ta); } } /**************************************************************************/ static int delete_marqued_area(GUITextArea *ta, int draw) { int i; if ((ta->fmline == ta->lmline) && (ta->fmch == ta->lmch)) return 0; for (i=0; i<(ta->lmline - ta->fmline - 1); i++) { nd_del_line(ta, ta->fmline+1); } if (ta->lmline != ta->fmline) { nd_del_char_from(ta, ta->fmline, ta->fmch); nd_del_char_to(ta, ta->fmline+1, ta->lmch); nd_join_lines(ta, ta->fmline+1); } else { nd_del_char_from_to(ta, ta->fmline, ta->fmch, ta->lmch); } ta->tclpos = ta->fmline; ta->tccpos = ta->fmch; ta->tcclast = ta->tccpos; reset_marqued_area(ta, 0); if (draw) { hide_tcursor(ta); while (ta->l[ta->tclpos].y > GrMaxY()) scroll_down(ta); while (ta->l[ta->tclpos].y < ta->ybits) scroll_up(ta); redraw(ta); show_tcursor(ta); } return 1; } /**************************************************************************/ static void copy_marqued_area(GUITextArea *ta, int clear, int draw) { unsigned short *buf; int len; if ((ta->fmline == ta->lmline) && (ta->fmch == ta->lmch)) return; buf = nd_get_ucs2text(ta, ta->fmline, ta->fmch, ta->lmline, ta->lmch, &len); if (buf == NULL) return; _GUISetClipBoard(buf, len); free(buf); if (clear) delete_marqued_area(ta, draw); } /**************************************************************************/ static void paste_marqued_area(GUITextArea *ta, int draw) { unsigned short *buf; int len; if (ta->full) return; len = _GUIGetLenClipBoard(); if (len <=0) return; buf = malloc((len+1)*sizeof(unsigned short)); if (buf == NULL) return; delete_marqued_area(ta, 0); len = _GUIGetClipBoard(buf, len+1); nd_put_ucs2text(ta, buf, len, ta->tclpos, ta->tccpos); free(buf); if (draw) { hide_tcursor(ta); while (ta->l[ta->tclpos].y > GrMaxY()) scroll_down(ta); while (ta->l[ta->tclpos].y < ta->ybits) scroll_up(ta); redraw(ta); show_tcursor(ta); } } /**************************************************************************/ /* nd_ functions do their work without drawing anything and without */ /* moving the text cursor */ /**************************************************************************/ static void nd_del_line(GUITextArea *ta, int line) { int i; if ((line < 0) || (line >= ta->nlines)) return; ta->nlines -= 1; free(ta->l[line].cl); for (i=line; inlines; i++) { ta->l[i] = ta->l[i+1]; ta->l[i].y -= ta->ybits; } ta->l[ta->nlines].cl = NULL; ta->l[ta->nlines].len = 0; ta->l[ta->nlines].maxlen = 0; ta->l[ta->nlines].nextx = 0; } /**************************************************************************/ static void nd_del_char_from(GUITextArea *ta, int line, int c) { if ((line < 0) || (line >= ta->nlines)) return; if ((c < 0) || (c >= ta->l[line].len-1)) return; ta->l[line].len = c; ta->l[line].nextx = ta->l[line].cl[c].x; } /**************************************************************************/ static void nd_del_char_to(GUITextArea *ta, int line, int c) { int i, x, newlen; if ((line < 0) || (line >= ta->nlines)) return; if ((c < 1) || (c > ta->l[line].len)) return; newlen = ta->l[line].len - c; x = 0; for (i=0; il[line].cl[i] = ta->l[line].cl[i+c]; ta->l[line].cl[i].x = x; x += ta->l[line].cl[i].w; } ta->l[line].len = newlen; ta->l[line].nextx = x; } /**************************************************************************/ static void nd_del_char_from_to(GUITextArea *ta, int line, int fc, int lc) { int i, x, newlen, delchars; if ((line < 0) || (line >= ta->nlines)) return; delchars = lc - fc; if ((fc < 0) || (delchars < 1)) return; if (lc > ta->l[line].len) return; newlen = ta->l[line].len - delchars; x = (fc == 0) ? 0 : ta->l[line].cl[fc].x; for (i=fc; il[line].cl[i] = ta->l[line].cl[i+delchars]; ta->l[line].cl[i].x = x; x += ta->l[line].cl[i].w; } ta->l[line].len = newlen; ta->l[line].nextx = x; } /**************************************************************************/ static int nd_join_lines(GUITextArea *ta, int line) { // join line-1 with line int i, x, olen1, ncc, csize, asize; void *ptr; TALine *tali1, *tali2; if ((line < 1) || (line >= ta->nlines)) return 0; tali1 = &(ta->l[line-1]); tali2 = &(ta->l[line]); if (tali1->cl == NULL) { tali1->cl = malloc(sizeof(TAChar)*INITIAL_LINE_SIZE); if (tali1->cl == NULL) { ta->full = 1; return 0; } tali1->maxlen = INITIAL_LINE_SIZE; } olen1 = tali1->len; csize = tali1->maxlen - tali1->len; asize = 0; ncc = tali2->len; while (csize+asize < ncc) asize += INCR_LINE_SIZE; if (asize > 0) { ptr = realloc(tali1->cl, sizeof(TAChar) * (tali1->maxlen + asize)); if (ptr == NULL) { ta->full = 1; return 0; } tali1->cl = ptr; tali1->maxlen += asize; } x = tali1->nextx; for (i=0; icl[olen1+i] = tali2->cl[i]; tali1->cl[olen1+i].x = x; x += tali1->cl[olen1+i].w; } tali1->len += ncc; tali1->nextx = x; nd_del_line(ta, line); return 1; } /**************************************************************************/ static unsigned short * nd_get_ucs2text(GUITextArea *ta, int fline, int fc, int lline, int lc, int *len) { unsigned short *buf; int nchars, i, j, ind; if ((fline < 0) || (fline > lline) || (lline >= ta->nlines)) return NULL; if ((fline == lline) && (fc >= lc)) return NULL; if ((fc < 0) || (fc > ta->l[fline].len)) return NULL; if ((lc < 0) || (lc > ta->l[lline].len)) return NULL; nchars = 0; if (fline == lline) { nchars = lc - fc; } else { nchars = ta->l[fline].len - fc; nchars += 1; // for \n for (j=fline+1; jl[j].len; nchars += 1; // for \n } nchars += lc; } nchars += 1; // for \0 buf = (unsigned short *)malloc(nchars*sizeof(unsigned short)); if (buf == NULL) return NULL; ind = 0; if (fline == lline) { for (i=fc; il[fline].cl[i].u; } } else { for (i=fc; il[fline].len; i++) { buf[ind++] = ta->l[fline].cl[i].u; } buf[ind++] = '\n'; for (j=fline+1; jl[j].len; i++) { buf[ind++] = ta->l[j].cl[i].u; } buf[ind++] = '\n'; } for (i=0; il[lline].cl[i].u; } } *len = ind; buf[ind] = 0; return buf; } /**************************************************************************/ static void nd_put_ucs2text(GUITextArea *ta, unsigned short *buf, int len, int line, int c) { int i; if ((line < 0) || (line >= ta->nlines)) return; if ((c < 0) || (c > ta->l[line].len)) return; for (i=0; i= ta->nlines)) return 0; if ((c < 0) || (c > ta->l[line].len)) return 0; tali = &(ta->l[line]); if (tali->cl == NULL) { tali->cl = malloc(sizeof(TAChar)*INITIAL_LINE_SIZE); if (tali->cl == NULL) { ta->full = 1; return 0; } tali->maxlen = INITIAL_LINE_SIZE; } if (tali->len >= tali->maxlen) { ptr = realloc(tali->cl, sizeof(TAChar) * (tali->maxlen + INCR_LINE_SIZE)); if (ptr == NULL) { ta->full = 1; return 0; } tali->cl = ptr; tali->maxlen += INCR_LINE_SIZE; } chr = GrFontCharRecode(ta->grt.txo_font, ch, chrtype); w = GrFontCharWidth(ta->grt.txo_font, chr); // we do this to show glyps if needed using GR_BYTE_TEXT or GR_WORD_TEXT // and also retain the real UCS2 char if (chrtype == GR_BYTE_TEXT || chrtype == GR_WORD_TEXT) chu = GrFontCharReverseRecode(ta->grt.txo_font, chr, GR_UCS2_TEXT); else if (chrtype == GR_UCS2_TEXT) chu = ch; else chu = GrCharRecodeToUCS2(ch, chrtype); tali->len += 1; for (i=tali->len-1; i>c; i--) { tali->cl[i] = tali->cl[i-1]; tali->cl[i].x += w; } tach = &(tali->cl[c]); tach->c = chr; tach->u = chu; tach->fg = ta->grt.txo_fgcolor; tach->bg = ta->grt.txo_bgcolor; if (c >= tali->len-1) tach->x = tali->nextx; tach->w = w; tali->nextx += tach->w; return 1; } /**************************************************************************/ static int nd_new_line(GUITextArea *ta, int line, int c) { TAChar *tach = NULL; int i, j, x, ncc, size = 0; if ((line < 0) || (line >= ta->nlines)) return 0; if ((c < 0) || (c > ta->l[line].len)) return 0; if (ta->nlines >= ta->maxlines-1) { ta->full = 1; return 0; } ncc = ta->l[line].len - c; if (ncc > 0) { size = INITIAL_LINE_SIZE; while (size < ncc) size += INCR_LINE_SIZE; tach = malloc(sizeof(TAChar)*size); if (tach == NULL) { ta->full = 1; return 0; } } ta->nlines += 1; for (i=ta->nlines-1; i>line; i--) { ta->l[i] = ta->l[i-1]; ta->l[i].y += ta->ybits; } line += 1; ta->l[line].cl = NULL; ta->l[line].len = 0; ta->l[line].maxlen = 0; ta->l[line].y = ta->l[line-1].y + ta->ybits; ta->l[line].nextx = 0; if (ncc > 0) { ta->l[line].cl = tach; ta->l[line].maxlen = size; x = 0; j = c; for (i=0; il[line].cl[i] = ta->l[line-1].cl[j]; ta->l[line].cl[i].x = x; x += ta->l[line].cl[i].w; j++; } ta->l[line].nextx = x; ta->l[line-1].nextx -= x; ta->l[line].len = ncc; ta->l[line-1].len -= ncc; } return 1; }