richedit: Borders are now drawn for tables and nested tables.

This commit is contained in:
Dylan Smith 2008-08-12 23:15:36 -04:00 committed by Alexandre Julliard
parent 967c148a68
commit 421c5b0e02
7 changed files with 338 additions and 13 deletions

View File

@ -451,6 +451,10 @@ static void ME_RTFParAttrHook(RTF_Info *info)
switch(info->rtfMinor)
{
case rtfParDef: /* restores default paragraph attributes */
if (!info->editor->bEmulateVersion10) /* v4.1 */
info->borderType = RTFBorderParaLeft;
else /* v1.0 - 3.0 */
info->borderType = RTFBorderParaTop;
fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS |
PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE |
PFM_STARTINDENT;
@ -656,6 +660,7 @@ static void ME_RTFParAttrHook(RTF_Info *info)
fmt.wNumberingStart = info->rtfParam;
break;
case rtfBorderLeft:
info->borderType = RTFBorderParaLeft;
ME_GetSelectionParaFormat(info->editor, &fmt);
if (!(fmt.dwMask & PFM_BORDER))
{
@ -667,6 +672,7 @@ static void ME_RTFParAttrHook(RTF_Info *info)
fmt.dwMask = PFM_BORDER;
break;
case rtfBorderRight:
info->borderType = RTFBorderParaRight;
ME_GetSelectionParaFormat(info->editor, &fmt);
if (!(fmt.dwMask & PFM_BORDER))
{
@ -678,6 +684,7 @@ static void ME_RTFParAttrHook(RTF_Info *info)
fmt.dwMask = PFM_BORDER;
break;
case rtfBorderTop:
info->borderType = RTFBorderParaTop;
ME_GetSelectionParaFormat(info->editor, &fmt);
if (!(fmt.dwMask & PFM_BORDER))
{
@ -689,6 +696,7 @@ static void ME_RTFParAttrHook(RTF_Info *info)
fmt.dwMask = PFM_BORDER;
break;
case rtfBorderBottom:
info->borderType = RTFBorderParaBottom;
ME_GetSelectionParaFormat(info->editor, &fmt);
if (!(fmt.dwMask & PFM_BORDER))
{
@ -735,11 +743,24 @@ static void ME_RTFParAttrHook(RTF_Info *info)
fmt.dwMask = PFM_BORDER;
break;
case rtfBorderWidth:
{
int borderSide = info->borderType & RTFBorderSideMask;
RTFTable *tableDef = info->tableDef;
ME_GetSelectionParaFormat(info->editor, &fmt);
/* we assume that borders have been created before (RTF spec) */
fmt.wBorderWidth |= ((info->rtfParam / 15) & 7) << 8;
if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell)
{
RTFBorder *border;
if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS)
break;
border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide];
border->width = info->rtfParam;
break;
}
fmt.dwMask = PFM_BORDER;
break;
}
case rtfBorderSpace:
ME_GetSelectionParaFormat(info->editor, &fmt);
/* we assume that borders have been created before (RTF spec) */
@ -760,6 +781,10 @@ static void ME_RTFTblAttrHook(RTF_Info *info)
{
case rtfRowDef:
{
if (!info->editor->bEmulateVersion10) /* v4.1 */
info->borderType = 0; /* Not sure */
else /* v1.0 - 3.0 */
info->borderType = RTFBorderRowTop;
if (!info->tableDef) {
info->tableDef = ME_MakeTableDef(info->editor);
} else {
@ -786,6 +811,30 @@ static void ME_RTFTblAttrHook(RTF_Info *info)
}
info->tableDef->numCellsDefined++;
break;
case rtfRowBordTop:
info->borderType = RTFBorderRowTop;
break;
case rtfRowBordLeft:
info->borderType = RTFBorderRowLeft;
break;
case rtfRowBordBottom:
info->borderType = RTFBorderRowBottom;
break;
case rtfRowBordRight:
info->borderType = RTFBorderRowRight;
break;
case rtfCellBordTop:
info->borderType = RTFBorderCellTop;
break;
case rtfCellBordLeft:
info->borderType = RTFBorderCellLeft;
break;
case rtfCellBordBottom:
info->borderType = RTFBorderCellBottom;
break;
case rtfCellBordRight:
info->borderType = RTFBorderCellRight;
break;
case rtfRowGapH:
if (info->tableDef)
info->tableDef->gapH = info->rtfParam;
@ -882,6 +931,10 @@ static void ME_RTFSpecialCharHook(RTF_Info *info)
for (i = 0; i < tableDef->numCellsDefined; i++)
{
cell->member.cell.nRightBoundary = tableDef->cells[i].rightBoundary;
cell->member.cell.border.top.width = tableDef->cells[i].border[0].width;
cell->member.cell.border.left.width = tableDef->cells[i].border[1].width;
cell->member.cell.border.bottom.width = tableDef->cells[i].border[2].width;
cell->member.cell.border.right.width = tableDef->cells[i].border[3].width;
cell = cell->member.cell.next_cell;
if (!cell)
{

View File

@ -165,6 +165,19 @@ typedef struct tagME_Document {
int last_wrapped_line;
} ME_Document;
typedef struct tagME_Border
{
int width;
} ME_Border;
typedef struct tagME_BorderRect
{
ME_Border top;
ME_Border left;
ME_Border bottom;
ME_Border right;
} ME_BorderRect;
typedef struct tagME_Paragraph
{
PARAFORMAT2 *pFmt;
@ -184,8 +197,10 @@ typedef struct tagME_Cell /* v4.1 */
{
int nNestingLevel; /* 0 for normal cells, and greater for nested cells */
int nRightBoundary;
ME_BorderRect border;
POINT pt;
int nHeight, nWidth;
int yTextOffset; /* The text offset is caused by the largest top border. */
struct tagME_DisplayItem *prev_cell, *next_cell, *parent_cell;
} ME_Cell;

View File

@ -36,6 +36,7 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
item = editor->pBuffer->pFirst->next;
c.pt.y -= yoffset;
while(item != editor->pBuffer->pLast) {
int yTextOffset = 0;
int ye;
assert(item->type == diParagraph);
if (item->member.para.pCell
@ -47,6 +48,19 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
} else {
ye = c.pt.y + item->member.para.nHeight;
}
if (!(item->member.para.nFlags & MEPF_ROWEND) &&
item->member.para.pCell != item->member.para.prev_para->member.para.pCell)
{
ME_DisplayItem *cell;
if (item->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART)
cell = item->member.para.pCell;
else
cell = item->member.para.prev_para->member.para.pCell;
assert(cell);
/* the border shifts the text down */
yTextOffset = cell->member.cell.yTextOffset;
ye += yTextOffset;
}
if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT))
{
BOOL bPaint = (rcUpdate == NULL);
@ -54,30 +68,33 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, const RECT *
bPaint = c.pt.y<rcUpdate->bottom && ye>rcUpdate->top;
if (bPaint)
{
c.pt.y += yTextOffset;
ME_DrawParagraph(&c, item);
if (!rcUpdate || (rcUpdate->top<=c.pt.y && rcUpdate->bottom>=ye))
if (!rcUpdate || (rcUpdate->top<=c.pt.y-yTextOffset && rcUpdate->bottom>=ye))
item->member.para.nFlags &= ~MEPF_REPAINT;
}
}
if (item->member.para.pCell)
{
ME_Cell *cell = &item->member.para.pCell->member.cell;
ME_DisplayItem *next_para = item->member.para.next_para;
c.pt.x = cell->pt.x + cell->nWidth;
if (item->member.para.pCell == item->member.para.next_para->member.para.pCell)
if (item->member.para.pCell == next_para->member.para.pCell &&
!(next_para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)))
{
c.pt.y = ye;
} else {
if (item->member.para.next_para->member.para.nFlags & MEPF_ROWSTART)
if (next_para->member.para.nFlags & MEPF_ROWSTART)
{
cell = &ME_FindItemFwd(item->member.para.next_para, diCell)->member.cell;
cell = &ME_FindItemFwd(next_para, diCell)->member.cell;
}
else if (item->member.para.next_para->member.para.nFlags & MEPF_ROWEND)
else if (next_para->member.para.nFlags & MEPF_ROWEND)
{
cell = &cell->next_cell->member.cell;
}
else
{
cell = &item->member.para.next_para->member.para.pCell->member.cell;
cell = &next_para->member.para.pCell->member.cell;
}
c.pt.y = cell->pt.y - yoffset;
}
@ -548,14 +565,45 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
{
int idx, border_width, top_border, bottom_border;
RECT rc;
BOOL hasParaBorder;
SetRectEmpty(bounds);
if (!(para->pFmt->dwMask & (PFM_BORDER | PFM_SPACEBEFORE | PFM_SPACEAFTER))) return;
border_width = top_border = bottom_border = 0;
idx = (para->pFmt->wBorders >> 8) & 0xF;
if ((para->pFmt->dwMask & PFM_BORDER) && idx != 0 && (para->pFmt->wBorders & 0xF))
hasParaBorder = (!(c->editor->bEmulateVersion10 &&
para->pFmt->dwMask & PFM_TABLE &&
para->pFmt->wEffects & PFE_TABLE) &&
(para->pFmt->dwMask & PFM_BORDER) &&
idx != 0 &&
(para->pFmt->wBorders & 0xF));
if (hasParaBorder)
{
/* FIXME: wBorders is not stored as MSDN says in v1.0 - 4.1 of richedit
* controls. It actually stores the paragraph or row border style. Although
* the value isn't used for drawing, it is used for streaming out rich text.
*
* wBorders stores the border style for each side (top, left, bottom, right)
* using nibble (4 bits) to store each border style. The rich text format
* control words, and their associated value are the following:
* \brdrdash 0
* \brdrdashsm 1
* \brdrdb 2
* \brdrdot 3
* \brdrhair 4
* \brdrs 5
* \brdrth 6
* \brdrtriple 7
*
* The order of the sides stored actually differs from v1.0 to 3.0 and v4.1.
* The mask corresponding to each side for the version are the following:
* mask v1.0-3.0 v4.1
* 0x000F top left
* 0x00F0 left top
* 0x0F00 bottom right
* 0xF000 right bottom
*/
if (para->pFmt->wBorders & 0x00B0)
FIXME("Unsupported border flags %x\n", para->pFmt->wBorders);
border_width = ME_GetParaBorderWidth(c->editor, para->pFmt->wBorders);
@ -583,7 +631,9 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
FillRect(c->hDC, &rc, c->editor->hbrBackground);
}
if ((para->pFmt->dwMask & PFM_BORDER) && idx != 0 && (para->pFmt->wBorders & 0xF)) {
/* Native richedit doesn't support paragraph borders in v1.0 - 4.1,
* but might support it in later versions. */
if (hasParaBorder) {
int pen_width;
COLORREF pencr;
HPEN pen = NULL, oldpen = NULL;
@ -665,8 +715,139 @@ static void ME_DrawParaDecoration(ME_Context* c, ME_Paragraph* para, int y, RECT
static void ME_DrawTableBorders(ME_Context *c, ME_DisplayItem *paragraph)
{
ME_Paragraph *para = &paragraph->member.para;
if (c->editor->bEmulateVersion10) /* v1.0 - 3.0 */
if (!c->editor->bEmulateVersion10) /* v4.1 */
{
if (para->pCell)
{
RECT rc;
ME_Cell *cell = &para->pCell->member.cell;
ME_DisplayItem *paraAfterRow;
HPEN pen, oldPen;
LOGBRUSH logBrush;
HBRUSH brush;
COLORREF color;
POINT oldPt;
int width;
BOOL atTop = (para->pCell != para->prev_para->member.para.pCell);
BOOL atBottom = (para->pCell != para->next_para->member.para.pCell);
int top = (atTop ? cell->pt.y : para->pt.y) - ME_GetYScrollPos(c->editor);
int bottom = (atBottom ?
cell->pt.y + cell->nHeight - ME_GetYScrollPos(c->editor):
top + para->nHeight + (atTop ? cell->yTextOffset : 0));
rc.left = cell->pt.x;
rc.right = rc.left + cell->nWidth;
if (atTop) {
/* Erase gap before text if not all borders are the same height. */
width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
rc.top = top + width;
width = cell->yTextOffset - width;
rc.bottom = rc.top + width;
if (width) {
FillRect(c->hDC, &rc, c->editor->hbrBackground);
}
}
/* Draw cell borders.
* The borders borders are draw in is left, top, bottom, right in order
* to be consistent with native richedit. This is noticeable from the
* overlap of borders of different colours. */
if (!(para->nFlags & MEPF_ROWEND)) {
rc.top = top;
rc.bottom = bottom;
if (cell->border.left.width > 0)
{
color = RGB(0,0,0);
width = max(ME_twips2pointsX(c, cell->border.left.width), 1);
} else {
color = RGB(192,192,192);
width = 1;
}
logBrush.lbStyle = BS_SOLID;
logBrush.lbColor = color;
logBrush.lbHatch = 0;
pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
width, &logBrush, 0, NULL);
oldPen = SelectObject(c->hDC, pen);
MoveToEx(c->hDC, rc.left, rc.top, &oldPt);
LineTo(c->hDC, rc.left, rc.bottom);
SelectObject(c->hDC, oldPen);
DeleteObject(pen);
MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
}
if (atTop) {
if (cell->border.top.width > 0)
{
brush = GetStockObject(BLACK_BRUSH);
width = max(ME_twips2pointsY(c, cell->border.top.width), 1);
} else {
brush = GetStockObject(LTGRAY_BRUSH);
width = 1;
}
rc.top = top;
rc.bottom = rc.top + width;
FillRect(c->hDC, &rc, brush);
}
/* Draw the bottom border if at the last paragraph in the cell, and when
* in the last row of the table. */
if (atBottom) {
int oldLeft = rc.left;
width = max(ME_twips2pointsY(c, cell->border.bottom.width), 1);
paraAfterRow = ME_GetTableRowEnd(paragraph)->member.para.next_para;
if (paraAfterRow->member.para.nFlags & MEPF_ROWSTART) {
ME_DisplayItem *nextEndCell;
nextEndCell = ME_FindItemBack(ME_GetTableRowEnd(paraAfterRow), diCell);
assert(nextEndCell && !nextEndCell->member.cell.next_cell);
rc.left = nextEndCell->member.cell.pt.x;
/* FIXME: Native draws FROM the bottom of the table rather than
* TO the bottom of the table in this case, but just doing so here
* will case the next row to erase the border. */
/*
rc.top = bottom;
rc.bottom = rc.top + width;
*/
}
if (rc.left < rc.right) {
if (cell->border.bottom.width > 0) {
brush = GetStockObject(BLACK_BRUSH);
} else {
brush = GetStockObject(LTGRAY_BRUSH);
}
rc.bottom = bottom;
rc.top = rc.bottom - width;
FillRect(c->hDC, &rc, brush);
}
rc.left = oldLeft;
}
/* Right border only drawn if at the end of the table row. */
if (!cell->next_cell->member.cell.next_cell &&
!(para->nFlags & MEPF_ROWSTART))
{
rc.top = top;
rc.bottom = bottom;
if (cell->border.right.width > 0) {
color = RGB(0,0,0);
width = max(ME_twips2pointsX(c, cell->border.right.width), 1);
} else {
color = RGB(192,192,192);
width = 1;
}
logBrush.lbStyle = BS_SOLID;
logBrush.lbColor = color;
logBrush.lbHatch = 0;
pen = ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
width, &logBrush, 0, NULL);
oldPen = SelectObject(c->hDC, pen);
MoveToEx(c->hDC, rc.right - 1, rc.top, &oldPt);
LineTo(c->hDC, rc.right - 1, rc.bottom);
SelectObject(c->hDC, oldPen);
DeleteObject(pen);
MoveToEx(c->hDC, oldPt.x, oldPt.y, NULL);
}
}
} else { /* v1.0 - 3.0 */
/* Draw simple table border */
if (para->pFmt->dwMask & PFM_TABLE && para->pFmt->wEffects & PFE_TABLE) {
HPEN pen = NULL, oldpen = NULL;
int i, firstX, startX, endX, rowY, rowBottom, nHeight;
@ -785,7 +966,7 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) {
pt.y = 12+y;
ME_DebugWrite(c->hDC, &pt, buf);
}
height = p->member.row.nHeight;
baseline = p->member.row.nBaseline;
break;
@ -830,6 +1011,7 @@ void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) {
{
FillRect(c->hDC, &rc, c->editor->hbrBackground);
}
break;
default:
break;
}

View File

@ -247,6 +247,7 @@ void RTFInit(RTF_Info *info)
info->tableDef = NULL;
info->nestingLevel = 0;
info->canInheritInTbl = FALSE;
info->borderType = 0;
}
/*

View File

@ -950,6 +950,7 @@ typedef struct RTFFont RTFFont;
typedef struct RTFColor RTFColor;
typedef struct RTFStyle RTFStyle;
typedef struct RTFStyleElt RTFStyleElt;
typedef struct RTFBorder RTFBorder;
typedef struct RTFCell RTFCell;
typedef struct RTFTable RTFTable;
@ -1006,10 +1007,15 @@ struct RTFStyleElt
RTFStyleElt *rtfNextSE; /* next element in style */
};
struct RTFBorder
{
int width;
};
struct RTFCell
{
int rightBoundary;
RTFBorder border[4];
};
@ -1019,6 +1025,8 @@ struct RTFTable
int numCellsDefined;
int gapH, leftEdge;
/* borders for the table row */
RTFBorder border[6];
/* Used in v1.0 - v3.0 */
int numCellsInserted;
@ -1033,6 +1041,40 @@ struct RTFTable
RTFTable *parent;
};
# define RTFBorderTypeNone 0x00
# define RTFBorderTypePara 0x10 /* for \brdrX control words */
# define RTFBorderTypeRow 0x20 /* for \trbrdrX control words */
# define RTFBorderTypeCell 0x30 /* for \clbrdrX control words */
# define RTFBorderTypeMask 0xf0
/* The X in the control words \brdrX \trbrdrX and \clbrdrX mentioned above
* should be one of t, l, b, r which stand for top, left, bottom, right
* respectively. */
# define RTFBorderSideTop 0x00
# define RTFBorderSideLeft 0x01
# define RTFBorderSideBottom 0x02
# define RTFBorderSideRight 0x03
# define RTFBorderSideHorizontal 0x04
# define RTFBorderSideVertical 0x05
# define RTFBorderSideMask 0x0f
/* Here are the values from the border types and sides put together. */
# define RTFBorderParaTop 0x10
# define RTFBorderParaLeft 0x11
# define RTFBorderParaBottom 0x12
# define RTFBorderParaRight 0x13
# define RTFBorderRowTop 0x20
# define RTFBorderRowLeft 0x21
# define RTFBorderRowBottom 0x22
# define RTFBorderRowRight 0x23
# define RTFBorderRowHorizontal 0x24
# define RTFBorderRowVertical 0x25
# define RTFBorderCellTop 0x30
# define RTFBorderCellLeft 0x31
# define RTFBorderCellBottom 0x32
# define RTFBorderCellRight 0x33
/*
* Return pointer to new element of type t, or NULL
* if no memory available.
@ -1141,6 +1183,7 @@ struct _RTF_Info {
RTFTable *tableDef;
int nestingLevel;
BOOL canInheritInTbl;
int borderType; /* value corresponds to the RTFBorder constants. */
};

View File

@ -608,6 +608,7 @@ struct RTFTable *ME_MakeTableDef(ME_TextEditor *editor)
void ME_InitTableDef(ME_TextEditor *editor, struct RTFTable *tableDef)
{
ZeroMemory(tableDef->cells, sizeof(tableDef->cells));
ZeroMemory(tableDef->border, sizeof(tableDef->border));
tableDef->numCellsDefined = 0;
tableDef->leftEdge = 0;
if (!editor->bEmulateVersion10) /* v4.1 */

View File

@ -606,8 +606,24 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
{
ME_DisplayItem *cell = ME_FindItemFwd(item, diCell);
ME_DisplayItem *endRowPara;
int borderWidth = 0;
cell->member.cell.pt = c.pt;
endRowPara = ME_GetTableRowEnd(item);
/* Offset the text by the largest top border width. */
while (cell->member.cell.next_cell) {
borderWidth = max(borderWidth, cell->member.cell.border.top.width);
cell = cell->member.cell.next_cell;
}
endRowPara = ME_FindItemFwd(cell, diParagraph);
assert(endRowPara->member.para.nFlags & MEPF_ROWEND);
if (borderWidth > 0)
{
borderWidth = max(ME_twips2pointsY(&c, borderWidth), 1);
while (cell) {
cell->member.cell.yTextOffset = borderWidth;
cell = cell->member.cell.prev_cell;
}
c.pt.y += borderWidth;
}
if (endRowPara->member.para.pFmt->dxStartIndent > 0)
{
int dxStartIndent = endRowPara->member.para.pFmt->dxStartIndent;
@ -620,13 +636,26 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
{
/* Set all the cells to the height of the largest cell */
ME_DisplayItem *startRowPara;
int prevHeight, nHeight;
int prevHeight, nHeight, bottomBorder = 0;
ME_DisplayItem *cell = ME_FindItemBack(item, diCell);
if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWSTART))
{
/* Last row, the bottom border is added to the height. */
cell = cell->member.cell.prev_cell;
while (cell)
{
bottomBorder = max(bottomBorder, cell->member.cell.border.bottom.width);
cell = cell->member.cell.prev_cell;
}
bottomBorder = ME_twips2pointsY(&c, bottomBorder);
cell = ME_FindItemBack(item, diCell);
}
prevHeight = cell->member.cell.nHeight;
nHeight = cell->member.cell.prev_cell->member.cell.nHeight;
nHeight = cell->member.cell.prev_cell->member.cell.nHeight + bottomBorder;
cell->member.cell.nHeight = nHeight;
item->member.para.nHeight = nHeight;
cell = cell->member.cell.prev_cell;
cell->member.cell.nHeight = nHeight;
while (cell->member.cell.prev_cell)
{
cell = cell->member.cell.prev_cell;
@ -664,6 +693,7 @@ BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
c.pt.x = cell->pt.x + cell->nWidth;
c.pt.y = cell->pt.y;
cell->next_cell->member.cell.pt = c.pt;
c.pt.y += cell->yTextOffset;
}
else
{