/* v_line.c *********** Copyright 1988-1993 by Vermont Creative Software *********** v_line() Draws a vertical or horizontal line, optionally with intersection characters Call #include void v_line(rb, cb, len, dir, att, ln_stylep, wnp) int rb; Window row to start line int cb; Window column to start line int len; Length of line int dir; Direction of line drawing UCHAR att; Attribute to draw line with LINEPTR ln_stylep; Line style WINDOWPTR wnp; Pointer to window to write to dir: Specify UP, DOWN, LEFT, or RIGHT. These values can be bitwise or'ed with the modifiers X_LINE (to intersect with other lines and boxes) Intersections can only occur for single and double lines. ln_stylep: Specify a pointer to a line style or one of the following #defined line types. Appearance is operating system dependent. LINE_SLNP Single line LINE_DLNP Double line LINE_SPACEP Space character line LINE_DOTP Stippled block graphics character or dot line LINE_STARP Asterisk line LINE_SOLIDP Solid block graphics character or space line Returns None Description v_line() draws a line beginning at the specified window location. The line is drawn from the origin specified in the direction specified. The line is drawn with the specified attribute, size, and line style. If the specified pointer to a line style structure is NULLP, the function returns immediately without drawing a line. This function does not move the virtual cursor. For intersecting lines, v_line() checks each position before writing a character to determine if this position requires an intersection character. If required, a table is used to determine what character to place in the position. Intersection characters for all combinations of single and double lines are not available. Only those intersections available in the IBM Extended ASCII character set (for PCDOS and OS/2) or available in the vvtermcap file are supported. If no appropriate intersection character is defined, the specified line character is drawn. Cautions If you want the text, line, or box to be protected, use bg_txtdef(), bg_linedef(), or bg_boxdef(). Drawing intersecting lines is slower than drawing a simple line. Only specify intersecting lines if the line being drawn will overlap an existing box, line, or border. Terminal Systems: Under non-memory-mapped terminal systems, the line type is BDR_SLNP or BDR_DLNP, and the attribute is one of the predefined VCS logical attributes, the specified attribute is ignored. The line is displayed in normal video. */ #include #include #define DIRECTION 0x0003 /*Direction mask */ #define VERTICAL 0x0002 /*Vertical line mask */ #define BACKWARDS 0x0001 /*Direction LEFT or UP mask */ /******************************************************************************/ /* The method used for determining the line character to place in a given */ /* location is based on "Dress Up Your Help Screens", by Jeff Prosie, PC */ /* Magazine, January 26, 1988. The method used here differs significantly */ /* from the one used in the program described there, but we use the same */ /* approach to coding of the line characters. */ /* */ /* Each line character is "described" by a line-character-code contained in */ /* a single byte. Each bit of the code byte describes whether a line */ /* extension goes in one of the four possible directions: */ /* */ /* bit 1: extension up (single line) right-most bit */ /* bit 2: extension up (double line) */ /* bit 3: extension rt (single line) */ /* bit 4: extension rt (double line) */ /* bit 5: extension dn (single line) */ /* bit 6: extension dn (double line) */ /* bit 7: extension lft (single line) */ /* bit 8: extension lft (double line) */ /* */ /* For example byte 00101000 describes a single-line an upper left-hand */ /* corner character. */ /* The linecode[] array contains the code for each of the IBM-PC defined */ /* line-drawing characters, arranged sequentially starting with the lowest */ /* numbered line character (179). The codes in the array below are converted*/ /* to decimal values. */ /* */ /* FOR TERMINAL SYSTEMS, the line-drawing characters themselves are in the */ /* _ttlinedrawchar array, where they are stored after being parsed from the */ /* vvtermcap file. The _linecode array contains only the bit-mask which */ /* describes the extensions for the characters. The order of the characters */ /* in the _ttlinedrawchar array corresponds to the order of the characters in*/ /* the _linecode array. To find the correct extensions for the current */ /* character, we look the character up in the _ttlinedrawchar array. We then*/ /* do pointer arithmetic, by subtracting the location of the character in the*/ /* array from the location of the first element in the array, to find out the*/ /* number of the element which contains the current character. We look at */ /* the corresponding element in the _linecode array to check for the correct */ /* extensions for that character. */ /******************************************************************************/ /* We define some masks to set code bits for the possible extensions */ /* The masks are defined only for single lines. To create masks for double */ /* we will shift the bits left by one */ #define SLN_UPEXT 0x01 #define DLN_UPEXT 0x02 #define UPEXT (SLN_UPEXT | DLN_UPEXT) #define SLN_RTEXT 0x04 #define DLN_RTEXT 0x08 #define RTEXT (SLN_RTEXT | DLN_RTEXT) #define SLN_DNEXT 0x10 #define DLN_DNEXT 0x20 #define DNEXT (SLN_DNEXT | DLN_DNEXT) #define SLN_LFTEXT 0x40 #define DLN_LFTEXT 0x80 #define LFTEXT (SLN_LFTEXT | DLN_LFTEXT) #define SINGLE_MASK 0x55 #define DOUBLE_MASK 0xAA /*_code_make modifies the (external) value of targetcode, depending upon */ /*the character found in the current cell of wn and the (external) value of */ /*testmask. */ #ifdef LINT_ARGS static UCHAR FASTCALL _code_make(UCHAR FAR *, int, UCHAR); #endif #ifdef OLD_STYLE static UCHAR FASTCALL _code_make(srcp, move_type, testmask) UCHAR FAR *srcp; int move_type; UCHAR testmask; #else static UCHAR FASTCALL _code_make(UCHAR FAR *srcp, int move_type, UCHAR testmask) #endif { UCHAR ch[2]; UCHAR temp; int offset; UCHAR *p; _vcscpy(srcp, (UCHAR FAR *)ch, 1, move_type, (UCHAR) '\0'); if (_wnmemmap) { if (ch[0] >= LNCHARMIN && ch[0] <= LNCHARMAX) /*line character */ { temp = _linecode[ch[0] - LNCHARMIN] & testmask; return(temp); } else return(0); } else { if ((ch[1] == BLK_GRAPH) && (p = (UCHAR *) memchr(_ttlinedrawchar, ch[0], LNCHARQ))) { /* pointer arithmetic to find the element in _ttlinedrawchar */ /* that contains the character */ offset = (int) ((UCHAR *) p - (UCHAR *) _ttlinedrawchar); /* check the element in the _linecode array to see what */ /* extensions are present for this character */ return(_linecode[offset] & testmask); } else return(0); } } #ifdef OLD_STYLE void FASTCALL v_line(r, c, len, ln_dir, att, ln_stylep, wnp) int r; int c; int len; int ln_dir; UCHAR att; LINEPTR ln_stylep; WINDOWPTR wnp; #else void FASTCALL v_line(int r, int c, int len, int ln_dir, UCHAR att, LINEPTR ln_stylep, WINDOWPTR wnp) #endif { int dummy; UCHAR linechar; /*line drawing character */ UCHAR FAR *wn_addr; UCHAR *p; register int i; int tr, tc; int row_limit; int col_limit; int scr_rowlen; SCRPTR scrp; int move_type; int top_mg, lft_mg; ULONG flags; /*window flags */ int todrawq; /*number of line characters to draw */ int draw_segment = FALSE; UCHAR islinechar; /*true if line char in current cell */ UCHAR scr_char[2]; /*character found on screen */ UCHAR target_char; /*substitution character */ UCHAR targetcode; /*char code for target cell */ UCHAR mask; int offset; #ifndef NO_DEBUG_CODE int row_q, col_q; /*last row and column of the line */ static UCHAR fn[] = "v_line"; #endif INIT_MODULE(fn); if (!ln_stylep) /*draw no line */ goto END; /*figure out the normal line character*/ linechar = ln_dir & VERTICAL ? ln_stylep->lft_ch : ln_stylep->up_ch; /*function draws lines downward and to*/ /*right; so adjust origin of line if */ /*necessary */ if (ln_dir & BACKWARDS) { if (ln_dir & VERTICAL) r = r - len + 1; else c = c - len + 1; } #ifndef NO_DEBUG_CODE if (ln_dir & VERTICAL) { col_q = 1; row_q = len; } else { col_q = len; row_q = 1; } if (!_wn_rgnfit(r, c, row_q, col_q, wnp)) { VV_ERR = BADLINECOORD; goto END; } #endif /*----------------------------------------------------------------------------*/ /* If a single or double line box, we may have to use BLK_GRAPH for an */ /* attribute. VCS defined attributes (<= LSPINCH) do NOT use block */ /* graphics, and so must be replaced. Any attribute > LSPINCH is customer */ /* defined, and we must assume that the programmer will handle graphics mode.*/ /*----------------------------------------------------------------------------*/ if (!_wnmemmap && (ln_stylep == LINE_SLNP || ln_stylep == LINE_DLNP) && (att <= LSPINCH)) att = USE_BLK_GRAPH; /*no intersection characters desired */ if (!(ln_dir & X_LINE) || (ln_stylep != LINE_SLNP && ln_stylep != LINE_DLNP)) { if (ln_dir & VERTICAL) { dummy = v_chattcol(r, c, linechar, att, len, CHATT, wnp); } else v_chattrow(r, c, linechar, att, len, CHATT, wnp); } else /*intersections desired */ { flags = wnp->flags; if (flags & BUFFERED) { scrp = wnp->bufp; scr_rowlen = scrp->col_q * 2; wn_addr = scrp->vbufp; if (!_wnmemmap) move_type = VST + TO_VST; else move_type = VST + TO_ST; row_limit = scrp->row_q - 1; col_limit = scrp->col_q - 1; } else { if (ln_dir & X_LINE && (wnp->bdr_linep == BDR_SLNP || wnp->bdr_linep == BDR_DLNP)) { wnp->flags |= FULL; /* deal with FULL window */ lft_mg = wnp->lft_mg; top_mg = wnp->top_mg; if (ln_dir & VERTICAL) { tr = wnp->r; tc = wnp->c; c += (lft_mg + 1); /*adjust to reflect FULL dimensions */ r += (top_mg + 1); /*adjust to reflect FULL dimensions */ wnp->r = tr; wnp->c = tc; } else { r += (top_mg + 1); /*adjust to reflect FULL dimensions */ c += (lft_mg + 1); /*adjust to reflect FULL dimensions */ } } scrp = wnp->destp; scr_rowlen = scrp->col_q * 2; wn_addr = scrp->vbufp + wnp->rb * scr_rowlen + wnp->cb * 2; if (!_wnmemmap) move_type = (scrp->type == TO_VID ? VID + TO_VST : VST + TO_VST); else move_type = (scrp->type == TO_VID ? VID + TO_ST : VST + TO_ST); row_limit = wnp->re - wnp->rb; col_limit = wnp->ce - wnp->cb; } wn_addr += (r * scr_rowlen + c * 2); todrawq = 0; /*characters to draw = 0 */ draw_segment = FALSE; islinechar = FALSE; for (i = len; i > 0; i--) { _vcscpy(wn_addr, (UCHAR FAR *)scr_char, 1, move_type, (UCHAR) '\0'); if (_wnmemmap) { islinechar = scr_char[0] < LNCHARMIN || scr_char[0] > LNCHARMAX || scr_char[0] == linechar ? FALSE : TRUE; } else { if (p = (UCHAR *) memchr(_ttlinedrawchar, scr_char[0], LNCHARQ)) { /* pointer arithmetic to find the number of the element */ /* in _ttlinedrawchar that contains this character */ offset = (int) ((UCHAR *) p - (UCHAR *) _ttlinedrawchar); /* check the corresponding element in _linecode */ islinechar = _linecode[offset] ? TRUE : FALSE; } else islinechar = FALSE; } if (!islinechar) /*not a line character */ todrawq++; else /*current cell contains a line char */ { if (todrawq > 0) /*if any accumulated characters */ draw_segment = TRUE; /*draw them */ } /*----------------------------------------------------------------*/ /* Draw the segment of the line that doesn't have any */ /* intersecting characters. */ /*----------------------------------------------------------------*/ if (draw_segment) { if (ln_dir & VERTICAL) { dummy = v_chattcol(r - todrawq, c, linechar, att, todrawq, CHATT, wnp); } else v_chattrow(r, c - todrawq, linechar, att, todrawq, CHATT, wnp); todrawq = 0; draw_segment = FALSE; } if (islinechar) /*found line character in current cell*/ { if (i == len) { switch (ln_dir & DIRECTION) { case UP: /*------------------------------------------------*/ /* If the line is only 1 character long, we want */ /* to show the tick mark on the upper side of any*/ /* intersecting horizontal line. If the line is */ /* longer than one character, we always draw it */ /* in the DOWN direction so we fall through to */ /* the DOWN case. */ /*------------------------------------------------*/ if (len == 1) { mask = SLN_UPEXT; break; } case DOWN: mask = SLN_DNEXT; break; case LEFT: /*------------------------------------------------*/ /* If the line is only 1 character long, we want */ /* to show the tick mark on the left side of any */ /* intersecting vertical line. If the line is */ /* longer than one character, we always draw it */ /* in the RIGHT direction so we fall through to */ /* the RIGHT case. */ /*------------------------------------------------*/ if (len == 1) { mask = SLN_LFTEXT; break; } case RIGHT: mask = SLN_RTEXT; break; } } else if (i == 1) /*check for endpoint in line */ { /*--------------------------------------------------------*/ /* Since we always draw to the RIGHT or DOWN, the last */ /* character in the line only needs to connect to the */ /* LEFT or UP. */ /*--------------------------------------------------------*/ mask = (UCHAR) (ln_dir & VERTICAL ? SLN_UPEXT : SLN_LFTEXT); } else /*interior of line */ mask = (UCHAR) (ln_dir & VERTICAL ? SLN_DNEXT | SLN_UPEXT : SLN_RTEXT | SLN_LFTEXT); if (ln_stylep == LINE_DLNP) mask = (UCHAR) (mask << 1); targetcode = 0; /*------------------------------------------------------------*/ /* If the line is not being extended to the right, check the */ /* cell to the right to see if the character in it extends */ /* into the current cell. */ /*------------------------------------------------------------*/ if ( !(mask & RTEXT) && c != col_limit) targetcode |= _code_make(wn_addr + 2, move_type, LFTEXT); /*------------------------------------------------------------*/ /* If the line is not being extended to the left, check the */ /* cell to the left to see if the character in it extends */ /* into the current cell. */ /*------------------------------------------------------------*/ if ( !(mask & LFTEXT) && c != 0) targetcode |= _code_make(wn_addr - 2, move_type, RTEXT); /*------------------------------------------------------------*/ /* If the line is not being extended down, check the cell */ /* below to see if the character in it extends into the */ /* current cell. */ /*------------------------------------------------------------*/ if ( !(mask & DNEXT) && r != row_limit) targetcode |= _code_make(wn_addr + scr_rowlen, move_type, UPEXT); /*------------------------------------------------------------*/ /* If the line is not being extended up, check the cell */ /* above to see if the character in it extends into the */ /* current cell. */ /*------------------------------------------------------------*/ if ( !(mask & UPEXT) && r != 0) targetcode |= _code_make(wn_addr - scr_rowlen, move_type, DNEXT); /*------------------------------------------------------------*/ /* Targetcode now contains a description of how the four */ /* surrounding cells extend into the current cell. By */ /* swapping the upper nibble with the lower nibble, we then */ /* describe how the current cell needs to extend to match */ /* the surrounding cells. This works because of the way the */ /* line character code system is set up. Very ingenious!!! */ /*------------------------------------------------------------*/ targetcode = (UCHAR) ((targetcode << 4) | (targetcode >> 4) | mask); /*------------------------------------------------------------*/ /* Search the intersection table looking for a character that*/ /* matches the intersection requirements. */ /*------------------------------------------------------------*/ if (p = (UCHAR *) memchr(_linecode, targetcode, LNCHARQ)) { if (_wnmemmap) target_char = (UCHAR) (p - (UCHAR *)_linecode + LNCHARMIN); else { target_char = _ttlinedrawchar[p - _linecode]; if (!target_char) target_char = linechar; } } else /*try just one style */ { targetcode = (UCHAR) (ln_stylep == LINE_DLNP ? targetcode & DOUBLE_MASK : targetcode & SINGLE_MASK); if (p = (UCHAR *) memchr(_linecode, targetcode, LNCHARQ)) { if (_wnmemmap) { target_char = (UCHAR) (p - (UCHAR *)_linecode + LNCHARMIN); } else { target_char = _ttlinedrawchar[p - _linecode]; if (!target_char) target_char = linechar; } } else target_char = linechar; } v_chattrow(r, c, target_char, att, 1, CHATT, wnp); islinechar = FALSE; } /*----------------------------------------------------------------*/ /* Advance to the next position */ /*----------------------------------------------------------------*/ if (ln_dir & VERTICAL) { r++; wn_addr += scr_rowlen; } else { c++; wn_addr += 2; } } if (todrawq) { if (ln_dir & VERTICAL) { dummy = v_chattcol(r - todrawq, c, linechar, att, todrawq, CHATT, wnp); } else v_chattrow(r, c - todrawq, linechar, att, todrawq, CHATT, wnp); } if (!(flags & FULL)) wnp->flags &= ~FULL; /* Restore window flags */ } END: #ifndef NO_DEBUG_CODE EXIT_MODULE(fn); #else EXIT_NOERRH(fn); #endif return; }