| /* | |
| * RichEdit - functions working on paragraphs of text (diParagraph). | |
| * | |
| * Copyright 2004 by Krzysztof Foltman | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "editor.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(richedit); | |
| static WCHAR wszParagraphSign[] = {0xB6, 0}; | |
| void ME_MakeFirstParagraph(HDC hDC, ME_TextBuffer *text) | |
| { | |
| PARAFORMAT2 fmt; | |
| CHARFORMAT2W cf; | |
| LOGFONTW lf; | |
| HFONT hf; | |
| ME_DisplayItem *para = ME_MakeDI(diParagraph); | |
| ME_DisplayItem *run; | |
| ME_Style *style; | |
| hf = (HFONT)GetStockObject(SYSTEM_FONT); | |
| assert(hf); | |
| GetObjectW(hf, sizeof(LOGFONTW), &lf); | |
| ZeroMemory(&cf, sizeof(cf)); | |
| cf.cbSize = sizeof(cf); | |
| cf.dwMask = CFM_BACKCOLOR|CFM_COLOR|CFM_FACE|CFM_SIZE|CFM_CHARSET; | |
| cf.dwMask |= CFM_ALLCAPS|CFM_BOLD|CFM_DISABLED|CFM_EMBOSS|CFM_HIDDEN; | |
| cf.dwMask |= CFM_IMPRINT|CFM_ITALIC|CFM_LINK|CFM_OUTLINE|CFM_PROTECTED; | |
| cf.dwMask |= CFM_REVISED|CFM_SHADOW|CFM_SMALLCAPS|CFM_STRIKEOUT; | |
| cf.dwMask |= CFM_SUBSCRIPT|CFM_UNDERLINE; | |
| cf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR; | |
| lstrcpyW(cf.szFaceName, lf.lfFaceName); | |
| cf.yHeight=lf.lfHeight*1440/GetDeviceCaps(hDC, LOGPIXELSY); | |
| if (lf.lfWeight>=700) /* FIXME correct weight ? */ | |
| cf.dwEffects |= CFE_BOLD; | |
| cf.wWeight = lf.lfWeight; | |
| if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC; | |
| if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE; | |
| if (lf.lfStrikeOut) cf.dwEffects |= CFE_STRIKEOUT; | |
| ZeroMemory(&fmt, sizeof(fmt)); | |
| fmt.cbSize = sizeof(fmt); | |
| fmt.dwMask = PFM_ALIGNMENT | PFM_OFFSET | PFM_STARTINDENT | PFM_RIGHTINDENT; | |
| CopyMemory(para->member.para.pFmt, &fmt, sizeof(PARAFORMAT2)); | |
| style = ME_MakeStyle(&cf); | |
| text->pDefaultStyle = style; | |
| run = ME_MakeRun(style, ME_MakeString(wszParagraphSign), MERF_ENDPARA); | |
| run->member.run.nCharOfs = 0; | |
| ME_InsertBefore(text->pLast, para); | |
| ME_InsertBefore(text->pLast, run); | |
| para->member.para.prev_para = text->pFirst; | |
| para->member.para.next_para = text->pLast; | |
| text->pFirst->member.para.next_para = para; | |
| text->pLast->member.para.prev_para = para; | |
| text->pLast->member.para.nCharOfs = 1; | |
| } | |
| void ME_MarkAllForWrapping(ME_TextEditor *editor) | |
| { | |
| ME_MarkForWrapping(editor, editor->pBuffer->pFirst->member.para.next_para, editor->pBuffer->pLast); | |
| } | |
| void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last) | |
| { | |
| while(first != last) | |
| { | |
| first->member.para.nFlags |= MEPF_REWRAP; | |
| first = first->member.para.next_para; | |
| } | |
| } | |
| /* split paragraph at the beginning of the run */ | |
| ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style) | |
| { | |
| ME_DisplayItem *next_para = NULL; | |
| ME_DisplayItem *run_para = NULL; | |
| ME_DisplayItem *new_para = ME_MakeDI(diParagraph); | |
| ME_DisplayItem *end_run = ME_MakeRun(style,ME_MakeString(wszParagraphSign), MERF_ENDPARA); | |
| ME_UndoItem *undo = NULL; | |
| int ofs; | |
| ME_DisplayItem *pp; | |
| assert(run->type == diRun); | |
| run_para = ME_GetParagraph(run); | |
| assert(run_para->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); | |
| ofs = end_run->member.run.nCharOfs = run->member.run.nCharOfs; | |
| next_para = run_para->member.para.next_para; | |
| assert(next_para == ME_FindItemFwd(run_para, diParagraphOrEnd)); | |
| undo = ME_AddUndoItem(editor, diUndoJoinParagraphs, NULL); | |
| if (undo) | |
| undo->nStart = run_para->member.para.nCharOfs + ofs; | |
| /* the new paragraph will have a different starting offset, so let's update its runs */ | |
| pp = run; | |
| while(pp->type == diRun) { | |
| pp->member.run.nCharOfs -= ofs; | |
| pp = ME_FindItemFwd(pp, diRunOrParagraphOrEnd); | |
| } | |
| new_para->member.para.nCharOfs = ME_GetParagraph(run)->member.para.nCharOfs+ofs; | |
| new_para->member.para.nCharOfs += 1; | |
| new_para->member.para.nFlags = MEPF_REWRAP; /* FIXME copy flags (if applicable) */ | |
| /* FIXME initialize format style and call ME_SetParaFormat blah blah */ | |
| CopyMemory(new_para->member.para.pFmt, run_para->member.para.pFmt, sizeof(PARAFORMAT2)); | |
| /* FIXME remove this as soon as nLeftMargin etc are replaced with proper fields of PARAFORMAT2 */ | |
| 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; | |
| /* insert paragraph into paragraph double linked list */ | |
| new_para->member.para.prev_para = run_para; | |
| new_para->member.para.next_para = next_para; | |
| run_para->member.para.next_para = new_para; | |
| next_para->member.para.prev_para = new_para; | |
| /* insert end run of the old paragraph, and new paragraph, into DI double linked list */ | |
| ME_InsertBefore(run, new_para); | |
| ME_InsertBefore(new_para, end_run); | |
| /* force rewrap of the */ | |
| run_para->member.para.prev_para->member.para.nFlags |= MEPF_REWRAP; | |
| new_para->member.para.prev_para->member.para.nFlags |= MEPF_REWRAP; | |
| /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */ | |
| ME_PropagateCharOffset(next_para, 1); | |
| editor->nParagraphs++; | |
| return new_para; | |
| } | |
| /* join tp with tp->member.para.next_para, keeping tp's style; this | |
| * is consistent with the original */ | |
| ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp) | |
| { | |
| ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp; | |
| int i, shift; | |
| ME_UndoItem *undo = NULL; | |
| assert(tp->type == diParagraph); | |
| assert(tp->member.para.next_para); | |
| assert(tp->member.para.next_para->type == diParagraph); | |
| pNext = tp->member.para.next_para; | |
| { | |
| /* null char format operation to store the original char format for the ENDPARA run */ | |
| CHARFORMAT2W fmt; | |
| ME_InitCharFormat2W(&fmt); | |
| ME_SetCharFormat(editor, pNext->member.para.nCharOfs-1, 1, &fmt); | |
| } | |
| undo = ME_AddUndoItem(editor, diUndoSplitParagraph, NULL); | |
| if (undo) | |
| { | |
| undo->nStart = pNext->member.para.nCharOfs-1; | |
| assert(pNext->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); | |
| CopyMemory(undo->di.member.para.pFmt, pNext->member.para.pFmt, sizeof(PARAFORMAT2)); | |
| } | |
| shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - 1; | |
| pRun = ME_FindItemBack(pNext, diRunOrParagraph); | |
| pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph); | |
| assert(pRun); | |
| assert(pRun->type == diRun); | |
| assert(pRun->member.run.nFlags & MERF_ENDPARA); | |
| assert(pFirstRunInNext->type == diRun); | |
| /* if some cursor points at end of paragraph, make it point to the first | |
| run of the next joined paragraph */ | |
| for (i=0; i<editor->nCursors; i++) { | |
| if (editor->pCursors[i].pRun == pRun) { | |
| editor->pCursors[i].pRun = pFirstRunInNext; | |
| editor->pCursors[i].nOffset = 0; | |
| } | |
| } | |
| pTmp = pNext; | |
| do { | |
| pTmp = ME_FindItemFwd(pTmp, diRunOrParagraphOrEnd); | |
| if (pTmp->type != diRun) | |
| break; | |
| TRACE("shifting \"%s\" by %d (previous %d)\n", debugstr_w(pTmp->member.run.strText->szData), shift, pTmp->member.run.nCharOfs); | |
| pTmp->member.run.nCharOfs += shift; | |
| } while(1); | |
| ME_Remove(pRun); | |
| ME_DestroyDisplayItem(pRun); | |
| tp->member.para.next_para = pNext->member.para.next_para; | |
| pNext->member.para.next_para->member.para.prev_para = tp; | |
| ME_Remove(pNext); | |
| ME_DestroyDisplayItem(pNext); | |
| ME_PropagateCharOffset(tp->member.para.next_para, -1); | |
| ME_CheckCharOffsets(editor); | |
| editor->nParagraphs--; | |
| tp->member.para.nFlags |= MEPF_REWRAP; | |
| return tp; | |
| } | |
| ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *item) { | |
| return ME_FindItemBackOrHere(item, diParagraph); | |
| } | |
| static void ME_DumpStyleEffect(char **p, const char *name, PARAFORMAT2 *fmt, int mask) | |
| { | |
| *p += sprintf(*p, "%-22s%s\n", name, (fmt->dwMask & mask) ? ((fmt->wEffects & mask) ? "yes" : "no") : "N/A"); | |
| } | |
| void ME_DumpParaStyleToBuf(PARAFORMAT2 *pFmt, char buf[2048]) | |
| { | |
| /* FIXME only PARAFORMAT styles implemented */ | |
| char *p; | |
| p = buf; | |
| p += sprintf(p, "Alignment: %s\n", | |
| !(pFmt->dwMask & PFM_ALIGNMENT) ? "N/A" : | |
| ((pFmt->wAlignment == PFA_LEFT) ? "left" : | |
| ((pFmt->wAlignment == PFA_RIGHT) ? "right" : | |
| ((pFmt->wAlignment == PFA_CENTER) ? "center" : | |
| /*((pFmt->wAlignment == PFA_JUSTIFY) ? "justify" : "incorrect")*/ | |
| "incorrect")))); | |
| if (pFmt->dwMask & PFM_OFFSET) | |
| p += sprintf(p, "Offset: %d\n", (int)pFmt->dxOffset); | |
| else | |
| p += sprintf(p, "Offset: N/A\n"); | |
| if (pFmt->dwMask & PFM_OFFSETINDENT) | |
| p += sprintf(p, "Offset indent: %d\n", (int)pFmt->dxStartIndent); | |
| else | |
| p += sprintf(p, "Offset indent: N/A\n"); | |
| if (pFmt->dwMask & PFM_STARTINDENT) | |
| p += sprintf(p, "Start indent: %d\n", (int)pFmt->dxStartIndent); | |
| else | |
| p += sprintf(p, "Start indent: N/A\n"); | |
| if (pFmt->dwMask & PFM_RIGHTINDENT) | |
| p += sprintf(p, "Right indent: %d\n", (int)pFmt->dxRightIndent); | |
| else | |
| p += sprintf(p, "Right indent: N/A\n"); | |
| ME_DumpStyleEffect(&p, "Page break before:", pFmt, PFM_PAGEBREAKBEFORE); | |
| } | |
| void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt) | |
| { | |
| PARAFORMAT2 copy; | |
| assert(sizeof(*para->member.para.pFmt) == sizeof(PARAFORMAT2)); | |
| ME_AddUndoItem(editor, diUndoSetParagraphFormat, para); | |
| CopyMemory(©, para->member.para.pFmt, sizeof(PARAFORMAT2)); | |
| if (pFmt->dwMask & PFM_ALIGNMENT) | |
| para->member.para.pFmt->wAlignment = pFmt->wAlignment; | |
| /* FIXME to be continued (indents, bulleting and such) */ | |
| if (memcmp(©, para->member.para.pFmt, sizeof(PARAFORMAT2))) | |
| para->member.para.nFlags |= MEPF_REWRAP; | |
| } | |
| void ME_SetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt) | |
| { | |
| int nFrom, nTo; | |
| ME_DisplayItem *para, *para_end, *run; | |
| int nOffset; | |
| ME_GetSelection(editor, &nFrom, &nTo); | |
| if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */ | |
| nTo--; | |
| ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset); | |
| para = ME_GetParagraph(run); | |
| ME_RunOfsFromCharOfs(editor, nTo, &run, &nOffset); | |
| para_end = ME_GetParagraph(run); | |
| do { | |
| ME_SetParaFormat(editor, para, pFmt); | |
| if (para == para_end) | |
| break; | |
| para = para->member.para.next_para; | |
| } while(1); | |
| } | |
| void ME_GetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt) | |
| { | |
| if (pFmt->cbSize >= sizeof(PARAFORMAT2)) | |
| { | |
| CopyMemory(pFmt, para->member.para.pFmt, sizeof(PARAFORMAT2)); | |
| return; | |
| } | |
| CopyMemory(pFmt, para->member.para.pFmt, pFmt->cbSize); | |
| } | |
| void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt) | |
| { | |
| int nFrom, nTo; | |
| ME_DisplayItem *para, *para_end, *run; | |
| int nOffset; | |
| PARAFORMAT2 tmp; | |
| ME_GetSelection(editor, &nFrom, &nTo); | |
| if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */ | |
| nTo--; | |
| ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset); | |
| para = ME_GetParagraph(run); | |
| ME_RunOfsFromCharOfs(editor, nTo, &run, &nOffset); | |
| para_end = ME_GetParagraph(run); | |
| ME_GetParaFormat(editor, para, pFmt); | |
| if (para == para_end) return; | |
| do { | |
| ZeroMemory(&tmp, sizeof(tmp)); | |
| tmp.cbSize = sizeof(tmp); | |
| ME_GetParaFormat(editor, para, &tmp); | |
| assert(tmp.dwMask & PFM_ALIGNMENT); | |
| if (pFmt->wAlignment != tmp.wAlignment) | |
| pFmt->dwMask &= ~PFM_ALIGNMENT; | |
| if (para == para_end) | |
| return; | |
| para = para->member.para.next_para; | |
| } while(1); | |
| } |