/* Draw the selection background for the given block */ static void DrawSelectionBackground( HtmlWidget *htmlPtr, /* The HTML widget */ HtmlBlock *pBlock, /* The block whose background is drawn */ Drawable drawable, /* Draw the background on this drawable */ int x, int y /* Virtual coords of top-left of drawable */ ){ int xLeft, xRight; /* Left and right bounds of box to draw */ int yTop, yBottom; /* Top and bottom of box */ HtmlElement *p = 0; /* First element of the block */ Tk_Font font; /* Font */ GC gc; /* GC for drawing */ XRectangle xrec; /* Size of a filled rectangle to be drawn */ if( pBlock==0 || (pBlock->base.flags & HTML_Selected)==0 ){ TestPoint(0); return; } xLeft = pBlock->left - x; if( pBlock==htmlPtr->pSelStartBlock && htmlPtr->selStartIndex>0 ){ if( htmlPtr->selStartIndex >= pBlock->n ){ TestPoint(0); return; } p = pBlock->base.pNext; font = HtmlGetFont(htmlPtr, p->base.style.font); if( font==0 ) return; if( p->base.type==Html_Text ){ xLeft = p->text.x - x + Tk_TextWidth(font, pBlock->z, htmlPtr->selStartIndex); } } xRight = pBlock->right - x; if( pBlock==htmlPtr->pSelEndBlock && htmlPtr->selEndIndex<pBlock->n ){ if( p==0 ){ p = pBlock->base.pNext; font = HtmlGetFont(htmlPtr, p->base.style.font); if( font==0 ) return; } if( p->base.type==Html_Text ){ xRight = p->text.x - x + Tk_TextWidth(font, pBlock->z, htmlPtr->selEndIndex); } } yTop = pBlock->top - y; yBottom = pBlock->bottom - y; gc = HtmlGetGC(htmlPtr, COLOR_Selection, FONT_Any); xrec.x = xLeft; xrec.y = yTop; xrec.width = xRight - xLeft; xrec.height = yBottom - yTop; XFillRectangles(htmlPtr->display, drawable, gc, &xrec, 1); }
/* ** Increase the headroom to create a paragraph break at the current token */ static void Paragraph( HtmlLayoutContext *pLC, HtmlElement *p ){ int headroom; if( p==0 ){ TestPoint(0); return; } if( p->base.type==Html_Text ){ headroom = p->text.ascent + p->text.descent; TestPoint(0); }else if( p->pNext && p->pNext->base.type==Html_Text ){ headroom = p->pNext->text.ascent + p->pNext->text.descent; TestPoint(0); }else{ Tk_FontMetrics fontMetrics; Tk_Font font; font = HtmlGetFont(pLC->htmlPtr, p->base.style.font); if( font==0 ) return; Tk_GetFontMetrics(font, &fontMetrics); headroom = fontMetrics.descent + fontMetrics.ascent; TestPoint(0); } if( pLC->headRoom < headroom && pLC->bottom > pLC->top ){ pLC->headRoom = headroom; } }
/* ** Compute the size of all elements in the widget. Assume that a ** style has already been assigned to all elements. ** ** Some of the elements might have already been sized. Refer to the ** htmlPtr->lastSized and only compute sizes for elements that follow ** this one. If htmlPtr->lastSized==0, then size everything. ** ** This routine only computes the sizes of individual elements. The ** size of aggregate elements (like tables) are computed separately. ** ** The HTML_Visible flag is also set on every element that results ** in ink on the page. ** ** This routine may invoke a callback procedure which could delete ** the HTML widget. */ void HtmlSizer(HtmlWidget *htmlPtr){ HtmlElement *p; int iFont = -1; Tk_Font font; int spaceWidth = 0; Tk_FontMetrics fontMetrics; char *z; int stop = 0; if( htmlPtr->pFirst==0 ){ TestPoint(0); return; } if( htmlPtr->lastSized==0 ){ p = htmlPtr->pFirst; TestPoint(0); }else{ p = htmlPtr->lastSized->pNext; TestPoint(0); } for(; !stop && p; p=p->pNext){ if( p->base.style.flags & STY_Invisible ){ p->base.flags &= ~HTML_Visible; TestPoint(0); continue; } if( iFont != p->base.style.font ){ iFont = p->base.style.font; HtmlLock(htmlPtr); font = HtmlGetFont(htmlPtr, iFont); if( HtmlUnlock(htmlPtr) ) break; Tk_GetFontMetrics(font, &fontMetrics); spaceWidth = 0; } switch( p->base.type ){ case Html_Text: p->text.w = Tk_TextWidth(font, p->text.zText, p->base.count); p->base.flags |= HTML_Visible; p->text.descent = fontMetrics.descent; p->text.ascent = fontMetrics.ascent; if( spaceWidth==0 ){ spaceWidth = Tk_TextWidth(font, " ", 1); TestPoint(0); }else{ TestPoint(0); } p->text.spaceWidth = spaceWidth; break; case Html_Space: if( spaceWidth==0 ){ spaceWidth = Tk_TextWidth(font, " ", 1); } p->space.w = spaceWidth; p->space.descent = fontMetrics.descent; p->space.ascent = fontMetrics.ascent; p->base.flags &= ~HTML_Visible; break; case Html_TD: case Html_TH: z = HtmlMarkupArg(p, "rowspan","1"); p->cell.rowspan = atoi(z); z = HtmlMarkupArg(p, "colspan","1"); p->cell.colspan = atoi(z); p->base.flags |= HTML_Visible; TestPoint(0); break; case Html_LI: p->li.descent = fontMetrics.descent; p->li.ascent = fontMetrics.ascent; p->base.flags |= HTML_Visible; TestPoint(0); break; case Html_IMG: p->base.flags |= HTML_Visible; p->image.redrawNeeded = 0; p->image.textAscent = fontMetrics.ascent; p->image.textDescent = fontMetrics.descent; p->image.align = HtmlGetImageAlignment(p); if( p->image.pImage==0 ){ p->image.ascent = fontMetrics.ascent; p->image.descent = fontMetrics.descent; p->image.zAlt = HtmlMarkupArg(p, "alt", "<image>"); p->image.w = Tk_TextWidth(font, p->image.zAlt, strlen(p->image.zAlt)); }else{ int w, h; p->image.pNext = p->image.pImage->pList; p->image.pImage->pList = p; Tk_SizeOfImage(p->image.pImage->image, &w, &h); p->image.h = h; p->image.w = w; p->image.ascent = h/2; p->image.descent = h - p->image.ascent; } if( (z = HtmlMarkupArg(p, "width", 0))!=0 ){ int w = atoi(z); if( w>0 ) p->image.w = w; } if( (z = HtmlMarkupArg(p, "height", 0))!=0 ){ int h = atoi(z); if( h>0 ) p->image.h = h; } break; case Html_HR: case Html_TABLE: p->base.flags |= HTML_Visible; TestPoint(0); break; case Html_APPLET: case Html_EMBED: case Html_INPUT: p->input.textAscent = fontMetrics.ascent; p->input.textDescent = fontMetrics.descent; stop = HtmlControlSize(htmlPtr, p); break; case Html_SELECT: case Html_TEXTAREA: p->input.textAscent = fontMetrics.ascent; p->input.textDescent = fontMetrics.descent; break; case Html_EndSELECT: case Html_EndTEXTAREA: if( p->ref.pOther ){ p->ref.pOther->input.pEnd = p; stop = HtmlControlSize(htmlPtr, p->ref.pOther); } break; default: p->base.flags &= ~HTML_Visible; break; } } if( p ){ htmlPtr->lastSized = p; }else{ htmlPtr->lastSized = htmlPtr->pLast; } }
/* ** Display a single HtmlBlock. This is where all the drawing ** happens. */ void HtmlBlockDraw( HtmlWidget *htmlPtr, /* The main HTML widget */ HtmlBlock *pBlock, /* Block which needs to be drawn */ Drawable drawable, /* Draw the line on this */ int drawableLeft, /* Virtual coordinate of left edge of drawable */ int drawableTop, /* Virtual coordinate of top edge of drawable */ int drawableWidth, /* Width of the drawable */ int drawableHeight /* Height of the drawable */ ){ Tk_Font font; /* Font to use to render text */ GC gc; /* A graphics context */ HtmlElement *src; /* HtmlElement holding style information */ HtmlElement *pTable; /* The table (when drawing part of a table) */ int x, y; /* Where to draw */ if( pBlock==0 ){ TestPoint(0); return; } src = pBlock->base.pNext; while( src && (src->base.flags & HTML_Visible)==0 ){ src = src->base.pNext; TestPoint(0); } if( src==0 ){ TestPoint(0); return; } if( pBlock->n>0 ){ /* We must be dealing with plain old text */ if( src->base.type==Html_Text ){ x = src->text.x; y = src->text.y; TestPoint(0); }else{ CANT_HAPPEN; return; } if( pBlock->base.flags & HTML_Selected ){ HtmlLock(htmlPtr); DrawSelectionBackground(htmlPtr, pBlock, drawable, drawableLeft, drawableTop); if( HtmlUnlock(htmlPtr) ) return; } gc = HtmlGetGC(htmlPtr, src->base.style.color, src->base.style.font); font = HtmlGetFont(htmlPtr, src->base.style.font); if( font==0 ) return; Tk_DrawChars(htmlPtr->display, drawable, gc, font, pBlock->z, pBlock->n, x - drawableLeft, y - drawableTop); if( src->base.style.flags & STY_Underline ){ Tk_UnderlineChars(htmlPtr->display, drawable, gc, font, pBlock->z, x - drawableLeft, y-drawableTop, 0, pBlock->n); } if( src->base.style.flags & STY_StrikeThru ){ XRectangle xrec; xrec.x = pBlock->left - drawableLeft; xrec.y = (pBlock->top + pBlock->bottom)/2 - drawableTop; xrec.width = pBlock->right - pBlock->left; xrec.height = 1 + (pBlock->bottom - pBlock->top > 15); XFillRectangles(htmlPtr->display, drawable, gc, &xrec, 1); } if( pBlock==htmlPtr->pInsBlock && htmlPtr->insStatus>0 ){ int x; XRectangle xrec; if( htmlPtr->insIndex < pBlock->n ){ x = src->text.x - drawableLeft; x += Tk_TextWidth(font, pBlock->z, htmlPtr->insIndex); }else{ x = pBlock->right - drawableLeft; } if( x>0 ){ TestPoint(0); x--; } xrec.x = x; xrec.y = pBlock->top - drawableTop; xrec.width = 2; xrec.height = pBlock->bottom - pBlock->top; XFillRectangles(htmlPtr->display, drawable, gc, &xrec, 1); } }else{ /* We are dealing with a single HtmlElement which contains something ** other than plain text. */ int top, btm, cntr; int cnt, w; char zBuf[30]; switch( src->base.type ){ case Html_LI: x = src->li.x; y = src->li.y; cntr = (top+btm)/2; switch( src->li.type ){ case LI_TYPE_Enum_1: sprintf(zBuf,"%d.",src->li.cnt); TestPoint(0); break; case LI_TYPE_Enum_A: GetLetterIndex(zBuf,src->li.cnt,1); TestPoint(0); break; case LI_TYPE_Enum_a: GetLetterIndex(zBuf,src->li.cnt,0); TestPoint(0); break; case LI_TYPE_Enum_I: GetRomanIndex(zBuf,src->li.cnt,1); TestPoint(0); break; case LI_TYPE_Enum_i: GetRomanIndex(zBuf,src->li.cnt,0); TestPoint(0); break; default: zBuf[0] = 0; TestPoint(0); break; } gc = HtmlGetGC(htmlPtr, src->base.style.color, src->base.style.font); switch( src->li.type ){ case LI_TYPE_Undefined: case LI_TYPE_Bullet1: XFillArc(htmlPtr->display, drawable, gc, x - 7 - drawableLeft, y - 8 - drawableTop, 7, 7, 0, 360*64); TestPoint(0); break; case LI_TYPE_Bullet2: XDrawArc(htmlPtr->display, drawable, gc, x - 7 - drawableLeft, y - 8 - drawableTop, 7, 7, 0, 360*64); TestPoint(0); break; case LI_TYPE_Bullet3: XDrawRectangle(htmlPtr->display, drawable, gc, x - 7 - drawableLeft, y - 8 - drawableTop, 7, 7); TestPoint(0); break; case LI_TYPE_Enum_1: case LI_TYPE_Enum_A: case LI_TYPE_Enum_a: case LI_TYPE_Enum_I: case LI_TYPE_Enum_i: cnt = strlen(zBuf); font = HtmlGetFont(htmlPtr, src->base.style.font); if( font==0 ) return; w = Tk_TextWidth(font, zBuf, cnt); Tk_DrawChars(htmlPtr->display, drawable, gc, font, zBuf, cnt, x - w - drawableLeft, y - drawableTop); TestPoint(0); break; } break; case Html_HR: { int relief = htmlPtr->ruleRelief; switch( relief ){ case TK_RELIEF_RAISED: case TK_RELIEF_SUNKEN: break; default: relief = TK_RELIEF_FLAT; break; } HtmlDrawRect(htmlPtr, drawable, src, src->hr.x - drawableLeft, src->hr.y - drawableTop, src->hr.w, src->hr.h, 1, relief); break; } case Html_TABLE: { int relief = htmlPtr->tableRelief; switch( relief ){ case TK_RELIEF_RAISED: case TK_RELIEF_SUNKEN: break; default: relief = TK_RELIEF_FLAT; break; } HtmlDrawRect(htmlPtr, drawable, src, src->table.x - drawableLeft, src->table.y - drawableTop, src->table.w, src->table.h, src->table.borderWidth, relief); break; } case Html_TH: case Html_TD: { int depth, relief; pTable = src->cell.pTable; depth = pTable && pTable->table.borderWidth>0; switch( htmlPtr->tableRelief ){ case TK_RELIEF_RAISED: relief = TK_RELIEF_SUNKEN; break; case TK_RELIEF_SUNKEN: relief = TK_RELIEF_RAISED; break; default: relief = TK_RELIEF_FLAT; break; } HtmlDrawRect(htmlPtr, drawable, src, src->cell.x - drawableLeft, src->cell.y - drawableTop, src->cell.w, src->cell.h, depth, relief); break; } case Html_IMG: if( src->image.pImage ){ HtmlDrawImage(src, drawable, drawableLeft, drawableTop, drawableLeft + drawableWidth, drawableTop + drawableHeight); }else if( src->image.zAlt ){ gc = HtmlGetGC(htmlPtr, src->base.style.color, src->base.style.font); font = HtmlGetFont(htmlPtr, src->base.style.font); if( font==0 ) return; Tk_DrawChars(htmlPtr->display, drawable, gc, font, src->image.zAlt, strlen(src->image.zAlt), src->image.x - drawableLeft, src->image.y - drawableTop); TestPoint(0); } break; default: TestPoint(0); break; } } }
/* ** Given a Block and an x coordinate, find the Index of the character ** that is closest to the given x coordinate. ** ** The x-coordinate might specify a point to the left of the block, ** in which case the procedure returns the first token and a character ** index of 0. Or the x-coordinate might specify a point to the right ** of the block, in which case the last token is returned with an index ** equal to its last character. */ static void FindIndexInBlock( HtmlWidget *htmlPtr, /* The widget */ HtmlBlock *pBlock, /* The block */ int x, /* The x coordinate */ HtmlElement **ppToken, /* Write the closest token here */ int *pIndex /* Write the charater index in ppToken here */ ){ HtmlElement *p; Tk_Font font; int len; int n; p = pBlock->base.pNext; HtmlLock(htmlPtr); font = HtmlGetFont(htmlPtr, p->base.style.font); if( HtmlUnlock(htmlPtr) ){ *ppToken = p; *pIndex = 0; return; } if( x <= pBlock->left ){ *ppToken = p; *pIndex = 0; TestPoint(0); return; }else if( x>= pBlock->right ){ *ppToken = p; *pIndex = 0; while( p && p->base.type!=Html_Block ){ *ppToken = p; p = p->base.pNext; TestPoint(0); } p = *ppToken; if( p && p->base.type==Html_Text ){ *pIndex = p->base.count - 1; TestPoint(0); }else{ TestPoint(0); } return; } if( pBlock->n==0 ){ *ppToken = p; *pIndex = 0; TestPoint(0); }else{ TestPoint(0); } n = Tk_MeasureChars(font, pBlock->z, pBlock->n, x - pBlock->left, 0, &len); *pIndex = 0; *ppToken = 0; while( p && n>=0 ){ switch( p->base.type ){ case Html_Text: if( n<p->base.count ){ *pIndex = n; TestPoint(0); }else{ *pIndex = p->base.count - 1; TestPoint(0); } *ppToken = p; n -= p->base.count; break; case Html_Space: if( p->base.style.flags & STY_Preformatted ){ if( n<p->base.count ){ *pIndex = n; TestPoint(0); }else{ *pIndex = p->base.count - 1; TestPoint(0); } *ppToken = p; n -= p->base.count; }else{ *pIndex = 0; *ppToken = p; n--; TestPoint(0); } break; default: TestPoint(0); break; } if( p ){ p = p->base.pNext; TestPoint(0); }else{ TestPoint(0); } } }