diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c index 231a091e84c..4bcfbf8114c 100644 --- a/dlls/riched20/caret.c +++ b/dlls/riched20/caret.c @@ -312,7 +312,7 @@ void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars); } -static void +static ME_DisplayItem * ME_InternalInsertTextFromCursor(ME_TextEditor *editor, int nCursor, const WCHAR *str, int len, ME_Style *style, int flags) @@ -323,7 +323,7 @@ ME_InternalInsertTextFromCursor(ME_TextEditor *editor, int nCursor, assert(p->pRun->type == diRun); - ME_InsertRunAtCursor(editor, p, style, str, len, flags); + return ME_InsertRunAtCursor(editor, p, style, str, len, flags); } @@ -343,6 +343,32 @@ void ME_InsertGraphicsFromCursor(ME_TextEditor *editor, int nCursor) } +void +ME_InsertTableCellFromCursor(ME_TextEditor *editor, int nCursor) +{ + WCHAR tab = '\t'; + ME_DisplayItem *p, *run; + ME_Style *pStyle = ME_GetInsertStyle(editor, nCursor); + + p = ME_InternalInsertTextFromCursor(editor, nCursor, &tab, 1, pStyle, + MERF_CELL); + run = p; + while ((run = ME_FindItemBack(run, diRunOrParagraph))->type == diRun) + { + if (run->member.run.nFlags & MERF_CELL) + { + assert(run->member.run.pCell->next); + p->member.run.pCell = run->member.run.pCell->next; + return; + } + } + assert(run->type == diParagraph); + assert(run->member.para.bTable); + assert(run->member.para.pCells); + p->member.run.pCell = run->member.para.pCells; +} + + void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, const WCHAR *str, int len, ME_Style *style) { diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c index 5111807ac36..2a458e7256c 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -414,12 +414,24 @@ static void ME_RTFParAttrHook(RTF_Info *info) switch(info->rtfMinor) { - case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */ + case rtfParDef: /* restores default paragraph attributes */ fmt.dwMask = PFM_ALIGNMENT | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT; fmt.wAlignment = PFA_LEFT; fmt.cTabCount = 0; fmt.dxOffset = fmt.dxStartIndent = 0; + RTFFlushOutputBuffer(info); + ME_GetParagraph(info->editor->pCursors[0].pRun)->member.para.bTable = FALSE; break; + case rtfInTable: + { + ME_DisplayItem *para; + + RTFFlushOutputBuffer(info); + para = ME_GetParagraph(info->editor->pCursors[0].pRun); + assert(para->member.para.pCells); + para->member.para.bTable = TRUE; + return; + } case rtfFirstIndent: ME_GetSelectionParaFormat(info->editor, &fmt); fmt.dwMask = PFM_STARTINDENT | PFM_OFFSET; @@ -466,6 +478,38 @@ static void ME_RTFParAttrHook(RTF_Info *info) } } +static void ME_RTFTblAttrHook(RTF_Info *info) +{ + ME_DisplayItem *para; + + switch (info->rtfMinor) + { + case rtfRowDef: + RTFFlushOutputBuffer(info); + para = ME_GetParagraph(info->editor->pCursors[0].pRun); + + para->member.para.pCells = ALLOC_OBJ(ME_TableCell); + para->member.para.pCells->nRightBoundary = 0; + para->member.para.pCells->next = NULL; + para->member.para.pLastCell = para->member.para.pCells; + break; + case rtfCellPos: + RTFFlushOutputBuffer(info); + para = ME_GetParagraph(info->editor->pCursors[0].pRun); + + if (para->member.para.pLastCell->nRightBoundary) + { + ME_TableCell *pCell = ALLOC_OBJ(ME_TableCell); + + pCell->next = NULL; + para->member.para.pLastCell->next = pCell; + para->member.para.pLastCell = pCell; + } + para->member.para.pLastCell->nRightBoundary = info->rtfParam; + break; + } +} + static void ME_RTFReadHook(RTF_Info *info) { switch(info->rtfClass) { @@ -513,6 +557,15 @@ static void ME_RTFReadHook(RTF_Info *info) { case rtfParAttr: ME_RTFParAttrHook(info); break; + case rtfTblAttr: + ME_RTFTblAttrHook(info); + break; + case rtfSpecialChar: + if (info->rtfMinor == rtfCell) + { + RTFFlushOutputBuffer(info); + ME_InsertTableCellFromCursor(info->editor, 0); + } } break; } diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index f452870eef7..bd3416644db 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -56,6 +56,7 @@ ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass) BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass); ME_DisplayItem *ME_MakeDI(ME_DIType type); void ME_DestroyDisplayItem(ME_DisplayItem *item); +void ME_DestroyTableCellList(ME_DisplayItem *item); void ME_DumpDocument(ME_TextBuffer *buffer); const char *ME_GetDITypeName(ME_DIType type); @@ -170,6 +171,7 @@ BOOL ME_IsSelection(ME_TextEditor *editor); void ME_DeleteSelection(ME_TextEditor *editor); void ME_SendSelChange(ME_TextEditor *editor); void ME_InsertGraphicsFromCursor(ME_TextEditor *editor, int nCursor); +void ME_InsertTableCellFromCursor(ME_TextEditor *editor, int nCursor); void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars); int ME_GetTextLength(ME_TextEditor *editor); int ME_GetTextLengthEx(ME_TextEditor *editor, GETTEXTLENGTHEX *how); diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h index d27eab98b04..7a6012437ea 100644 --- a/dlls/riched20/editstr.h +++ b/dlls/riched20/editstr.h @@ -91,6 +91,10 @@ typedef enum { #define MERF_GRAPHICS 1 /* run is a tab (or, in future, any kind of content whose size is dependent on run position) */ #define MERF_TAB 2 +/* run is a cell boundary */ +#define MERF_CELL 4 + +#define MERF_NONTEXT (MERF_GRAPHICS | MERF_TAB | MERF_CELL) /* run is splittable (contains white spaces in the middle or end) */ #define MERF_SPLITTABLE 0x001000 @@ -136,6 +140,7 @@ typedef struct tagME_Run int nFlags; int nAscent, nDescent; /* pixels above/below baseline */ POINT pt; /* relative to para's position */ + struct tagME_TableCell *pCell; /* for MERF_CELL: points to respective cell in ME_Paragraph */ } ME_Run; typedef struct tagME_Document { @@ -144,9 +149,20 @@ typedef struct tagME_Document { int last_wrapped_line; } ME_Document; +typedef struct tagME_TableCell +{ + int nRightBoundary; + struct tagME_TableCell *next; +} ME_TableCell; + typedef struct tagME_Paragraph { PARAFORMAT2 *pFmt; + + BOOL bTable; /* this paragraph is a table row */ + struct tagME_TableCell *pCells; /* list of cells and their properties */ + struct tagME_TableCell *pLastCell; /* points to the last cell in the list */ + int nLeftMargin, nRightMargin, nFirstMargin; int nCharOfs; int nFlags; diff --git a/dlls/riched20/list.c b/dlls/riched20/list.c index f0d15ddf30b..cf30474664d 100644 --- a/dlls/riched20/list.c +++ b/dlls/riched20/list.c @@ -112,6 +112,7 @@ void ME_DestroyDisplayItem(ME_DisplayItem *item) { /* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */ if (item->type==diParagraph || item->type == diUndoSetParagraphFormat) { FREE_OBJ(item->member.para.pFmt); + ME_DestroyTableCellList(item); } if (item->type==diRun || item->type == diUndoInsertRun) { ME_ReleaseStyle(item->member.run.style); @@ -123,6 +124,23 @@ void ME_DestroyDisplayItem(ME_DisplayItem *item) { FREE_OBJ(item); } +void +ME_DestroyTableCellList(ME_DisplayItem *item) +{ + if (item->member.para.pCells) + { + ME_TableCell *pCell = item->member.para.pCells; + ME_TableCell *pNext; + + while (pCell) { + pNext = pCell->next; + FREE_OBJ(pCell); + pCell = pNext; + } + item->member.para.pCells = NULL; + } +} + ME_DisplayItem *ME_MakeDI(ME_DIType type) { ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem); ZeroMemory(item, sizeof(ME_DisplayItem)); diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c index d35c539e8f9..42166ede31c 100644 --- a/dlls/riched20/paint.c +++ b/dlls/riched20/paint.c @@ -238,7 +238,7 @@ static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Pa start->member.row.nHeight); /* you can always comment it out if you need visible paragraph marks */ - if (run->nFlags & (MERF_ENDPARA|MERF_TAB)) + if (run->nFlags & (MERF_ENDPARA | MERF_TAB | MERF_CELL)) return; if (run->nFlags & MERF_GRAPHICS) diff --git a/dlls/riched20/para.c b/dlls/riched20/para.c index 12f63ace794..174514e6aa2 100644 --- a/dlls/riched20/para.c +++ b/dlls/riched20/para.c @@ -2,6 +2,7 @@ * RichEdit - functions working on paragraphs of text (diParagraph). * * Copyright 2004 by Krzysztof Foltman + * Copyright 2006 by Phil Krylov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -133,6 +134,17 @@ ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME new_para->member.para.nLeftMargin = run_para->member.para.nLeftMargin; new_para->member.para.nRightMargin = run_para->member.para.nRightMargin; new_para->member.para.nFirstMargin = run_para->member.para.nFirstMargin; + + new_para->member.para.bTable = run_para->member.para.bTable; + new_para->member.para.pCells = NULL; + + /* fix paragraph properties. FIXME only needed when called from RTF reader */ + if (run_para->member.para.pCells && !run_para->member.para.bTable) + { + /* Paragraph does not have an \intbl keyword, so any table definition + * stored is invalid */ + ME_DestroyTableCellList(run_para); + } /* insert paragraph into paragraph double linked list */ new_para->member.para.prev_para = run_para; diff --git a/dlls/riched20/reader.c b/dlls/riched20/reader.c index 72cff6567e1..0a8d52a53ea 100644 --- a/dlls/riched20/reader.c +++ b/dlls/riched20/reader.c @@ -2,6 +2,7 @@ * WINE RTF file reader * * Portions Copyright 2004 Mike McCormack for CodeWeavers + * Portions Copyright 2006 by Phil Krylov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -2652,9 +2653,6 @@ static void SpecialChar (RTF_Info *info) case rtfPar: RTFPutUnicodeChar (info, '\n'); break; - case rtfCell: - RTFPutUnicodeChar (info, ' '); /* make sure cells are separated */ - break; case rtfNoBrkSpace: RTFPutUnicodeChar (info, 0x00A0); break; diff --git a/dlls/riched20/run.c b/dlls/riched20/run.c index 473078471e7..86a8c279751 100644 --- a/dlls/riched20/run.c +++ b/dlls/riched20/run.c @@ -4,6 +4,7 @@ * Character/pixel conversions. * * Copyright 2004 by Krzysztof Foltman + * Copyright 2006 by Phil Krylov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -256,7 +257,7 @@ ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, i int i; assert(nVChar > 0 && nVChar < ME_StrVLen(run->strText)); assert(item->type == diRun); - assert(!(item->member.run.nFlags & (MERF_GRAPHICS | MERF_TAB))); + assert(!(item->member.run.nFlags & MERF_NONTEXT)); assert(item->member.run.nCharOfs != -1); item2 = ME_MakeRun(run->style, @@ -390,7 +391,7 @@ int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run) if (!run->strText->nLen) return 0; - if (run->nFlags & MERF_TAB) + if (run->nFlags & (MERF_TAB | MERF_CELL)) { if (cx < run->nWidth/2) return 0; @@ -422,7 +423,7 @@ int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run) if (!run->strText->nLen) return 0; - if (run->nFlags & MERF_TAB) + if (run->nFlags & (MERF_TAB | MERF_CELL)) { if (cx < run->nWidth/2) return 0; @@ -494,7 +495,7 @@ SIZE ME_GetRunSizeCommon(ME_Context *c, ME_Paragraph *para, ME_Run *run, int nLe nLen = nMaxLen; /* FIXME the following call also ensures that TEXTMETRIC structure is filled - * this is wasteful for graphics and TAB runs, but that shouldn't matter + * this is wasteful for MERF_NONTEXT runs, but that shouldn't matter * in practice */ ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size); @@ -534,7 +535,13 @@ SIZE ME_GetRunSizeCommon(ME_Context *c, ME_Paragraph *para, ME_Run *run, int nLe /* descent is unchanged */ return size; } + if (run->nFlags & MERF_CELL) + { + int lpsx = GetDeviceCaps(c->hDC, LOGPIXELSX); + size.cx = run->pCell->nRightBoundary * lpsx / 1440 - run->pt.x; + return size; + } return size; } diff --git a/dlls/riched20/writer.c b/dlls/riched20/writer.c index c8d1226c894..5e2cee54b87 100644 --- a/dlls/riched20/writer.c +++ b/dlls/riched20/writer.c @@ -290,9 +290,27 @@ ME_StreamOutRTFParaProps(ME_TextEditor *editor, ME_DisplayItem *para) char props[STREAMOUT_BUFFER_SIZE] = ""; int i; + if (para->member.para.pCells) + { + ME_TableCell *cell = para->member.para.pCells; + + if (!ME_StreamOutPrint(editor, "\\trowd")) + return FALSE; + do { + sprintf(props, "\\cellx%d", cell->nRightBoundary); + if (!ME_StreamOutPrint(editor, props)) + return FALSE; + cell = cell->next; + } while (cell); + props[0] = '\0'; + } + /* TODO: Don't emit anything if the last PARAFORMAT2 is inherited */ if (!ME_StreamOutPrint(editor, "\\pard")) return FALSE; + + if (para->member.para.bTable) + strcat(props, "\\intbl"); /* TODO: PFM_BORDER. M$ does not emit any keywords for these properties, and * when streaming border keywords in, PFM_BORDER is set, but wBorder field is @@ -634,11 +652,14 @@ ME_StreamOutRTFText(ME_TextEditor *editor, WCHAR *text, LONG nChars) static BOOL ME_StreamOutRTF(ME_TextEditor *editor, int nStart, int nChars, int dwFormat) { - ME_DisplayItem *p, *pEnd; - int nOffset, nEndLen; + ME_DisplayItem *p, *pEnd, *pPara; + int nOffset, nEndLen; + ME_RunOfsFromCharOfs(editor, nStart, &p, &nOffset); ME_RunOfsFromCharOfs(editor, nStart+nChars, &pEnd, &nEndLen); + pPara = ME_GetParagraph(p); + if (!ME_StreamOutRTFHeader(editor, dwFormat)) return FALSE; @@ -669,6 +690,7 @@ ME_StreamOutRTF(ME_TextEditor *editor, int nStart, int nChars, int dwFormat) case diParagraph: if (!ME_StreamOutRTFParaProps(editor, p)) return FALSE; + pPara = p; break; case diRun: if (p == pEnd && !nEndLen) @@ -677,10 +699,19 @@ ME_StreamOutRTF(ME_TextEditor *editor, int nStart, int nChars, int dwFormat) /* TODO: emit embedded objects */ if (p->member.run.nFlags & MERF_GRAPHICS) FIXME("embedded objects are not handled\n"); - if (p->member.run.nFlags & MERF_ENDPARA) { - if (!ME_StreamOutPrint(editor, "\r\n\\par")) + if (p->member.run.nFlags & MERF_CELL) { + if (!ME_StreamOutPrint(editor, "\\cell ")) return FALSE; nChars--; + } else if (p->member.run.nFlags & MERF_ENDPARA) { + if (pPara->member.para.bTable) { + if (!ME_StreamOutPrint(editor, "\\row \r\n")) + return FALSE; + } else { + if (!ME_StreamOutPrint(editor, "\r\n\\par")) + return FALSE; + } + nChars--; if (editor->bEmulateVersion10 && nChars) nChars--; } else {