/* * Help system * Copyright * (C) 1992 Joseph H. Allen * (C) 2001 Marek 'Marx' Grac * * This file is part of JOE (Joe's Own Editor) */ #include "types.h" struct help { char *text; /* help text with attributes */ int lines; /* number of lines */ struct help *prev; /* previous help screen */ struct help *next; /* nex help screen */ char *name; /* context name for context sensitive help */ }; #define NOT_ENOUGH_MEMORY -11 int bg_help; /* Background color for help */ struct help *help_actual = NULL; /* actual help screen */ struct help *help_ptr = NULL; /* build pointer */ /* * Process help file * Returns new line number */ int help_init(JFILE *fd,char *bf,int line) { char buf[1024]; /* input buffer */ struct help *tmp; ptrdiff_t bfl; /* buffer length */ ptrdiff_t hlpsiz, hlpbsz; /* number of used/allocated bytes for tmp->text */ char *tempbuf; if (bf[0] == '{') { /* start of help screen */ tmp = (struct help *) joe_malloc(SIZEOF(struct help)); tmp->text = NULL; tmp->lines = 0; hlpsiz = 0; hlpbsz = 0; tmp->name = vsncpy(NULL, 0, sz(bf + 1) - 1); /* -1 kill the \n */ while ((jfgets(buf, SIZEOF(buf), fd)) && (buf[0] != '}')) { ++line; bfl = zlen(buf); if (hlpsiz + bfl > hlpbsz) { if (tmp->text) { tempbuf = (char *)joe_realloc(tmp->text, hlpbsz + bfl + 1024); tmp->text = tempbuf; } else { tmp->text = (char *)joe_malloc(bfl + 1024); tmp->text[0] = 0; } hlpbsz += bfl + 1024; } mcpy(tmp->text + hlpsiz, buf, bfl); hlpsiz += bfl; ++tmp->lines; } tmp->prev = help_ptr; tmp->next = NULL; if (help_ptr) { help_ptr->next = tmp; } else { help_actual = tmp; } help_ptr = tmp; if (buf[0] == '}') { /* set new help screen as actual one */ ++line; } else { logerror_1(joe_gettext(_("\n%d: EOF before end of help text\n")), line); } } return line; } /* * Find context help - find help entry with the same name */ static struct help *find_context_help(const char *name) { struct help *tmp = help_actual; while (tmp->prev != NULL) /* find the first help entry */ tmp = tmp->prev; while (tmp != NULL && zcmp(tmp->name, name) != 0) tmp = tmp->next; return tmp; } int help_is_utf8; /* * Display help text */ void help_display(Screen *t) { const char *str; int y, x, c, z; int atr = BG_COLOR(bg_help); if (help_actual) { str = help_actual->text; } else { str = NULL; } for (y = skiptop; y != t->wind; ++y) { if (t->t->updtab[y]) { const char *start = str, *eol; ptrdiff_t width=0; ptrdiff_t nspans=0; ptrdiff_t spanwidth; ptrdiff_t spancount=0; ptrdiff_t spanextra; ptrdiff_t len; eol = zchr(str, '\n'); /* First pass: count no. springs \| and determine minimum width */ while(*str && *str!='\n') { if (*str == '\\') { ++str; switch(*str) { case 'i': case 'I': case 'u': case 'U': case 'd': case 'D': case 'b': case 'B': case 'f': case 'F': ++str; break; case '|': ++str; ++nspans; break; case 0: break; default: ++str; ++width; } } else { len = eol - str; if (help_is_utf8) c = utf8_decode_fwrd(&str, &len); else { c = *(const unsigned char *)str++; --len; } width += joe_wcwidth((help_is_utf8 ? 1 : !!locale_map->type), c); } } str = start; /* Now calculate span width */ if (width >= t->w || nspans==0) { spanwidth = 0; spanextra = nspans; } else { spanwidth = (t->w - width)/nspans; spanextra = nspans - (t->w - width - nspans*spanwidth); } /* Second pass: display text */ for (x = 0; x != t->w; ++x) { if (*str == '\n' || !*str) { if (eraeol(t->t, x, y, BG_COLOR(bg_help))) { return; } else { break; } } else { if (*str == '\\') { switch (*++str) { case '|': ++str; for (z=0;z!=spanwidth;++z) outatr((help_is_utf8 ? utf8_map : locale_map),t->t,t->t->scrn+x+y*t->w+z,t->t->attr+x+y*t->w+z,x+z,y,' ',atr); if (spancount++ >= spanextra) { outatr((help_is_utf8 ? utf8_map : locale_map),t->t,t->t->scrn+x+y*t->w+z,t->t->attr+x+y*t->w+z,x+z,y,' ',atr); ++z; } x += z-1; continue; case 'i': case 'I': atr ^= INVERSE; /* Take status line colors for inverse */ atr = (atr & ~(BG_MASK | FG_MASK)) | ((atr & INVERSE ? bg_stalin : bg_menu) & (BG_MASK | FG_MASK)); ++str; --x; continue; case 'u': case 'U': atr ^= UNDERLINE; ++str; --x; continue; case 'd': case 'D': atr ^= DIM; ++str; --x; continue; case 'b': case 'B': atr ^= BOLD; ++str; --x; continue; case 'l': case 'L': atr ^= ITALIC; ++str; --x; continue; case 'f': case 'F': atr ^= BLINK; ++str; --x; continue; case 0: --x; continue; } } len = eol - str; if (help_is_utf8) c = utf8_decode_fwrd(&str, &len); else { c = *(const unsigned char *)str++; --len; } outatr((help_is_utf8 ? utf8_map : locale_map), t->t, t->t->scrn + x + y * t->w, t->t->attr + x + y * t->w, x, y, c, atr); x += (joe_wcwidth((help_is_utf8 ? 1 : !!locale_map->type), c) - 1); } } atr = BG_COLOR(bg_help); t->t->updtab[y] = 0; outatr_complete(t->t); } while (*str && *str != '\n') ++str; if (*str == '\n') ++str; } } /* * Show help screen */ int help_on(Screen *t) { if (help_actual) { t->wind = help_actual->lines + skiptop; if ((t->h - t->wind) < FITMIN) { t->wind = t->h - FITMIN; } if (t->wind <= skiptop) { t->wind = skiptop; return -1; } wfit(t); msetI(t->t->updtab + skiptop, 1, t->wind - skiptop); return 0; } else { return -1; } } /* * Hide help screen */ static void help_off(Screen *t) { t->wind = skiptop; wfit(t); } /* * Show/hide current help screen */ int u_help(W *w, int k) { struct help *new_help; if (w->huh && (new_help = find_context_help(w->huh)) != NULL) { if (help_actual != new_help) { if (w->t->wind != skiptop) help_off(w->t); help_actual = new_help; /* prepare context help */ } } if (w->t->wind == skiptop) { return help_on(w->t); /* help screen is hidden, so show the actual one */ } else { help_off(w->t); /* hide actual help screen */ return 0; } } /* * Show next help screen (if it is possible) */ int u_help_next(W *w, int k) { if (help_actual && help_actual->next) { /* is there any next help screen? */ if (w->t->wind != skiptop) { help_off(w->t); /* if help screen was visible, then hide it */ } help_actual = help_actual->next; /* change to next help screen */ return help_on(w->t); /* show actual help screen */ } else { return -1; } } /* * Show previous help screen (if it is possible) */ int u_help_prev(W *w, int k) { if (help_actual && help_actual->prev) { /* is there any previous help screen? */ if (w->t->wind != skiptop) help_off(w->t); /* if help screen was visible, then hide it */ help_actual = help_actual->prev; /* change to previous help screen */ return help_on(w->t); /* show actual help screen */ } else { return -1; } }