--- /dev/null
+/*
+ * Compatibility header
+ *
+ * This header is wrapper to allow compilation of Wine DLLs under ReactOS
+ * build system. It contains definitions commonly refered to as Wineisms
+ * and definitions that are missing in w32api.
+ */
+
+#include <richedit.h>
+
+#include_next <richole.h>
+
+#ifndef WINE_RICHOLE_H_INCLUDED
+#define WINE_RICHOLE_H_INCLUDED
+
+#ifdef COBJMACROS
+/*** IUnknown methods ***/
+#define IRichEditOle_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IRichEditOle_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IRichEditOle_Release(p) (p)->lpVtbl->Release(p)
+/*** IRichEditOle methods ***/
+#define IRichEditOle_GetClientSite(p,a) (p)->lpVtbl->GetClientSite(p,a)
+#define IRichEditOle_GetObjectCount(p) (p)->lpVtbl->GetObjectCount(p)
+#define IRichEditOle_GetLinkCount(p) (p)->lpVtbl->GetLinkCount(p)
+#define IRichEditOle_GetObject(p,a,b,c) (p)->lpVtbl->GetObject(p,a,b,c)
+#define IRichEditOle_InsertObject(p,a) (p)->lpVtbl->InsertObject(p,a)
+#define IRichEditOle_ConvertObject(p,a,b,c) (p)->lpVtbl->ConvertObject(p,a,b,c)
+#define IRichEditOle_ActivateAs(p,a,b) (p)->lpVtbl->ActivateAs(p,a,b)
+#define IRichEditOle_SetHostNames(p,a,b) (p)->lpVtbl->SetHostNames(p,a,b)
+#define IRichEditOle_SetLinkAvailable(p,a,b) (p)->lpVtbl->SetLinkAvailable(p,a,b)
+#define IRichEditOle_SetDvaspect(p,a,b) (p)->lpVtbl->SetDvaspect(p,a,b)
+#define IRichEditOle_HandsOffStorage(p,a) (p)->lpVtbl->HandsOffStorage(p,a)
+#define IRichEditOle_SaveCompleted(p,a,b) (p)->lpVtbl->SaveCompleted(p,a,b)
+#define IRichEditOle_InPlaceDeactivate(p) (p)->lpVtbl->InPlaceDeactivate(p)
+#define IRichEditOle_ContextSensitiveHelp(p,a) (p)->lpVtbl->ContextSensitiveHelp(p,a)
+#define IRichEditOle_GetClipboardData(p,a,b,c) (p)->lpVtbl->GetClipboardData(p,a,b,c)
+#define IRichEditOle_ImportDataObject(p,a,b,c) (p)->lpVtbl->ImportDataObject(p,a,b,c)
+#endif
+
+#endif /* WINE_RICHOLE_H_INCLUDED */
--- /dev/null
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = riched20.dll
+IMPORTS = user32 gdi32 kernel32
+EXTRALIBS = -luuid
+
+C_SRCS = \
+ caret.c \
+ context.c \
+ editor.c \
+ list.c \
+ paint.c \
+ para.c \
+ reader.c \
+ richole.c \
+ row.c \
+ run.c \
+ string.c \
+ style.c \
+ undo.c \
+ wrap.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
--- /dev/null
+# $Id$
+
+TARGET_NAME = riched20
+
+TARGET_OBJECTS = @C_SRCS@
+
+TARGET_CFLAGS = @EXTRADEFS@ -D__REACTOS__ -D__WINESRC__
+
+TARGET_SDKLIBS = @IMPORTS@ wine.a wine_uuid.a ntdll.a
+
+TARGET_BASE = $(TARGET_BASE_LIB_SETUPAPI)
+
+TARGET_NORC = yes
+
+default: all
+
+DEP_OBJECTS = $(TARGET_OBJECTS)
+
+include $(TOOLS_PATH)/depend.mk
--- /dev/null
+/*
+ * RichEdit - Caret and selection functions.
+ *
+ * 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);
+
+void ME_GetSelection(ME_TextEditor *editor, int *from, int *to)
+{
+ *from = ME_GetCursorOfs(editor, 0);
+ *to = ME_GetCursorOfs(editor, 1);
+
+ if (*from > *to)
+ {
+ int tmp = *from;
+ *from = *to;
+ *to = tmp;
+ }
+}
+
+int ME_GetTextLength(ME_TextEditor *editor)
+{
+ return ME_CharOfsFromRunOfs(editor, ME_FindItemBack(editor->pBuffer->pLast, diRun), 0);
+}
+
+void ME_SetSelection(ME_TextEditor *editor, int from, int to)
+{
+ if (from == 0 && to == -1)
+ {
+ editor->pCursors[1].pRun = ME_FindItemFwd(editor->pBuffer->pFirst, diRun);
+ editor->pCursors[1].nOffset = 0;
+ editor->pCursors[0].pRun = ME_FindItemBack(editor->pBuffer->pLast, diRun);
+ editor->pCursors[0].nOffset = 0;
+ ME_Repaint(editor);
+ ME_ClearTempStyle(editor);
+ return;
+ }
+ if (from == -1)
+ {
+ editor->pCursors[1] = editor->pCursors[0];
+ ME_Repaint(editor);
+ ME_ClearTempStyle(editor);
+ return;
+ }
+ if (from>to)
+ {
+ int tmp = from;
+ from = to;
+ to = tmp;
+ }
+ ME_RunOfsFromCharOfs(editor, from, &editor->pCursors[1].pRun, &editor->pCursors[1].nOffset);
+ ME_RunOfsFromCharOfs(editor, to, &editor->pCursors[0].pRun, &editor->pCursors[0].nOffset);
+}
+
+void ME_MoveCaret(ME_TextEditor *editor)
+{
+ HDC hDC = GetDC(editor->hWnd);
+ ME_Context c;
+
+ ME_Cursor *pCursor = &editor->pCursors[0];
+ ME_DisplayItem *pCursorRun = pCursor->pRun;
+ ME_DisplayItem *pSizeRun = pCursor->pRun;
+
+ ME_InitContext(&c, editor, hDC);
+ assert(!pCursor->nOffset || !editor->bCaretAtEnd);
+
+ if (pCursorRun->type == diRun) {
+ ME_DisplayItem *row = ME_FindItemBack(pCursorRun, diStartRowOrParagraph);
+ if (row) {
+ ME_DisplayItem *run = pCursorRun;
+ ME_DisplayItem *para;
+ SIZE sz = {0, 0};
+ if (!pCursor->nOffset && !editor->bCaretAtEnd)
+ {
+ ME_DisplayItem *prev = ME_FindItemBack(pCursorRun, diRunOrStartRow);
+ if (prev->type == diRun)
+ pSizeRun = prev;
+ }
+ assert(row->type == diStartRow); /* paragraph -> run without start row ?*/
+ para = ME_FindItemBack(row, diParagraph);
+ if (editor->bCaretAtEnd && !pCursor->nOffset &&
+ run == ME_FindItemFwd(row, diRun))
+ {
+ ME_DisplayItem *tmp = ME_FindItemBack(row, diRunOrParagraph);
+ if (tmp->type == diRun)
+ {
+ row = ME_FindItemBack(tmp, diStartRow);
+ pSizeRun = run = tmp;
+ sz = ME_GetRunSize(&c, &run->member.run, ME_StrLen(run->member.run.strText));
+ }
+ }
+ if (pCursor->nOffset && !(run->member.run.nFlags & MERF_SKIPPED)) {
+ sz = ME_GetRunSize(&c, &run->member.run, pCursor->nOffset);
+ }
+ CreateCaret(editor->hWnd, NULL, 0, pSizeRun->member.run.nAscent+pSizeRun->member.run.nDescent);
+ SetCaretPos(run->member.run.pt.x+sz.cx,
+ para->member.para.nYPos+row->member.row.nBaseline+pSizeRun->member.run.pt.y-pSizeRun->member.run.nAscent-GetScrollPos(editor->hWnd, SB_VERT));
+ } else {
+ assert(0 == "Wrapped paragraph run without a row?");
+ CreateCaret(editor->hWnd, NULL, 0, 10);
+ SetCaretPos(0,0);
+ }
+ }
+ else {
+ assert(0 == "Cursor not on a run");
+ CreateCaret(editor->hWnd, NULL, 0, 10); /* FIXME use global font */
+ SetCaretPos(0,0);
+ }
+ ME_DestroyContext(&c);
+ ReleaseDC(editor->hWnd, hDC);
+}
+
+void ME_ShowCaret(ME_TextEditor *ed)
+{
+ ME_MoveCaret(ed);
+ ShowCaret(ed->hWnd);
+}
+
+void ME_HideCaret(ME_TextEditor *ed)
+{
+ HideCaret(ed->hWnd);
+ DestroyCaret();
+}
+
+void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs,
+ int nChars)
+{
+ ME_Cursor c;
+ int shift = 0;
+
+ while(nChars > 0)
+ {
+ ME_Run *run;
+ ME_CursorFromCharOfs(editor, nOfs, &c);
+ run = &c.pRun->member.run;
+ if (run->nFlags & MERF_ENDPARA) {
+ if (!ME_FindItemFwd(c.pRun, diParagraph))
+ {
+ return;
+ }
+ ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun));
+ /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */
+ ME_CheckCharOffsets(editor);
+ nChars--;
+ continue;
+ }
+ else
+ {
+ ME_Cursor cursor;
+ int nIntendedChars = nChars;
+ int nCharsToDelete = nChars;
+ int i;
+ int loc = c.nOffset;
+
+ ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags |= MEPF_REWRAP;
+
+ cursor = c;
+ ME_StrRelPos(run->strText, loc, &nChars);
+ /* nChars is the number of characters that should be deleted from the
+ FOLLOWING runs (these AFTER cursor.pRun)
+ nCharsToDelete is a number of chars to delete from THIS run */
+ nCharsToDelete -= nChars;
+ shift -= nCharsToDelete;
+ TRACE("Deleting %d (intended %d-remaning %d) chars at %d in '%s' (%d)\n",
+ nCharsToDelete, nIntendedChars, nChars, c.nOffset,
+ debugstr_w(run->strText->szData), run->strText->nLen);
+
+ if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete)
+ {
+ /* undo = reinsert whole run */
+ /* nOfs is a character offset (from the start of the document
+ to the current (deleted) run */
+ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun);
+ if (pUndo)
+ pUndo->di.member.run.nCharOfs = nOfs;
+ }
+ else
+ {
+ /* undo = reinsert partial run */
+ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun);
+ if (pUndo) {
+ ME_DestroyString(pUndo->di.member.run.strText);
+ pUndo->di.member.run.nCharOfs = nOfs;
+ pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete);
+ }
+ }
+ TRACE("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen);
+ TRACE("Shift value: %d\n", shift);
+ ME_StrDeleteV(run->strText, c.nOffset, nCharsToDelete);
+
+ /* update cursors (including c) */
+ for (i=-1; i<editor->nCursors; i++) {
+ ME_Cursor *pThisCur = editor->pCursors + i;
+ if (i == -1) pThisCur = &c;
+ if (pThisCur->pRun == cursor.pRun) {
+ if (pThisCur->nOffset > cursor.nOffset) {
+ if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete)
+ pThisCur->nOffset = cursor.nOffset;
+ else
+ pThisCur->nOffset -= nCharsToDelete;
+ assert(pThisCur->nOffset >= 0);
+ assert(pThisCur->nOffset <= ME_StrVLen(run->strText));
+ }
+ if (pThisCur->nOffset == ME_StrVLen(run->strText))
+ {
+ pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd);
+ assert(pThisCur->pRun->type == diRun);
+ pThisCur->nOffset = 0;
+ }
+ }
+ }
+
+ /* c = updated data now */
+
+ if (c.pRun == cursor.pRun)
+ ME_SkipAndPropagateCharOffset(c.pRun, shift);
+ else
+ ME_PropagateCharOffset(c.pRun, shift);
+
+ if (!ME_StrVLen(cursor.pRun->member.run.strText))
+ {
+ TRACE("Removing useless run\n");
+ ME_Remove(cursor.pRun);
+ ME_DestroyDisplayItem(cursor.pRun);
+ }
+
+ shift = 0;
+ /*
+ ME_CheckCharOffsets(editor);
+ */
+ continue;
+ }
+ }
+}
+
+void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor,
+ int nChars)
+{
+ assert(nCursor>=0 && nCursor<editor->nCursors);
+ ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars);
+}
+
+static WCHAR wszSpace[] = {' ', 0};
+
+/* FIXME this is temporary, just to have something to test how bad graphics handler is */
+void ME_InsertGraphicsFromCursor(ME_TextEditor *editor, int nCursor)
+{
+ ME_Cursor *pCursor = &editor->pCursors[nCursor];
+ ME_DisplayItem *pItem = NULL;
+ ME_DisplayItem *pNewRun = NULL;
+ ME_Style *pStyle = ME_GetInsertStyle(editor, nCursor);
+ ME_UndoItem *pUndo;
+
+ /* FIXME no no no */
+ if (ME_IsSelection(editor))
+ ME_DeleteSelection(editor);
+
+ pUndo = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
+ if (pUndo) {
+ pUndo->nStart = pCursor->nOffset + pCursor->pRun->member.run.nCharOfs + ME_GetParagraph(pCursor->pRun)->member.para.nCharOfs;
+ pUndo->nLen = 1;
+ }
+ if (pCursor->nOffset)
+ {
+ ME_SplitRunSimple(editor, pCursor->pRun, pCursor->nOffset);
+ }
+ pItem = pCursor->pRun;
+ pNewRun = ME_MakeRun(pStyle, ME_MakeStringN(wszSpace, 1), MERF_GRAPHICS);
+ pNewRun->member.run.nCharOfs = pCursor->pRun->member.run.nCharOfs;
+ ME_InsertBefore(pCursor->pRun, pNewRun);
+ ME_PropagateCharOffset(pItem, 1);
+ ME_CheckCharOffsets(editor);
+ ME_SendSelChange(editor);
+}
+
+void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor,
+ const WCHAR *str, int len, ME_Style *style)
+{
+ const WCHAR *pos;
+ ME_Cursor *p = NULL;
+
+ assert(style);
+ editor->bCaretAtEnd = FALSE;
+
+ /*
+ if (!style)
+ style = ME_GetInsertStyle(editor, nCursor);
+ else
+ ME_AddRefStyle(style);
+ */
+ ME_AddRefStyle(style);
+
+ /* FIXME really HERE ? */
+ if (ME_IsSelection(editor))
+ ME_DeleteSelection(editor);
+
+ assert(nCursor>=0 && nCursor<editor->nCursors);
+ if (len == -1)
+ len = lstrlenW(str);
+ pos = str;
+ /* FIXME this sucks - no respect for unicode (what else can be a line separator in unicode?) */
+ while(pos-str < len && *pos != '\r' && *pos != '\n')
+ pos++;
+ /* handle EOLs */
+ if (pos-str < len) {
+ ME_DisplayItem *tp, *end_run;
+ ME_Paragraph *para;
+ ME_Style *tmp_style;
+ if (pos!=str)
+ ME_InsertTextFromCursor(editor, nCursor, str, pos-str, style);
+ p = &editor->pCursors[nCursor];
+ tp = ME_FindItemBack(p->pRun, diParagraph);
+ para = &tp->member.para;
+ assert(tp);
+ if (p->nOffset) {
+ ME_SplitRunSimple(editor, p->pRun, p->nOffset);
+ p = &editor->pCursors[nCursor];
+ }
+ tmp_style = ME_GetInsertStyle(editor, nCursor);
+ /* ME_SplitParagraph increases style refcount */
+ tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style);
+ p->pRun = ME_FindItemFwd(tp, diRun);
+ end_run = ME_FindItemBack(tp, diRun);
+ ME_ReleaseStyle(end_run->member.run.style);
+ end_run->member.run.style = tmp_style;
+ p->nOffset = 0;
+ if(pos-str < len && *pos =='\r')
+ pos++;
+ if(pos-str < len && *pos =='\n')
+ pos++;
+ if(pos-str < len) {
+ ME_InsertTextFromCursor(editor, nCursor, pos, len-(pos-str), style);
+ }
+ ME_ReleaseStyle(style);
+ return;
+ }
+ p = &editor->pCursors[nCursor];
+ if (style) {
+ ME_DisplayItem *pNewRun = NULL;
+
+ assert(p->pRun->type == diRun);
+ pNewRun = ME_MakeRun(style, ME_MakeStringN(str, len), 0); /* addrefs style */
+ ME_InsertRun(editor, ME_CharOfsFromRunOfs(editor, p->pRun, p->nOffset), pNewRun);
+ ME_DestroyDisplayItem(pNewRun);
+ ME_ReleaseStyle(style);
+ return;
+ } else {
+ assert(0);
+ }
+}
+
+BOOL ME_ArrowLeft(ME_TextEditor *editor, ME_Cursor *p)
+{
+ if (p->nOffset) {
+ p->nOffset = ME_StrRelPos2(p->pRun->member.run.strText, p->nOffset, -1);
+ return TRUE;
+ }
+ else
+ {
+ ME_DisplayItem *pRun = ME_FindItemBack(p->pRun, diRunOrParagraph);
+ assert(pRun);
+ if (pRun->type == diRun) {
+ p->pRun = pRun;
+ assert(p->pRun->type == diRun);
+ assert(pRun->member.run.strText->nLen);
+ p->nOffset = pRun->member.run.strText->nLen;
+ if (p->nOffset) {
+ p->nOffset = ME_StrRelPos2(pRun->member.run.strText, p->nOffset, -1);
+ return TRUE;
+ }
+ else
+ assert(0);
+ }
+ if (pRun->type == diParagraph)
+ {
+ if (pRun->member.para.prev_para->type == diTextStart)
+ return FALSE;
+ assert(pRun->member.para.prev_para->type == diParagraph);
+ pRun = ME_FindItemBack(pRun, diRunOrParagraph);
+ /* every paragraph ought to have at least one run */
+ assert(pRun && pRun->type == diRun);
+ assert(pRun->member.run.nFlags & MERF_ENDPARA);
+ p->pRun = pRun;
+ p->nOffset = 0;
+ return TRUE;
+ }
+ assert(0);
+ }
+}
+
+BOOL ME_ArrowRight(ME_TextEditor *editor, ME_Cursor *p)
+{
+ int new_ofs = ME_StrRelPos2(p->pRun->member.run.strText, p->nOffset, 1);
+ if (new_ofs<p->pRun->member.run.strText->nLen) {
+ p->nOffset = new_ofs;
+ }
+ else
+ {
+ ME_DisplayItem *pRun = ME_FindItemFwd(p->pRun, diRun);
+ if (pRun) {
+ p->pRun = pRun;
+ assert(p->pRun->type == diRun);
+ p->nOffset = 0;
+ }
+ }
+ return TRUE;
+}
+
+int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor)
+{
+ ME_Cursor *pCursor = &editor->pCursors[nCursor];
+
+ return ME_GetParagraph(pCursor->pRun)->member.para.nCharOfs
+ + pCursor->pRun->member.run.nCharOfs + pCursor->nOffset;
+}
+
+int ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol)
+{
+ ME_DisplayItem *p = editor->pBuffer->pFirst->member.para.next_para;
+ int rx = 0;
+
+ if (is_eol)
+ *is_eol = 0;
+
+ while(p != editor->pBuffer->pLast)
+ {
+ if (p->type == diParagraph)
+ {
+ int ry = y - p->member.para.nYPos;
+ if (ry < 0)
+ {
+ result->pRun = ME_FindItemFwd(p, diRun);
+ result->nOffset = 0;
+ return 0;
+ }
+ if (ry >= p->member.para.nHeight)
+ {
+ p = p->member.para.next_para;
+ continue;
+ }
+ p = ME_FindItemFwd(p, diStartRow);
+ y = ry;
+ continue;
+ }
+ if (p->type == diStartRow)
+ {
+ int ry = y - p->member.row.nYPos;
+ if (ry < 0)
+ return 0;
+ if (ry >= p->member.row.nHeight)
+ {
+ p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
+ if (p->type != diStartRow)
+ return 0;
+ continue;
+ }
+ p = ME_FindItemFwd(p, diRun);
+ continue;
+ }
+ if (p->type == diRun)
+ {
+ ME_DisplayItem *pp;
+ rx = x - p->member.run.pt.x;
+ if (rx < 0)
+ rx = 0;
+ if (rx >= p->member.run.nWidth) /* not this run yet... find next item */
+ {
+ pp = p;
+ do {
+ p = p->next;
+ if (p->type == diRun)
+ {
+ rx = x - p->member.run.pt.x;
+ goto continue_search;
+ }
+ if (p->type == diStartRow)
+ {
+ p = ME_FindItemFwd(p, diRun);
+ if (is_eol)
+ *is_eol = 1;
+ rx = 0; /* FIXME not sure */
+ goto found_here;
+ }
+ if (p->type == diParagraph || p->type == diTextEnd)
+ {
+ rx = 0; /* FIXME not sure */
+ p = pp;
+ goto found_here;
+ }
+ } while(1);
+ continue;
+ }
+ found_here:
+ if (p->member.run.nFlags & MERF_ENDPARA)
+ rx = 0;
+ result->pRun = p;
+ result->nOffset = ME_CharFromPointCursor(editor, rx, &p->member.run);
+ if (editor->pCursors[0].nOffset == p->member.run.strText->nLen && rx)
+ {
+ result->pRun = ME_FindItemFwd(editor->pCursors[0].pRun, diRun);
+ result->nOffset = 0;
+ }
+ return 1;
+ }
+ assert(0);
+ continue_search:
+ ;
+ }
+ result->pRun = ME_FindItemBack(p, diRun);
+ result->nOffset = 0;
+ assert(result->pRun->member.run.nFlags & MERF_ENDPARA);
+ return 0;
+}
+
+void ME_LButtonDown(ME_TextEditor *editor, int x, int y)
+{
+ ME_Cursor tmp_cursor;
+ int is_selection = 0;
+
+ editor->nUDArrowX = -1;
+
+ y += GetScrollPos(editor->hWnd, SB_VERT);
+
+ tmp_cursor = editor->pCursors[0];
+ is_selection = ME_IsSelection(editor);
+
+ ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd);
+
+ if (GetKeyState(VK_SHIFT)>=0)
+ {
+ editor->pCursors[1] = editor->pCursors[0];
+ }
+ else
+ {
+ if (!is_selection) {
+ editor->pCursors[1] = tmp_cursor;
+ is_selection = 1;
+ }
+ }
+ HideCaret(editor->hWnd);
+ ME_MoveCaret(editor);
+ if (is_selection)
+ ME_Repaint(editor);
+ ShowCaret(editor->hWnd);
+ ME_ClearTempStyle(editor);
+ ME_SendSelChange(editor);
+}
+
+void ME_MouseMove(ME_TextEditor *editor, int x, int y)
+{
+ ME_Cursor tmp_cursor;
+
+ y += GetScrollPos(editor->hWnd, SB_VERT);
+
+ tmp_cursor = editor->pCursors[0];
+ if (!ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd))
+ /* return */;
+
+ if (tmp_cursor.pRun == editor->pCursors[0].pRun &&
+ tmp_cursor.nOffset == editor->pCursors[0].nOffset)
+ return;
+
+ HideCaret(editor->hWnd);
+ ME_MoveCaret(editor);
+ ME_Repaint(editor);
+ ShowCaret(editor->hWnd);
+ ME_SendSelChange(editor);
+}
+
+static ME_DisplayItem *ME_FindRunInRow(ME_TextEditor *editor, ME_DisplayItem *pRow,
+ int x, int *pOffset, int *pbCaretAtEnd)
+{
+ ME_DisplayItem *pNext, *pLastRun;
+ pNext = ME_FindItemFwd(pRow, diRunOrStartRow);
+ assert(pNext->type == diRun);
+ pLastRun = pNext;
+ *pbCaretAtEnd = FALSE;
+ do {
+ int run_x = pNext->member.run.pt.x;
+ int width = pNext->member.run.nWidth;
+ if (x < run_x)
+ {
+ if (pOffset) *pOffset = 0;
+ return pNext;
+ }
+ if (x >= run_x && x < run_x+width)
+ {
+ int ch = ME_CharFromPointCursor(editor, x-run_x, &pNext->member.run);
+ ME_String *s = pNext->member.run.strText;
+ if (ch < s->nLen) {
+ if (pOffset)
+ *pOffset = ch;
+ return pNext;
+ }
+ }
+ pLastRun = pNext;
+ pNext = ME_FindItemFwd(pNext, diRunOrStartRow);
+ } while(pNext && pNext->type == diRun);
+
+ if ((pLastRun->member.run.nFlags & MERF_ENDPARA) == 0)
+ {
+ pNext = ME_FindItemFwd(pNext, diRun);
+ if (pbCaretAtEnd) *pbCaretAtEnd = 1;
+ if (pOffset) *pOffset = 0;
+ return pNext;
+ } else {
+ if (pbCaretAtEnd) *pbCaretAtEnd = 0;
+ if (pOffset) *pOffset = 0;
+ return pLastRun;
+ }
+}
+
+static int ME_GetXForArrow(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRun = pCursor->pRun;
+ int x;
+
+ if (editor->nUDArrowX != -1)
+ x = editor->nUDArrowX;
+ else {
+ if (editor->bCaretAtEnd)
+ {
+ pRun = ME_FindItemBack(pRun, diRun);
+ assert(pRun);
+ x = pRun->member.run.pt.x + pRun->member.run.nWidth;
+ }
+ else {
+ x = pRun->member.run.pt.x;
+ x += ME_PointFromChar(editor, &pRun->member.run, pCursor->nOffset);
+ }
+ editor->nUDArrowX = x;
+ }
+ return x;
+}
+
+void ME_ArrowUp(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRun = pCursor->pRun;
+ ME_DisplayItem *pItem, *pItem2;
+ int x = ME_GetXForArrow(editor, pCursor);
+
+ if (editor->bCaretAtEnd && !pCursor->nOffset)
+ {
+ pRun = ME_FindItemBack(pRun, diRun);
+ if (!pRun)
+ return;
+ }
+
+ /* start of this row */
+ pItem = ME_FindItemBack(pRun, diStartRow);
+ assert(pItem);
+ /* start of the previous row */
+ pItem2 = ME_FindItemBack(pItem, diStartRow);
+ /* no previous row = the first line of the first paragraph */
+ if (!pItem2) /* can't go up - don't go BOL (as in MS richedit) */
+ return;
+ /* FIXME
+ ME_WrapTextParagraph(editor, ME_FindItemBack(pItem2, diParagraph));
+ */
+ pCursor->pRun = ME_FindRunInRow(editor, pItem2, x, &pCursor->nOffset, &editor->bCaretAtEnd);
+}
+
+void ME_ArrowDown(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRun = pCursor->pRun;
+ ME_DisplayItem *pItem;
+ int x = ME_GetXForArrow(editor, pCursor);
+ if (!pCursor->nOffset && editor->bCaretAtEnd)
+ {
+ pRun = ME_FindItemBack(pRun, diRun);
+/* x = pRun->member.run.pt.x + pRun->member.run.nWidth; */
+ }
+ /* start of the next row */
+ pItem = ME_FindItemFwd(pRun, diStartRow);
+ /* FIXME If diParagraph is before diStartRow, wrap the next paragraph?
+ */
+ if (!pItem)
+ {
+ /* next row not found - ignore */
+ return;
+ }
+ pCursor->pRun = ME_FindRunInRow(editor, pItem, x, &pCursor->nOffset, &editor->bCaretAtEnd);
+ assert(pCursor->pRun);
+ assert(pCursor->pRun->type == diRun);
+}
+
+void ME_ArrowHome(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow);
+ if (pRow) {
+ ME_DisplayItem *pRun;
+ if (editor->bCaretAtEnd && !pCursor->nOffset) {
+ pRow = ME_FindItemBack(pRow, diStartRow);
+ if (!pRow)
+ return;
+ }
+ pRun = ME_FindItemFwd(pRow, diRun);
+ if (pRun) {
+ pCursor->pRun = pRun;
+ pCursor->nOffset = 0;
+ }
+ }
+ editor->bCaretAtEnd = FALSE;
+}
+
+void ME_ArrowCtrlHome(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diTextStart);
+ if (pRow) {
+ ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun);
+ if (pRun) {
+ pCursor->pRun = pRun;
+ pCursor->nOffset = 0;
+ }
+ }
+}
+
+void ME_ArrowEnd(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRow;
+
+ if (editor->bCaretAtEnd && !pCursor->nOffset)
+ return;
+
+ pRow = ME_FindItemFwd(pCursor->pRun, diStartRowOrParagraphOrEnd);
+ assert(pRow);
+ if (pRow->type == diStartRow) {
+ /* FIXME WTF was I thinking about here ? */
+ ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun);
+ assert(pRun);
+ pCursor->pRun = pRun;
+ pCursor->nOffset = 0;
+ editor->bCaretAtEnd = 1;
+ return;
+ }
+ pCursor->pRun = ME_FindItemBack(pRow, diRun);
+ assert(pCursor->pRun && pCursor->pRun->member.run.nFlags & MERF_ENDPARA);
+ pCursor->nOffset = 0;
+ editor->bCaretAtEnd = FALSE;
+}
+
+void ME_ArrowCtrlEnd(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *p = ME_FindItemFwd(pCursor->pRun, diTextEnd);
+ assert(p);
+ p = ME_FindItemBack(p, diRun);
+ assert(p);
+ assert(p->member.run.nFlags & MERF_ENDPARA);
+ pCursor->pRun = p;
+ pCursor->nOffset = 0;
+ editor->bCaretAtEnd = FALSE;
+}
+
+BOOL ME_IsSelection(ME_TextEditor *editor)
+{
+ return memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor))!=0;
+}
+
+int ME_GetSelCursor(ME_TextEditor *editor, int dir)
+{
+ int cdir = ME_GetCursorOfs(editor, 0) - ME_GetCursorOfs(editor, 1);
+
+ if (cdir*dir>0)
+ return 0;
+ else
+ return 1;
+}
+
+BOOL ME_CancelSelection(ME_TextEditor *editor, int dir)
+{
+ int cdir;
+
+ if (GetKeyState(VK_SHIFT)<0)
+ return FALSE;
+ if (!memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor)))
+ return FALSE;
+
+ cdir = ME_GetCursorOfs(editor, 0) - ME_GetCursorOfs(editor, 1);
+
+ if (cdir*dir>0)
+ editor->pCursors[1] = editor->pCursors[0];
+ else
+ editor->pCursors[0] = editor->pCursors[1];
+ /* FIXME optimize */
+ ME_MarkAllForWrapping(editor);
+ ME_Repaint(editor);
+ return TRUE;
+}
+
+void ME_RepaintSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor)
+{
+ ME_Cursor old_anchor = editor->pCursors[1];
+ BOOL bRedraw = FALSE;
+ bRedraw = memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor));
+
+ if (bRedraw)
+ {
+ /* FIXME optimize */
+ ME_MarkAllForWrapping(editor);
+ }
+
+ if (GetKeyState(VK_SHIFT)>=0) /* cancelling selection */
+ {
+ /* any selection was present ? if so, it's no more, repaint ! */
+ editor->pCursors[1] = editor->pCursors[0];
+ if (memcmp(pTempCursor, &old_anchor, sizeof(ME_Cursor))) {
+ ME_Repaint(editor);
+ return;
+ }
+ return;
+ }
+ else
+ {
+ if (!memcmp(pTempCursor, &editor->pCursors[1], sizeof(ME_Cursor))) /* starting selection */
+ {
+ editor->pCursors[1] = *pTempCursor;
+ }
+ }
+
+ ME_Repaint(editor);
+}
+
+void ME_DeleteSelection(ME_TextEditor *editor)
+{
+ int from, to;
+ ME_GetSelection(editor, &from, &to);
+ ME_DeleteTextAtCursor(editor, ME_GetSelCursor(editor,-1), to-from);
+}
+
+ME_Style *ME_GetSelectionInsertStyle(ME_TextEditor *editor)
+{
+ ME_Style *style;
+ int from, to;
+ ME_Cursor c;
+
+ ME_GetSelection(editor, &from, &to);
+ ME_CursorFromCharOfs(editor, from, &c);
+ if (from != to) {
+ style = c.pRun->member.run.style;
+ ME_AddRefStyle(style); /* ME_GetInsertStyle has already done that */
+ }
+ else
+ style = ME_GetInsertStyle(editor, 0);
+ return style;
+}
+
+void ME_SendSelChange(ME_TextEditor *editor)
+{
+ SELCHANGE sc;
+ if (!(editor->nEventMask & ENM_SELCHANGE))
+ return;
+ sc.nmhdr.hwndFrom = editor->hWnd;
+ sc.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
+ sc.nmhdr.code = EN_SELCHANGE;
+ SendMessageW(editor->hWnd, EM_EXGETSEL, 0, (LPARAM)&sc.chrg);
+ sc.seltyp = SEL_EMPTY;
+ if (sc.chrg.cpMin != sc.chrg.cpMax)
+ sc.seltyp |= SEL_TEXT;
+ if (sc.chrg.cpMin < sc.chrg.cpMax+1) /* wth were RICHEDIT authors thinking ? */
+ sc.seltyp |= SEL_MULTICHAR;
+ SendMessageW(GetParent(editor->hWnd), WM_NOTIFY, sc.nmhdr.idFrom, (LPARAM)&sc);
+}
+
+BOOL ME_ArrowKey(ME_TextEditor *editor, int nVKey, int nCtrl)
+{
+ int nCursor = 0;
+ ME_Cursor *p = &editor->pCursors[nCursor];
+ ME_Cursor tmp_curs = *p;
+
+ switch(nVKey) {
+ case VK_UP:
+ ME_ArrowUp(editor, p);
+ ME_ClearTempStyle(editor);
+ ME_RepaintSelection(editor, &tmp_curs);
+ ME_SendSelChange(editor);
+ return TRUE;
+ case VK_DOWN:
+ ME_ArrowDown(editor, p);
+ ME_ClearTempStyle(editor);
+ ME_RepaintSelection(editor, &tmp_curs);
+ ME_SendSelChange(editor);
+ return TRUE;
+ }
+
+ editor->nUDArrowX = -1;
+ switch(nVKey) {
+ case VK_BACK: { /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
+ if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY)
+ return FALSE;
+ if (ME_IsSelection(editor))
+ {
+ editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */
+ ME_DeleteSelection(editor);
+ ME_UpdateRepaint(editor);
+ return TRUE;
+ }
+ if (ME_ArrowLeft(editor, p)) {
+ editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */
+ ME_MoveCaret(editor);
+ ME_DeleteTextAtCursor(editor, nCursor, 1);
+ ME_UpdateRepaint(editor);
+ }
+ return TRUE;
+ }
+ case VK_DELETE: {
+ if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY)
+ return FALSE;
+ /* editor->bCaretAtEnd = 0; FIXME or maybe not */
+ if (ME_IsSelection(editor))
+ {
+ ME_DeleteSelection(editor);
+ ME_UpdateRepaint(editor);
+ return TRUE;
+ }
+ ME_DeleteTextAtCursor(editor, nCursor, 1);
+ ME_UpdateRepaint(editor);
+ return TRUE;
+ }
+ case VK_HOME: {
+ if (GetKeyState(VK_CONTROL)<0)
+ ME_ArrowCtrlHome(editor, p);
+ else
+ ME_ArrowHome(editor, p);
+ editor->bCaretAtEnd = 0;
+ ME_ClearTempStyle(editor);
+ ME_RepaintSelection(editor, &tmp_curs);
+ ME_SendSelChange(editor);
+ return TRUE;
+ }
+ case VK_END:
+ if (GetKeyState(VK_CONTROL)<0)
+ ME_ArrowCtrlEnd(editor, p);
+ else
+ ME_ArrowEnd(editor, p);
+ ME_ClearTempStyle(editor);
+ ME_RepaintSelection(editor, &tmp_curs);
+ ME_SendSelChange(editor);
+ return TRUE;
+ case VK_LEFT:
+ editor->bCaretAtEnd = 0;
+ if (ME_CancelSelection(editor, -1))
+ return TRUE;
+ ME_ArrowLeft(editor, p);
+ ME_RepaintSelection(editor, &tmp_curs);
+ ME_ClearTempStyle(editor);
+ ME_SendSelChange(editor);
+ return TRUE;
+ case VK_RIGHT:
+ editor->bCaretAtEnd = 0;
+ if (ME_CancelSelection(editor, +1))
+ return TRUE;
+ ME_ArrowRight(editor, p);
+ ME_RepaintSelection(editor, &tmp_curs);
+ ME_ClearTempStyle(editor);
+ ME_SendSelChange(editor);
+ return TRUE;
+ }
+ return FALSE;
+}
--- /dev/null
+/*
+ * RichEdit - Operation context functions
+ *
+ * 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"
+
+void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC)
+{
+ c->nSequence = editor->nSequence++;
+ c->hDC = hDC;
+ c->editor = editor;
+ c->pt.x = 0;
+ c->pt.y = 0;
+ c->hbrMargin = CreateSolidBrush(RGB(224,224,224));
+ GetClientRect(editor->hWnd, &c->rcView);
+}
+
+void ME_DestroyContext(ME_Context *c)
+{
+ DeleteObject(c->hbrMargin);
+}
--- /dev/null
+/*
+ * RichEdit - functions dealing with editor object
+ *
+ * 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
+ */
+
+/*
+ API implementation status:
+
+ Messages (ANSI versions not done yet)
+ - EM_AUTOURLDETECT 2.0
+ - EM_CANPASTE
+ + EM_CANREDO 2.0
+ + EM_CANUNDO
+ - EM_CHARFROMPOS
+ - EM_DISPLAYBAND
+ + EM_EMPTYUNDOBUFFER
+ + EM_EXGETSEL
+ - EM_EXLIMITTEXT
+ - EM_EXLINEFROMCHAR
+ + EM_EXSETSEL
+ - EM_FINDTEXT
+ - EM_FINDTEXTEX
+ - EM_FINDWORDBREAK
+ - EM_FMTLINES
+ - EM_FORMATRANGE
+ - EM_GETCHARFORMAT (partly done)
+ + EM_GETEVENTMASK
+ - EM_GETFIRSTVISIBLELINE
+ - EM_GETIMECOLOR 1.0asian
+ - EM_GETIMECOMPMODE 2.0
+ - EM_GETIMEOPTIONS 1.0asian
+ - EM_GETIMESTATUS
+ - EM_GETLANGOPTIONS 2.0
+ - EM_GETLIMITTEXT
+ - EM_GETLINE
+ - EM_GETLINECOUNT returns number of rows, not of paragraphs
+ + EM_GETMODIFY
+ - EM_GETOLEINTERFACE
+ - EM_GETOPTIONS
+ + EM_GETPARAFORMAT
+ - EM_GETPUNCTUATION 1.0asian
+ - EM_GETRECT
+ - EM_GETREDONAME 2.0
+ + EM_GETSEL
+ + EM_GETSELTEXT (ANSI&Unicode)
+! - EM_GETTHUMB
+ - EM_GETTEXTMODE 2.0
+? + EM_GETTEXTRANGE (ANSI&Unicode)
+ - EM_GETUNDONAME
+ - EM_GETWORDBREAKPROC
+ - EM_GETWORDBREAKPROCEX
+ - EM_GETWORDWRAPMODE 1.0asian
+ - EM_HIDESELECTION
+ - EM_LIMITTEXT
+ - EM_LINEFROMCHAR
+ - EM_LINEINDEX
+ - EM_LINELENGTH
+ - EM_LINESCROLL
+ - EM_PASTESPECIAL
+ - EM_POSFROMCHARS
+ - EM_REDO 2.0
+ - EM_REQUESTRESIZE
+ + EM_REPLACESEL (proper style?) ANSI&Unicode
+ - EM_SCROLL
+ - EM_SCROLLCARET
+ - EM_SELECTIONTYPE
+ + EM_SETBKGNDCOLOR
+ - EM_SETCHARFORMAT (partly done, no ANSI)
+ + EM_SETEVENTMASK (few notifications supported)
+ - EM_SETIMECOLOR 1.0asian
+ - EM_SETIMEOPTIONS 1.0asian
+ - EM_SETLANGOPTIONS 2.0
+ - EM_SETLIMITTEXT
+ + EM_SETMODIFY (not sure if implementation is correct)
+ - EM_SETOLECALLBACK
+ - EM_SETOPTIONS
+ + EM_SETPARAFORMAT
+ - EM_SETPUNCTUATION 1.0asian
+ + EM_SETREADONLY no beep on modification attempt
+ - EM_SETRECT
+ - EM_SETRECTNP (EM_SETRECT without repainting) - not supported in RICHEDIT
+ + EM_SETSEL
+ - EM_SETTARGETDEVICE
+ - EM_SETTEXTMODE 2.0
+ - EM_SETUNDOLIMIT 2.0
+ - EM_SETWORDBREAKPROC
+ - EM_SETWORDBREAKPROCEX
+ - EM_SETWORDWRAPMODE 1.0asian
+ - EM_STOPGROUPTYPING 2.0
+ - EM_STREAMIN
+ - EM_STREAMOUT
+ - EM_UNDO
+ + WM_CHAR
+ + WM_CLEAR
+ - WM_COPY (lame implementation, no RTF support)
+ - WM_CUT (lame implementation, no RTF support)
+ + WM_GETDLGCODE (the current implementation is incomplete)
+ + WM_GETTEXT (ANSI&Unicode)
+ + WM_GETTEXTLENGTH (ANSI version sucks)
+ - WM_PASTE
+ - WM_SETFONT
+ + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
+ - WM_STYLECHANGING
+ - WM_STYLECHANGED (things like read-only flag)
+ - WM_UNICHAR
+
+ Notifications
+
+ * EN_CHANGE (sent from the wrong place)
+ - EN_CORRECTTEXT
+ - EN_DROPFILES
+ - EN_ERRSPACE
+ - EN_HSCROLL
+ - EN_IMECHANGE
+ + EN_KILLFOCUS
+ - EN_LINK
+ - EN_MAXTEXT
+ - EN_MSGFILTER
+ - EN_OLEOPFAILED
+ - EN_PROTECTED
+ - EN_REQUESTRESIZE
+ - EN_SAVECLIPBOARD
+ + EN_SELCHANGE
+ + EN_SETFOCUS
+ - EN_STOPNOUNDO
+ * EN_UPDATE (sent from the wrong place)
+ - EN_VSCROLL
+
+ Styles
+
+ - ES_AUTOHSCROLL
+ - ES_AUTOVSCROLL
+ - ES_CENTER
+ - ES_DISABLENOSCROLL (scrollbar is always visible)
+ - ES_EX_NOCALLOLEINIT
+ - ES_LEFT
+ - ES_MULTILINE (currently single line controls aren't supported)
+ - ES_NOIME
+ - ES_READONLY (I'm not sure if beeping is the proper behaviour)
+ - ES_RIGHT
+ - ES_SAVESEL
+ - ES_SELFIME
+ - ES_SUNKEN
+ - ES_VERTICAL
+ - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
+ - WS_SETFONT
+ - WS_HSCROLL
+ - WS_VSCROLL
+*/
+
+/*
+ * RICHED20 TODO (incomplete):
+ *
+ * - font caching
+ * - add remaining CHARFORMAT/PARAFORMAT fields
+ * - right/center align should strip spaces from the beginning
+ * - more advanced navigation (Ctrl-arrows, PageUp/PageDn)
+ * - tabs
+ * - pictures (not just smiling faces that lack API support ;-) )
+ * - OLE objects
+ * - calculate heights of pictures (half-done)
+ * - EM_STREAMIN/EM_STREAMOUT
+ * - horizontal scrolling (not even started)
+ * - fix scrollbars and refresh (it sucks bigtime)
+ * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
+ * - should remember maximum row width for wrap hysteresis
+ * - find/replace
+ * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
+ * - italic cursor with italic fonts
+ * - IME
+ * - most notifications aren't sent at all (the most important ones are)
+ * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
+ * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
+ * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
+ * - bugs in end-of-text handling (the gray bar) could get me in jail ;-)
+ * - determination of row size
+ * - end-of-paragraph marks should be of reasonable size
+ *
+ * Bugs that are probably fixed, but not so easy to verify:
+ * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
+ * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
+ * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
+ * - caret shouldn't be displayed when selection isn't empty
+ * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
+ * - undo for setting default format (done, might be buggy)
+ * - styles might be not released properly (looks like they work like charm, but who knows?
+ *
+ */
+
+#include "editor.h"
+#include <ole2.h>
+#include <richole.h>
+#include <winreg.h>
+#define NO_SHLWAPI_STREAM
+#include <shlwapi.h>
+
+#include "rtf.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(richedit);
+
+int me_debug = 0;
+HANDLE me_heap = NULL;
+
+ME_TextBuffer *ME_MakeText() {
+
+ ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
+
+ ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
+ ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
+
+ p1->prev = NULL;
+ p1->next = p2;
+ p2->prev = p1;
+ p2->next = NULL;
+ p1->member.para.next_para = p2;
+ p2->member.para.prev_para = p1;
+ p2->member.para.nCharOfs = 0;
+
+ buf->pFirst = p1;
+ buf->pLast = p2;
+ buf->pCharStyle = NULL;
+
+ return buf;
+}
+
+#define STREAMIN_BUFFER_SIZE 4096 /* M$ compatibility */
+
+static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream, ME_Style *style)
+{
+ BYTE buffer[STREAMIN_BUFFER_SIZE+1];
+ WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
+
+ TRACE("%08lx %p\n", dwFormat, stream);
+ stream->dwError = 0;
+
+ do {
+ long nDataSize = 0, nWideChars = 0;
+ stream->dwError = stream->pfnCallback(stream->dwCookie,
+ (dwFormat & SF_UNICODE ? (BYTE *)wszText : buffer),
+ STREAMIN_BUFFER_SIZE, &nDataSize);
+
+ if (stream->dwError)
+ break;
+ if (!nDataSize)
+ break;
+
+ if (!(dwFormat & SF_UNICODE))
+ {
+ /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
+ nWideChars = MultiByteToWideChar(CP_ACP, 0, buffer, nDataSize, wszText, STREAMIN_BUFFER_SIZE);
+ }
+ else
+ nWideChars = nDataSize>>1;
+ ME_InsertTextFromCursor(editor, 0, wszText, nWideChars, style);
+ if (nDataSize<STREAMIN_BUFFER_SIZE)
+ break;
+ } while(1);
+ ME_CommitUndo(editor);
+ return 0;
+}
+
+static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
+{
+ RTF_Info parser;
+ ME_Style *style;
+
+ TRACE("%p %p\n", stream, editor->hWnd);
+
+ if (format & SFF_SELECTION) {
+ style = ME_GetSelectionInsertStyle(editor);
+ SendMessageW(editor->hWnd, WM_CLEAR, 0, 0);
+ }
+ else {
+ style = editor->pBuffer->pDefaultStyle;
+ ME_AddRefStyle(style);
+ SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
+ SetWindowTextA(editor->hWnd, "");
+ ME_ClearTempStyle(editor);
+ }
+
+ if (format & SF_RTF) {
+ /* setup the RTF parser */
+ memset(&parser, 0, sizeof parser);
+ RTFSetEditStream(&parser, stream);
+ parser.rtfFormat = format&(SF_TEXT|SF_RTF);
+ parser.hwndEdit = editor->hWnd;
+ WriterInit(&parser);
+ RTFInit(&parser);
+ BeginFile(&parser);
+
+ /* do the parsing */
+ RTFRead(&parser);
+ RTFFlushOutputBuffer(&parser);
+ }
+ else if (format & SF_TEXT)
+ ME_StreamInText(editor, format, stream, style);
+ else
+ ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
+ /* put the cursor at the top */
+ if (!(format & SFF_SELECTION))
+ SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
+ else
+ {
+ /* FIXME where to put cursor now ? */
+ }
+ ME_ReleaseStyle(style);
+
+ return 0;
+}
+
+ME_TextEditor *ME_MakeEditor(HWND hWnd) {
+ ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
+ HDC hDC;
+ int i;
+ ed->hWnd = hWnd;
+ ed->pBuffer = ME_MakeText();
+ hDC = GetDC(hWnd);
+ ME_MakeFirstParagraph(hDC, ed->pBuffer);
+ ReleaseDC(hWnd, hDC);
+ ed->bCaretShown = FALSE;
+ ed->nCursors = 3;
+ ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
+ ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
+ ed->pCursors[0].nOffset = 0;
+ ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
+ ed->pCursors[1].nOffset = 0;
+ ed->nLastTotalLength = ed->nTotalLength = 0;
+ ed->nScrollPos = 0;
+ ed->nUDArrowX = -1;
+ ed->nSequence = 0;
+ ed->rgbBackColor = -1;
+ ed->bCaretAtEnd = FALSE;
+ ed->nEventMask = 0;
+ ed->nModifyStep = 0;
+ ed->pUndoStack = ed->pRedoStack = NULL;
+ ed->nUndoMode = umAddToUndo;
+ ed->nParagraphs = 1;
+ ed->nLastSelStart = ed->nLastSelEnd = 0;
+ for (i=0; i<HFONT_CACHE_SIZE; i++)
+ {
+ ed->pFontCache[i].nRefs = 0;
+ ed->pFontCache[i].nAge = 0;
+ ed->pFontCache[i].hFont = NULL;
+ }
+ ME_CheckCharOffsets(ed);
+ return ed;
+}
+
+void ME_DestroyEditor(ME_TextEditor *editor)
+{
+ ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
+ ME_DisplayItem *p = pFirst, *pNext = NULL;
+ int i;
+
+ ME_ClearTempStyle(editor);
+ ME_EmptyUndoStack(editor);
+ while(p) {
+ pNext = p->next;
+ ME_DestroyDisplayItem(p);
+ p = pNext;
+ }
+ ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
+ for (i=0; i<HFONT_CACHE_SIZE; i++)
+ {
+ if (editor->pFontCache[i].hFont)
+ DeleteObject(editor->pFontCache[i].hFont);
+ }
+
+ FREE_OBJ(editor);
+}
+
+#define UNSUPPORTED_MSG(e) \
+ case e: \
+ FIXME(#e ": stub\n"); \
+ return DefWindowProcW(hWnd, msg, wParam, lParam);
+
+/******************************************************************
+ * RichEditANSIWndProc (RICHED20.10)
+ */
+LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+ HDC hDC;
+ PAINTSTRUCT ps;
+ SCROLLINFO si;
+ ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0);
+ TRACE("msg %d %08x %08lx\n", msg, wParam, lParam);
+ switch(msg) {
+
+ UNSUPPORTED_MSG(EM_AUTOURLDETECT)
+ UNSUPPORTED_MSG(EM_CANPASTE)
+ UNSUPPORTED_MSG(EM_CHARFROMPOS)
+ UNSUPPORTED_MSG(EM_DISPLAYBAND)
+ UNSUPPORTED_MSG(EM_EXLIMITTEXT)
+ UNSUPPORTED_MSG(EM_EXLINEFROMCHAR)
+ UNSUPPORTED_MSG(EM_FINDTEXT)
+ UNSUPPORTED_MSG(EM_FINDTEXTEX)
+ UNSUPPORTED_MSG(EM_FINDWORDBREAK)
+ UNSUPPORTED_MSG(EM_FMTLINES)
+ UNSUPPORTED_MSG(EM_FORMATRANGE)
+ UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE)
+ UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
+ /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */
+ UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
+ UNSUPPORTED_MSG(EM_GETLIMITTEXT)
+ UNSUPPORTED_MSG(EM_GETLINE)
+ UNSUPPORTED_MSG(EM_GETLINECOUNT)
+ /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */
+ UNSUPPORTED_MSG(EM_GETOPTIONS)
+ UNSUPPORTED_MSG(EM_GETRECT)
+ UNSUPPORTED_MSG(EM_GETREDONAME)
+ UNSUPPORTED_MSG(EM_GETTEXTMODE)
+ UNSUPPORTED_MSG(EM_GETUNDONAME)
+ UNSUPPORTED_MSG(EM_GETWORDBREAKPROC)
+ UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
+ UNSUPPORTED_MSG(EM_HIDESELECTION)
+ UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */
+ UNSUPPORTED_MSG(EM_LINEFROMCHAR)
+ UNSUPPORTED_MSG(EM_LINEINDEX)
+ UNSUPPORTED_MSG(EM_LINELENGTH)
+ UNSUPPORTED_MSG(EM_LINESCROLL)
+ UNSUPPORTED_MSG(EM_PASTESPECIAL)
+/* UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */
+ UNSUPPORTED_MSG(EM_REQUESTRESIZE)
+ UNSUPPORTED_MSG(EM_SCROLL)
+ UNSUPPORTED_MSG(EM_SCROLLCARET)
+ UNSUPPORTED_MSG(EM_SELECTIONTYPE)
+ UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
+ UNSUPPORTED_MSG(EM_SETOLECALLBACK)
+ UNSUPPORTED_MSG(EM_SETOPTIONS)
+ UNSUPPORTED_MSG(EM_SETRECT)
+ UNSUPPORTED_MSG(EM_SETRECTNP)
+ UNSUPPORTED_MSG(EM_SETTARGETDEVICE)
+ UNSUPPORTED_MSG(EM_SETTEXTMODE)
+ UNSUPPORTED_MSG(EM_SETUNDOLIMIT)
+ UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
+ UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
+ UNSUPPORTED_MSG(EM_STREAMOUT)
+ UNSUPPORTED_MSG(WM_SETFONT)
+ UNSUPPORTED_MSG(WM_PASTE)
+ UNSUPPORTED_MSG(WM_STYLECHANGING)
+ UNSUPPORTED_MSG(WM_STYLECHANGED)
+/* UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
+
+/* Messages specific to Richedit controls */
+
+ case EM_STREAMIN:
+ return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
+ case WM_GETDLGCODE:
+ {
+ UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
+ if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN)
+ code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */
+ return code;
+ }
+ case WM_NCCREATE:
+ {
+ CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
+ editor = ME_MakeEditor(hWnd);
+ SetWindowLongW(hWnd, 0, (long)editor);
+ pcs = 0; /* ignore */
+ return TRUE;
+ }
+ case EM_EMPTYUNDOBUFFER:
+ ME_EmptyUndoStack(editor);
+ return 0;
+ case EM_GETSEL:
+ {
+ ME_GetSelection(editor, (int *)wParam, (int *)lParam);
+ if (!((wParam|lParam) & 0xFFFF0000))
+ return (lParam<<16)|wParam;
+ return -1;
+ }
+ case EM_EXGETSEL:
+ {
+ CHARRANGE *pRange = (CHARRANGE *)lParam;
+ ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax);
+ return 0;
+ }
+ case EM_CANUNDO:
+ return editor->pUndoStack != NULL;
+ case EM_CANREDO:
+ return editor->pRedoStack != NULL;
+ case EM_UNDO:
+ ME_Undo(editor);
+ return 0;
+ case EM_REDO:
+ ME_Redo(editor);
+ return 0;
+ case EM_SETSEL:
+ {
+ ME_SetSelection(editor, wParam, lParam);
+ ME_Repaint(editor);
+ ME_SendSelChange(editor);
+ return 0;
+ }
+ case EM_EXSETSEL:
+ {
+ CHARRANGE *pRange = (CHARRANGE *)lParam;
+ ME_SetSelection(editor, pRange->cpMin, pRange->cpMax);
+ /* FIXME optimize */
+ ME_Repaint(editor);
+ ME_SendSelChange(editor);
+ return 0;
+ }
+ case EM_SETBKGNDCOLOR:
+ {
+ LRESULT lColor = ME_GetBackColor(editor);
+ if (wParam)
+ editor->rgbBackColor = -1;
+ else
+ editor->rgbBackColor = lParam;
+ InvalidateRect(hWnd, NULL, TRUE);
+ UpdateWindow(hWnd);
+ return lColor;
+ }
+ case EM_GETMODIFY:
+ return editor->nModifyStep == 0 ? 0 : 1;
+ case EM_SETMODIFY:
+ {
+ if (wParam)
+ editor->nModifyStep = 0x80000000;
+ else
+ editor->nModifyStep = 0;
+
+ return 0;
+ }
+ case EM_SETREADONLY:
+ {
+ long nStyle = GetWindowLongW(hWnd, GWL_STYLE);
+ if (wParam)
+ nStyle |= ES_READONLY;
+ else
+ nStyle &= ~ES_READONLY;
+ SetWindowLongW(hWnd, GWL_STYLE, nStyle);
+ ME_Repaint(editor);
+ return 0;
+ }
+ case EM_SETEVENTMASK:
+ editor->nEventMask = lParam;
+ return 0;
+ case EM_GETEVENTMASK:
+ return editor->nEventMask;
+ case EM_SETCHARFORMAT:
+ {
+ CHARFORMAT2W buf, *p;
+ p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
+ if (!wParam)
+ ME_SetDefaultCharFormat(editor, p);
+ else if (wParam == (SCF_WORD | SCF_SELECTION))
+ FIXME("word selection not supported\n");
+ else if (wParam == SCF_ALL)
+ ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
+ else
+ ME_SetSelectionCharFormat(editor, p);
+ ME_CommitUndo(editor);
+ ME_UpdateRepaint(editor);
+ return 0;
+ }
+ case EM_GETCHARFORMAT:
+ {
+ CHARFORMAT2W tmp;
+ tmp.cbSize = sizeof(tmp);
+ if (!wParam)
+ ME_GetDefaultCharFormat(editor, &tmp);
+ else
+ ME_GetSelectionCharFormat(editor, &tmp);
+ ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp);
+ return 0;
+ }
+ case EM_SETPARAFORMAT:
+ ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
+ ME_CommitUndo(editor);
+ return 0;
+ case EM_GETPARAFORMAT:
+ ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
+ return 0;
+ case WM_CLEAR:
+ {
+ int from, to;
+ ME_GetSelection(editor, &from, &to);
+ ME_InternalDeleteText(editor, from, to-from);
+ ME_CommitUndo(editor);
+ ME_UpdateRepaint(editor);
+ return 0;
+ }
+ case EM_REPLACESEL:
+ {
+ int from, to;
+ ME_Style *style;
+ LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
+ size_t len = lstrlenW(wszText);
+ TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
+
+ ME_GetSelection(editor, &from, &to);
+ style = ME_GetSelectionInsertStyle(editor);
+ ME_InternalDeleteText(editor, from, to-from);
+ ME_InsertTextFromCursor(editor, 0, wszText, len, style);
+ ME_ReleaseStyle(style);
+ ME_EndToUnicode(hWnd, wszText);
+ /* drop temporary style if line end */
+ /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */
+ if (len>0 && wszText[len-1] == '\n')
+ ME_ClearTempStyle(editor);
+
+ ME_CommitUndo(editor);
+ if (!wParam)
+ ME_EmptyUndoStack(editor);
+ ME_UpdateRepaint(editor);
+ return 0;
+ }
+ case WM_SETTEXT:
+ {
+ LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam);
+ TRACE("WM_SETTEXT - %s\n", (char *)(wszText)); /* debugstr_w() */
+ ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
+ /* uses default style! */
+ ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle);
+ ME_EndToUnicode(hWnd, wszText);
+ ME_CommitUndo(editor);
+ ME_EmptyUndoStack(editor);
+ ME_UpdateRepaint(editor);
+ return 0;
+ }
+ case WM_CUT:
+ case WM_COPY:
+ {
+ int from, to, pars;
+ WCHAR *data;
+ HANDLE hData;
+
+ if (!OpenClipboard(hWnd))
+ return 0;
+
+ EmptyClipboard();
+ ME_GetSelection(editor, &from, &to);
+ pars = ME_CountParagraphsBetween(editor, from, to);
+ hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1));
+ data = (WCHAR *)GlobalLock(hData);
+ ME_GetTextW(editor, data, from, to-from, TRUE);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_UNICODETEXT, hData);
+ CloseClipboard();
+ if (msg == WM_CUT)
+ {
+ ME_InternalDeleteText(editor, from, to-from);
+ ME_CommitUndo(editor);
+ ME_UpdateRepaint(editor);
+ }
+ return 0;
+ }
+ case WM_GETTEXTLENGTH:
+ return ME_GetTextLength(editor);
+ case WM_GETTEXT:
+ {
+ TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
+ tr.chrg.cpMin = 0;
+ tr.chrg.cpMax = wParam-1;
+ tr.lpstrText = (WCHAR *)lParam;
+ return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ }
+ case EM_GETSELTEXT:
+ {
+ int from, to;
+ TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */
+ ME_GetSelection(editor, &from, &to);
+ tr.chrg.cpMin = from;
+ tr.chrg.cpMax = to;
+ tr.lpstrText = (WCHAR *)lParam;
+ return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ }
+ case EM_GETTEXTRANGE:
+ {
+ TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
+ if (IsWindowUnicode(hWnd))
+ return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
+ else
+ {
+ int nLen = rng->chrg.cpMax-rng->chrg.cpMin;
+ WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
+ int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE);
+ /* FIXME this is a potential security hole (buffer overrun)
+ if you know more about wchar->mbyte conversion please explain
+ */
+ WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL);
+ FREE_OBJ(p);
+ return nChars;
+ }
+ return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE);
+ }
+ case WM_CREATE:
+ ME_CommitUndo(editor);
+ ME_WrapMarkedParagraphs(editor);
+ ME_MoveCaret(editor);
+ return 0;
+ case WM_DESTROY:
+ ME_DestroyEditor(editor);
+ SetWindowLongW(hWnd, 0, 0);
+ return 0;
+ case WM_LBUTTONDOWN:
+ SetFocus(hWnd);
+ ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
+ SetCapture(hWnd);
+ break;
+ case WM_MOUSEMOVE:
+ if (GetCapture() == hWnd)
+ ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
+ break;
+ case WM_LBUTTONUP:
+ if (GetCapture() == hWnd)
+ ReleaseCapture();
+ break;
+ case WM_PAINT:
+ hDC = BeginPaint(hWnd, &ps);
+ ME_PaintContent(editor, hDC, FALSE, &ps.rcPaint);
+ EndPaint(hWnd, &ps);
+ break;
+ case WM_SETFOCUS:
+ ME_ShowCaret(editor);
+ ME_SendOldNotify(editor, EN_SETFOCUS);
+ return 0;
+ case WM_KILLFOCUS:
+ ME_HideCaret(editor);
+ ME_SendOldNotify(editor, EN_KILLFOCUS);
+ return 0;
+ case WM_ERASEBKGND:
+ {
+ HDC hDC = (HDC)wParam;
+ RECT rc;
+ COLORREF rgbBG = ME_GetBackColor(editor);
+ if (GetUpdateRect(hWnd,&rc,TRUE))
+ {
+ HBRUSH hbr = CreateSolidBrush(rgbBG);
+ FillRect(hDC, &rc, hbr);
+ DeleteObject(hbr);
+ }
+ return 1;
+ }
+ case WM_COMMAND:
+ TRACE("editor wnd command = %d\n", LOWORD(wParam));
+ return 0;
+ case WM_KEYDOWN:
+ if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) {
+ ME_CommitUndo(editor);
+ ME_EnsureVisible(editor, editor->pCursors[0].pRun);
+ HideCaret(hWnd);
+ ME_MoveCaret(editor);
+ ShowCaret(hWnd);
+ return 0;
+ }
+ if (GetKeyState(VK_CONTROL)<0)
+ {
+ if (LOWORD(wParam)=='W')
+ {
+ CHARFORMAT2W chf;
+ char buf[2048];
+ ME_GetSelectionCharFormat(editor, &chf);
+ ME_DumpStyleToBuf(&chf, buf);
+ MessageBoxA(NULL, buf, "Style dump", MB_OK);
+ }
+ if (LOWORD(wParam)=='Q')
+ {
+ ME_CheckCharOffsets(editor);
+ }
+ }
+ goto do_default;
+ case WM_CHAR:
+ {
+ WCHAR wstr;
+ if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) {
+ MessageBeep(MB_ICONERROR);
+ return 0; /* FIXME really 0 ? */
+ }
+ wstr = LOWORD(wParam);
+ if (((unsigned)wstr)>=' ' || wstr=='\r') {
+ /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
+ ME_Style *style = ME_GetInsertStyle(editor, 0);
+ ME_SaveTempStyle(editor);
+ ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
+ ME_ReleaseStyle(style);
+ ME_CommitUndo(editor);
+ ME_UpdateRepaint(editor);
+ }
+ return 0;
+ }
+ case WM_VSCROLL:
+ {
+ si.cbSize = sizeof(SCROLLINFO);
+ si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
+ GetScrollInfo(hWnd, SB_VERT, &si);
+ switch(LOWORD(wParam)) {
+ case SB_THUMBTRACK:
+ SetScrollPos(hWnd, SB_VERT, si.nTrackPos, FALSE);
+ ScrollWindow(hWnd, 0, si.nPos-si.nTrackPos, NULL, NULL);
+ /* InvalidateRect(hWnd, NULL, TRUE); */
+ UpdateWindow(hWnd);
+ break;
+ }
+ break;
+ }
+ case WM_SIZE:
+ {
+ ME_MarkAllForWrapping(editor);
+ ME_Repaint(editor);
+ return DefWindowProcW(hWnd, msg, wParam, lParam);
+ }
+ case EM_GETOLEINTERFACE:
+ {
+ LPVOID *ppvObj = (LPVOID*) lParam;
+ FIXME("EM_GETOLEINTERFACE %p: stub\n", ppvObj);
+ return CreateIRichEditOle(ppvObj);
+ }
+ default:
+ do_default:
+ return DefWindowProcW(hWnd, msg, wParam, lParam);
+ }
+ return 0L;
+}
+
+/******************************************************************
+ * RichEdit10ANSIWndProc (RICHED20.9)
+ */
+LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ /* FIXME: this is NOT the same as 2.0 version */
+ return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
+}
+
+void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
+{
+ HWND hWnd = editor->hWnd;
+ SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd);
+}
+
+int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to)
+{
+ ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
+ int i = 0;
+
+ while(item && item->member.para.next_para->member.para.nCharOfs <= from)
+ item = item->member.para.next_para;
+ if (!item)
+ return 0;
+ while(item && item->member.para.next_para->member.para.nCharOfs <= to) {
+ item = item->member.para.next_para;
+ i++;
+ }
+ return i;
+}
+
+int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF)
+{
+ ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
+ int nWritten = 0;
+
+ while(item && item->member.para.next_para->member.para.nCharOfs <= nStart)
+ item = ME_FindItemFwd(item, diParagraph);
+ if (!item) {
+ *buffer = L'\0';
+ return 0;
+ }
+ nStart -= item->member.para.nCharOfs;
+
+ do {
+ item = ME_FindItemFwd(item, diRun);
+ } while(item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nStart));
+ assert(item);
+
+ nStart -= item->member.run.nCharOfs;
+
+ if (nStart)
+ {
+ int nLen = ME_StrLen(item->member.run.strText) - nStart;
+ if (nLen > nChars)
+ nLen = nChars;
+ CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen);
+ nChars -= nLen;
+ nWritten += nLen;
+ if (!nChars)
+ return nWritten;
+ buffer += nLen;
+ nStart = 0;
+ item = ME_FindItemFwd(item, diRun);
+ }
+
+ while(nChars && item)
+ {
+ int nLen = ME_StrLen(item->member.run.strText);
+ if (nLen > nChars)
+ nLen = nChars;
+
+ if (item->member.run.nFlags & MERF_ENDPARA)
+ {
+ if (bCRLF) {
+ *buffer++ = '\r';
+ nWritten++;
+ }
+ *buffer = '\n';
+ assert(nLen == 1);
+ }
+ else
+ CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen);
+ nChars -= nLen;
+ nWritten += nLen;
+ buffer += nLen;
+
+ if (!nChars)
+ {
+ *buffer = L'\0';
+ return nWritten;
+ }
+ item = ME_FindItemFwd(item, diRun);
+ }
+ *buffer = L'\0';
+ return nWritten;
+}
+
+static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0};
+static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0};
+
+void ME_RegisterEditorClass(HINSTANCE hInstance)
+{
+ BOOL bResult;
+ WNDCLASSW wcW;
+ WNDCLASSA wcA;
+
+ wcW.style = CS_HREDRAW | CS_VREDRAW;
+ wcW.lpfnWndProc = RichEditANSIWndProc;
+ wcW.cbClsExtra = 0;
+ wcW.cbWndExtra = 4;
+ wcW.hInstance = NULL; /* hInstance would register DLL-local class */
+ wcW.hIcon = NULL;
+ wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
+ wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
+ wcW.lpszMenuName = NULL;
+ wcW.lpszClassName = wszClassName;
+ bResult = RegisterClassW(&wcW);
+ assert(bResult);
+ wcW.lpszClassName = wszClassName50;
+ bResult = RegisterClassW(&wcW);
+ assert(bResult);
+
+ wcA.style = CS_HREDRAW | CS_VREDRAW;
+ wcA.lpfnWndProc = RichEditANSIWndProc;
+ wcA.cbClsExtra = 0;
+ wcA.cbWndExtra = 4;
+ wcA.hInstance = NULL; /* hInstance would register DLL-local class */
+ wcA.hIcon = NULL;
+ wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
+ wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
+ wcA.lpszMenuName = NULL;
+ wcA.lpszClassName = "RichEdit20A";
+ bResult = RegisterClassA(&wcA);
+ assert(bResult);
+ wcA.lpszClassName = "RichEdit50A";
+ bResult = RegisterClassA(&wcA);
+ assert(bResult);
+}
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ TRACE("\n");
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hinstDLL);
+ me_heap = HeapCreate (0, 0x10000, 0);
+ ME_RegisterEditorClass(hinstDLL);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ UnregisterClassW(wszClassName, 0);
+ UnregisterClassW(wszClassName50, 0);
+ UnregisterClassA("RichEdit20A", 0);
+ UnregisterClassA("RichEdit50A", 0);
+ HeapDestroy (me_heap);
+ me_heap = NULL;
+ break;
+ }
+ return TRUE;
+}
+
+/******************************************************************
+ * CreateTextServices (RICHED20.4)
+ *
+ * FIXME should be ITextHost instead of void*
+ */
+HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost,
+ IUnknown **ppUnk)
+{
+ FIXME("stub\n");
+ /* FIXME should support aggregation */
+ if (punkOuter)
+ return CLASS_E_NOAGGREGATION;
+
+ return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */
+}
+
+/******************************************************************
+ * REExtendedRegisterClass (RICHED20.8)
+ *
+ * FIXME undocumented
+ */
+void WINAPI REExtendedRegisterClass(void)
+{
+ FIXME("stub\n");
+}
--- /dev/null
+/*
+ * RichEdit - prototypes for functions and macro definitions
+ *
+ * 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 "editstr.h"
+
+#define ALLOC_OBJ(type) (type *)HeapAlloc(me_heap, 0, sizeof(type))
+#define ALLOC_N_OBJ(type, count) (type *)HeapAlloc(me_heap, 0, count*sizeof(type))
+#define FREE_OBJ(ptr) HeapFree(me_heap, 0, ptr)
+
+/* style.c */
+ME_Style *ME_MakeStyle(CHARFORMAT2W *style);
+void ME_AddRefStyle(ME_Style *item);
+void ME_ReleaseStyle(ME_Style *item);
+ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor);
+ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style);
+HFONT ME_SelectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s);
+void ME_UnselectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s, HFONT hOldFont);
+void ME_InitCharFormat2W(CHARFORMAT2W *pFmt);
+void ME_SaveTempStyle(ME_TextEditor *editor);
+void ME_ClearTempStyle(ME_TextEditor *editor);
+void ME_DumpStyleToBuf(CHARFORMAT2W *pFmt, char buf[2048]);
+void ME_DumpStyle(ME_Style *s);
+CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from);
+void ME_CopyToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from);
+CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from);
+void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from);
+void ME_CopyCharFormat(CHARFORMAT2W *pDest, CHARFORMAT2W *pSrc); /* only works with 2W structs */
+
+/* list.c */
+void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat);
+void ME_Remove(ME_DisplayItem *diWhere);
+ME_DisplayItem *ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass);
+ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass);
+ME_DisplayItem *ME_FindItemBackOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass);
+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_DumpDocument(ME_TextBuffer *buffer);
+const char *ME_GetDITypeName(ME_DIType type);
+
+/* string.c */
+int ME_GetOptimalBuffer(int nLen);
+ME_String *ME_MakeString(LPCWSTR szText);
+ME_String *ME_MakeStringN(LPCWSTR szText, int nMaxChars);
+ME_String *ME_StrDup(ME_String *s);
+void ME_DestroyString(ME_String *s);
+void ME_AppendString(ME_String *s1, ME_String *s2);
+ME_String *ME_ConcatString(ME_String *s1, ME_String *s2);
+ME_String *ME_VSplitString(ME_String *orig, int nVPos);
+int ME_IsWhitespaces(ME_String *s);
+int ME_IsSplitable(ME_String *s);
+/* int ME_CalcSkipChars(ME_String *s); */
+int ME_StrLen(ME_String *s);
+int ME_StrVLen(ME_String *s);
+int ME_FindNonWhitespaceV(ME_String *s, int nVChar);
+int ME_FindWhitespaceV(ME_String *s, int nVChar);
+int ME_GetCharFwd(ME_String *s, int nPos); /* get char starting from start */
+int ME_GetCharBack(ME_String *s, int nPos); /* get char starting from \0 */
+int ME_StrRelPos(ME_String *s, int nVChar, int *pRelChars);
+int ME_StrRelPos2(ME_String *s, int nVChar, int nRelChars);
+int ME_VPosToPos(ME_String *s, int nVPos);
+int ME_PosToVPos(ME_String *s, int nPos);
+void ME_StrDeleteV(ME_String *s, int nVChar, int nChars);
+/* smart helpers for A<->W conversions, they reserve/free memory and call MultiByte<->WideChar functions */
+LPWSTR ME_ToUnicode(HWND hWnd, LPVOID psz);
+void ME_EndToUnicode(HWND hWnd, LPVOID psz);
+LPSTR ME_ToAnsi(HWND hWnd, LPVOID psz);
+void ME_EndToAnsi(HWND hWnd, LPVOID psz);
+
+
+/* note: those two really return the first matching offset (starting from EOS)+1
+ * in other words, an offset of the first trailing white/black */
+int ME_ReverseFindNonWhitespaceV(ME_String *s, int nVChar);
+int ME_ReverseFindWhitespaceV(ME_String *s, int nVChar);
+
+/* row.c */
+ME_DisplayItem *ME_FindRowStart(ME_Context *c, ME_DisplayItem *run, int nRelPos);
+ME_DisplayItem *ME_RowStart(ME_DisplayItem *item);
+ME_DisplayItem *ME_RowEnd(ME_DisplayItem *item);
+void ME_RenumberParagraphs(ME_DisplayItem *item); /* TODO */
+
+/* run.c */
+ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags);
+/* note: ME_InsertRun inserts a copy of the specified run - so you need to destroy the original */
+ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem *pItem);
+void ME_CheckCharOffsets(ME_TextEditor *editor);
+void ME_PropagateCharOffset(ME_DisplayItem *p, int shift);
+void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize);
+int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run);
+/* this one accounts for 1/2 char tolerance */
+int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run);
+int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset);
+int ME_GetLastSplittablePlace(ME_Context *c, ME_Run *run);
+int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2);
+void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p);
+ME_DisplayItem *ME_SplitRun(ME_Context *c, ME_DisplayItem *item, int nChar);
+ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nChar);
+int ME_FindSplitPoint(ME_Context *c, POINT *pt, ME_Run *run, int desperate);
+void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run);
+ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *run);
+void ME_CalcRunExtent(ME_Context *c, ME_Run *run);
+SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen);
+void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor);
+void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs);
+int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs);
+void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift);
+void ME_SetCharFormat(ME_TextEditor *editor, int nFrom, int nLen, CHARFORMAT2W *pFmt);
+void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt);
+void ME_GetCharFormat(ME_TextEditor *editor, int nFrom, int nLen, CHARFORMAT2W *pFmt);
+void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt);
+void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt);
+void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod);
+
+/* caret.c */
+void ME_SetSelection(ME_TextEditor *editor, int from, int to);
+void ME_HideCaret(ME_TextEditor *ed);
+void ME_ShowCaret(ME_TextEditor *ed);
+void ME_MoveCaret(ME_TextEditor *ed);
+int ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol);
+void ME_LButtonDown(ME_TextEditor *editor, int x, int y);
+void ME_MouseMove(ME_TextEditor *editor, int x, int y);
+void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars);
+void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor,
+ const WCHAR *str, int len, ME_Style *style);
+void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W *pFmt);
+BOOL ME_ArrowKey(ME_TextEditor *ed, int nVKey, int nCtrl);
+
+void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC);
+void ME_DestroyContext(ME_Context *c);
+ME_Style *GetInsertStyle(ME_TextEditor *editor, int nCursor);
+void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para);
+int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor);
+void ME_GetSelection(ME_TextEditor *editor, int *from, int *to);
+int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to);
+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_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars);
+int ME_GetTextLength(ME_TextEditor *editor);
+ME_Style *ME_GetSelectionInsertStyle(ME_TextEditor *editor);
+
+/* wrap.c */
+void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp);
+ME_DisplayItem *ME_MakeRow(int height, int baseline, int width);
+void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd);
+void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp);
+void ME_WrapMarkedParagraphs(ME_TextEditor *editor);
+
+/* para.c */
+ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *run);
+void ME_MakeFirstParagraph(HDC hDC, ME_TextBuffer *editor);
+ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *rp, ME_Style *style);
+ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp);
+void ME_DumpParaStyle(ME_Paragraph *s);
+void ME_DumpParaStyleToBuf(PARAFORMAT2 *pFmt, char buf[2048]);
+void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt);
+void ME_SetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt);
+void ME_GetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt);
+void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt);
+/* marks from first up to (but not including) last */
+void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last);
+void ME_MarkAllForWrapping(ME_TextEditor *editor);
+
+/* paint.c */
+void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, RECT *rcUpdate);
+void ME_Repaint(ME_TextEditor *editor);
+void ME_UpdateRepaint(ME_TextEditor *editor);
+void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph);
+void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos);
+int ME_GetScrollPos(ME_TextEditor *editor);
+void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun);
+COLORREF ME_GetBackColor(ME_TextEditor *editor);
+
+/* richole.c */
+extern LRESULT CreateIRichEditOle(LPVOID *);
+
+/* wintest.c */
+
+/* editor.c */
+void ME_RegisterEditorClass();
+ME_TextEditor *ME_MakeEditor(HWND hWnd);
+void ME_DestroyEditor(ME_TextEditor *editor);
+void ME_SendOldNotify(ME_TextEditor *editor, int nCode);
+ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *di);
+void ME_CommitUndo(ME_TextEditor *editor);
+void ME_Undo(ME_TextEditor *editor);
+void ME_Redo(ME_TextEditor *editor);
+void ME_EmptyUndoStack(ME_TextEditor *editor);
+int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, BOOL bCRLF);
+
+extern int me_debug;
+extern HANDLE me_heap;
+extern void DoWrap(ME_TextEditor *editor);
--- /dev/null
+/*
+ * RichEdit - structures and constant
+ *
+ * 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
+ */
+
+#ifndef __EDITSTR_H
+#define __EDITSTR_H
+
+#ifndef _WIN32_IE
+#define _WIN32_IE 0x0400
+#endif
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winnls.h>
+#include <winnt.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <richedit.h>
+#include <commctrl.h>
+
+#include "wine/debug.h"
+
+typedef struct tagME_String
+{
+ WCHAR *szData;
+ int nLen, nBuffer;
+} ME_String;
+
+typedef struct tagME_Style
+{
+ CHARFORMAT2W fmt;
+
+ HFONT hFont; /* cached font for the style */
+ TEXTMETRICW tm; /* cached font metrics for the style */
+ int nRefs; /* reference count */
+ int nSequence; /* incremented when cache needs to be rebuilt, ie. every screen redraw */
+} ME_Style;
+
+typedef enum {
+ diTextStart, /* start of the text buffer */
+ diParagraph, /* paragraph start */
+ diRun, /* run (sequence of chars with the same character format) */
+ diStartRow, /* start of the row (line of text on the screen) */
+ diTextEnd, /* end of the text buffer */
+
+ /********************* these below are meant for finding only *********************/
+ diStartRowOrParagraph, /* 5 */
+ diStartRowOrParagraphOrEnd,
+ diRunOrParagraph,
+ diRunOrStartRow,
+ diParagraphOrEnd,
+ diRunOrParagraphOrEnd, /* 10 */
+
+ diUndoInsertRun, /* 11 */
+ diUndoDeleteRun, /* 12 */
+ diUndoJoinParagraphs, /* 13 */
+ diUndoSplitParagraph, /* 14 */
+ diUndoSetParagraphFormat, /* 15 */
+ diUndoSetCharFormat, /* 16 */
+ diUndoEndTransaction, /* 17 */
+ diUndoSetDefaultCharFormat, /* 18 */
+} ME_DIType;
+
+/******************************** run flags *************************/
+#define MERF_STYLEFLAGS 0x0FFF
+/* run contains non-text content, which has its own rules for wrapping, sizing etc */
+#define MERF_GRAPHICS 1
+
+/* run is splittable (contains white spaces in the middle or end) */
+#define MERF_SPLITTABLE 0x001000
+/* run starts with whitespaces */
+#define MERF_STARTWHITE 0x002000
+/* run ends with whitespaces */
+#define MERF_ENDWHITE 0x004000
+/* run is completely made of whitespaces */
+#define MERF_WHITESPACE 0x008000
+/* run is a last (dummy) run in the paragraph */
+#define MERF_SKIPPED 0x010000
+/* flags that are calculated during text wrapping */
+#define MERF_CALCBYWRAP 0x0F0000
+/* the "end of paragraph" run, contains 1 character */
+#define MERF_ENDPARA 0x100000
+
+/* those flags are kept when the row is split */
+#define MERF_SPLITMASK (~(0))
+
+/******************************** para flags *************************/
+
+/* this paragraph was already wrapped and hasn't changed, every change resets that flag */
+#define MEPF_REWRAP 1
+#define MEPF_REPAINT 2
+
+/******************************** structures *************************/
+
+struct tagME_DisplayItem;
+
+typedef struct tagME_Run
+{
+ ME_String *strText;
+ ME_Style *style;
+ int nCharOfs; /* relative to para's offset */
+ int nWidth; /* width of full run, width of leading&trailing ws */
+ int nFlags;
+ int nAscent, nDescent; /* pixels above/below baseline */
+ POINT pt; /* relative to para's position */
+} ME_Run;
+
+typedef struct tagME_Document {
+ struct tagME_DisplayItem *def_char_style;
+ struct tagME_DisplayItem *def_para_style;
+ int last_wrapped_line;
+} ME_Document;
+
+typedef struct tagME_Paragraph
+{
+ PARAFORMAT2 *pFmt;
+ int nLeftMargin, nRightMargin, nFirstMargin;
+ int nCharOfs;
+ int nFlags;
+ int nYPos, nHeight;
+ int nLastPaintYPos, nLastPaintHeight;
+ struct tagME_DisplayItem *prev_para, *next_para, *document;
+} ME_Paragraph;
+
+typedef struct tagME_Row
+{
+ int nHeight;
+ int nBaseline;
+ int nWidth;
+ int nLMargin;
+ int nRMargin;
+ int nYPos;
+} ME_Row;
+
+typedef struct tagME_DisplayItem
+{
+ ME_DIType type;
+ struct tagME_DisplayItem *prev, *next;
+ union {
+ ME_Run run;
+ ME_Row row;
+ ME_Paragraph para;
+ ME_Document doc; /* not used */
+ ME_Style *ustyle; /* used by diUndoSetCharFormat */
+ } member;
+} ME_DisplayItem;
+
+typedef struct tagME_UndoItem
+{
+ ME_DisplayItem di;
+ int nStart, nLen;
+} ME_UndoItem;
+
+typedef struct tagME_TextBuffer
+{
+ ME_DisplayItem *pFirst, *pLast;
+ ME_Style *pCharStyle;
+ ME_Style *pDefaultStyle;
+} ME_TextBuffer;
+
+typedef struct tagME_Cursor
+{
+ ME_DisplayItem *pRun;
+ int nOffset;
+} ME_Cursor;
+
+typedef enum {
+ umAddToUndo,
+ umAddToRedo,
+ umIgnore,
+ umAddBackToUndo
+} ME_UndoMode;
+
+typedef struct tagME_FontCacheItem
+{
+ LOGFONTW lfSpecs;
+ HFONT hFont;
+ int nRefs;
+ int nAge;
+} ME_FontCacheItem;
+
+#define HFONT_CACHE_SIZE 10
+
+typedef struct tagME_TextEditor
+{
+ HWND hWnd;
+ BOOL bCaretShown;
+ ME_TextBuffer *pBuffer;
+ ME_Cursor *pCursors;
+ int nCursors;
+ SIZE sizeWindow;
+ int nScrollPos;
+ int nTotalLength, nLastTotalLength;
+ int nUDArrowX;
+ int nSequence;
+ int nOldSelFrom, nOldSelTo;
+ COLORREF rgbBackColor;
+ BOOL bCaretAtEnd;
+ int nEventMask;
+ int nModifyStep;
+ ME_DisplayItem *pUndoStack, *pRedoStack;
+ ME_UndoMode nUndoMode;
+ int nParagraphs;
+ int nLastSelStart, nLastSelEnd;
+ ME_FontCacheItem pFontCache[HFONT_CACHE_SIZE];
+} ME_TextEditor;
+
+typedef struct tagME_Context
+{
+ HDC hDC;
+ POINT pt;
+ POINT ptRowOffset;
+ RECT rcView;
+ HBRUSH hbrMargin;
+
+ /* those are valid inside ME_WrapTextParagraph and related */
+ POINT ptFirstRun;
+ ME_TextEditor *editor;
+ int nSequence;
+} ME_Context;
+
+typedef struct tagME_WrapContext
+{
+ ME_Style *style;
+ ME_Context *context;
+ int nLeftMargin, nRightMargin, nFirstMargin;
+ int nTotalWidth, nAvailWidth;
+ int nRow;
+ POINT pt;
+ BOOL bOverflown;
+ ME_DisplayItem *pRowStart;
+
+ ME_DisplayItem *pLastSplittableRun;
+ POINT ptLastSplittableRun;
+} ME_WrapContext;
+
+#endif
--- /dev/null
+/*
+ * RichEdit - Basic operations on double linked lists.
+ *
+ * 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(riched20);
+
+void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat)
+{
+ diWhat->next = diWhere;
+ diWhat->prev = diWhere->prev;
+
+ diWhere->prev->next = diWhat;
+ diWhat->next->prev = diWhat;
+}
+
+void ME_Remove(ME_DisplayItem *diWhere)
+{
+ ME_DisplayItem *diNext = diWhere->next;
+ ME_DisplayItem *diPrev = diWhere->prev;
+ assert(diNext);
+ assert(diPrev);
+ diPrev->next = diNext;
+ diNext->prev = diPrev;
+}
+
+ME_DisplayItem *ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass)
+{
+ if (!di)
+ return NULL;
+ di = di->prev;
+ while(di!=NULL) {
+ if (ME_DITypesEqual(di->type, nTypeOrClass))
+ return di;
+ di = di->prev;
+ }
+ return NULL;
+}
+
+ME_DisplayItem *ME_FindItemBackOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass)
+{
+ while(di!=NULL) {
+ if (ME_DITypesEqual(di->type, nTypeOrClass))
+ return di;
+ di = di->prev;
+ }
+ return NULL;
+}
+
+ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass)
+{
+ if (!di) return NULL;
+ di = di->next;
+ while(di!=NULL) {
+ if (ME_DITypesEqual(di->type, nTypeOrClass))
+ return di;
+ di = di->next;
+ }
+ return NULL;
+}
+
+ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass)
+{
+ while(di!=NULL) {
+ if (ME_DITypesEqual(di->type, nTypeOrClass))
+ return di;
+ di = di->next;
+ }
+ return NULL;
+}
+
+BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass)
+{
+ if (type==nTypeOrClass)
+ return TRUE;
+ if (nTypeOrClass==diRunOrParagraph && (type==diRun || type==diParagraph))
+ return TRUE;
+ if (nTypeOrClass==diRunOrStartRow && (type==diRun || type==diStartRow))
+ return TRUE;
+ if (nTypeOrClass==diParagraphOrEnd && (type==diTextEnd || type==diParagraph))
+ return TRUE;
+ if (nTypeOrClass==diStartRowOrParagraph && (type==diStartRow || type==diParagraph))
+ return TRUE;
+ if (nTypeOrClass==diStartRowOrParagraphOrEnd
+ && (type==diStartRow || type==diParagraph || type==diTextEnd))
+ return TRUE;
+ if (nTypeOrClass==diRunOrParagraphOrEnd
+ && (type==diRun || type==diParagraph || type==diTextEnd))
+ return TRUE;
+ return FALSE;
+}
+
+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);
+ }
+ if (item->type==diRun || item->type == diUndoInsertRun) {
+ ME_ReleaseStyle(item->member.run.style);
+ ME_DestroyString(item->member.run.strText);
+ }
+ if (item->type==diUndoSetCharFormat || item->type==diUndoSetDefaultCharFormat) {
+ ME_ReleaseStyle(item->member.ustyle);
+ }
+ FREE_OBJ(item);
+}
+
+ME_DisplayItem *ME_MakeDI(ME_DIType type) {
+ ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem);
+ ZeroMemory(item, sizeof(ME_DisplayItem));
+ item->type = type;
+ item->prev = item->next = NULL;
+ if (type == diParagraph || type == diUndoSplitParagraph) {
+ item->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
+ item->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
+ item->member.para.pFmt->dwMask = 0;
+ item->member.para.nFlags = MEPF_REWRAP;
+ }
+
+ return item;
+}
+
+const char *ME_GetDITypeName(ME_DIType type)
+{
+ switch(type)
+ {
+ case diParagraph: return "diParagraph";
+ case diRun: return "diRun";
+ case diTextStart: return "diTextStart";
+ case diTextEnd: return "diTextEnd";
+ case diStartRow: return "diStartRow";
+ case diUndoEndTransaction: return "diUndoEndTransaction";
+ case diUndoSetParagraphFormat: return "diUndoSetParagraphFormat";
+ case diUndoSetCharFormat: return "diUndoSetCharFormat";
+ case diUndoInsertRun: return "diUndoInsertRun";
+ case diUndoDeleteRun: return "diUndoDeleteRun";
+ case diUndoJoinParagraphs: return "diJoinParagraphs";
+ case diUndoSplitParagraph: return "diSplitParagraph";
+ case diUndoSetDefaultCharFormat: return "diUndoSetDefaultCharFormat";
+ default: return "?";
+ }
+}
+
+void ME_DumpDocument(ME_TextBuffer *buffer)
+{
+ /* FIXME this is useless, */
+ ME_DisplayItem *pItem = buffer->pFirst;
+ TRACE("DOCUMENT DUMP START\n");
+ while(pItem) {
+ switch(pItem->type)
+ {
+ case diTextStart:
+ TRACE("Start");
+ break;
+ case diParagraph:
+ TRACE("\nParagraph(ofs=%d)", pItem->member.para.nCharOfs);
+ break;
+ case diStartRow:
+ TRACE(" - StartRow");
+ break;
+ case diRun:
+ TRACE(" - Run(\"%s\", %d)", debugstr_w(pItem->member.run.strText->szData),
+ pItem->member.run.nCharOfs);
+ break;
+ case diTextEnd:
+ TRACE("\nEnd\n");
+ break;
+ default:
+ break;
+ }
+ pItem = pItem->next;
+ }
+ TRACE("DOCUMENT DUMP END\n");
+}
--- /dev/null
+# $Id: makefile 12852 2005-01-06 13:58:04Z mf $\r
+\r
+PATH_TO_TOP = ../..\r
+\r
+TARGET_TYPE = winedll\r
+\r
+include $(PATH_TO_TOP)/rules.mak\r
+\r
+include $(TOOLS_PATH)/helper.mk\r
--- /dev/null
+/*
+ * RichEdit - painting functions
+ *
+ * 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);
+
+void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew, RECT *rcUpdate) {
+ ME_DisplayItem *item;
+ ME_Context c;
+ int yoffset;
+
+ editor->nSequence++;
+ yoffset = GetScrollPos(editor->hWnd, SB_VERT);
+ ME_InitContext(&c, editor, hDC);
+ SetBkMode(hDC, TRANSPARENT);
+ ME_MoveCaret(editor);
+ item = editor->pBuffer->pFirst->next;
+ c.pt.y -= yoffset;
+ while(item != editor->pBuffer->pLast) {
+ assert(item->type == diParagraph);
+ if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT))
+ {
+ BOOL bPaint = (rcUpdate == NULL);
+ if (rcUpdate)
+ bPaint = c.pt.y<rcUpdate->bottom &&
+ c.pt.y+item->member.para.nHeight>rcUpdate->top;
+ if (bPaint)
+ {
+ ME_DrawParagraph(&c, item);
+ item->member.para.nFlags &= ~MEPF_REPAINT;
+ }
+ }
+ c.pt.y += item->member.para.nHeight;
+ item = item->member.para.next_para;
+ }
+ if (c.pt.y<c.rcView.bottom) {
+ RECT rc;
+ int xs = c.rcView.left, xe = c.rcView.right;
+ int ys = c.pt.y, ye = c.rcView.bottom;
+
+ if (bOnlyNew)
+ {
+ int y1 = editor->nTotalLength-yoffset, y2 = editor->nLastTotalLength-yoffset;
+ if (y1<y2)
+ ys = y1, ye = y2+1;
+ else
+ ys = ye;
+ }
+
+ if (rcUpdate && ys!=ye)
+ {
+ xs = rcUpdate->left, xe = rcUpdate->right;
+ if (rcUpdate->top > ys)
+ ys = rcUpdate->top;
+ if (rcUpdate->bottom < ye)
+ ye = rcUpdate->bottom;
+ }
+
+ rc.left = xs; /* FIXME remove if it's not necessary anymore */
+ rc.top = c.pt.y;
+ rc.right = xe;
+ rc.bottom = c.pt.y+1;
+ FillRect(hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
+
+ if (ys == c.pt.y) /* don't overwrite the top bar */
+ ys++;
+ if (ye>ys) {
+ rc.left = xs;
+ rc.top = ys;
+ rc.right = xe;
+ rc.bottom = ye;
+ /* this is not supposed to be gray, I know, but lets keep it gray for now for debugging purposes */
+ FillRect(hDC, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
+ }
+ }
+ editor->nLastTotalLength = editor->nTotalLength;
+ ME_DestroyContext(&c);
+}
+
+void ME_MarkParagraphRange(ME_TextEditor *editor, ME_DisplayItem *p1,
+ ME_DisplayItem *p2, int nFlags)
+{
+ ME_DisplayItem *p3;
+ if (p1 == p2)
+ {
+ p1->member.para.nFlags |= nFlags;
+ return;
+ }
+ if (p1->member.para.nCharOfs > p2->member.para.nCharOfs)
+ p3 = p1, p1 = p2, p2 = p3;
+
+ p1->member.para.nFlags |= nFlags;
+ do {
+ p1 = p1->member.para.next_para;
+ p1->member.para.nFlags |= nFlags;
+ } while (p1 != p2);
+}
+
+void ME_MarkOffsetRange(ME_TextEditor *editor, int from, int to, int nFlags)
+{
+ ME_Cursor c1, c2;
+ ME_CursorFromCharOfs(editor, from, &c1);
+ ME_CursorFromCharOfs(editor, to, &c2);
+
+ ME_MarkParagraphRange(editor, ME_GetParagraph(c1.pRun), ME_GetParagraph(c2.pRun), nFlags);
+}
+
+void ME_MarkSelectionForRepaint(ME_TextEditor *editor)
+{
+ int from, to, from2, to2, end;
+
+ end = ME_GetTextLength(editor);
+ ME_GetSelection(editor, &from, &to);
+ from2 = editor->nLastSelStart;
+ to2 = editor->nLastSelEnd;
+ if (from<from2) ME_MarkOffsetRange(editor, from, from2, MEPF_REPAINT);
+ if (from>from2) ME_MarkOffsetRange(editor, from2, from, MEPF_REPAINT);
+ if (to<to2) ME_MarkOffsetRange(editor, to, to2, MEPF_REPAINT);
+ if (to>to2) ME_MarkOffsetRange(editor, to2, to, MEPF_REPAINT);
+
+ editor->nLastSelStart = from;
+ editor->nLastSelEnd = to;
+}
+
+void ME_Repaint(ME_TextEditor *editor)
+{
+ ME_Cursor *pCursor = &editor->pCursors[0];
+ ME_DisplayItem *pRun = NULL;
+ int nOffset = -1;
+ HDC hDC;
+
+ int nCharOfs = ME_CharOfsFromRunOfs(editor, pCursor->pRun, pCursor->nOffset);
+ ME_RunOfsFromCharOfs(editor, nCharOfs, &pRun, &nOffset);
+ assert(pRun == pCursor->pRun);
+ assert(nOffset == pCursor->nOffset);
+ ME_MarkSelectionForRepaint(editor);
+ ME_WrapMarkedParagraphs(editor);
+ hDC = GetDC(editor->hWnd);
+ ME_HideCaret(editor);
+ ME_PaintContent(editor, hDC, TRUE, NULL);
+ ReleaseDC(editor->hWnd, hDC);
+ ME_ShowCaret(editor);
+}
+
+void ME_UpdateRepaint(ME_TextEditor *editor)
+{
+/*
+ InvalidateRect(editor->hWnd, NULL, TRUE);
+ */
+ ME_SendOldNotify(editor, EN_CHANGE);
+ ME_Repaint(editor);
+ ME_SendOldNotify(editor, EN_UPDATE);
+ ME_SendSelChange(editor);
+}
+
+void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, int nChars,
+ ME_Style *s, int *width, int nSelFrom, int nSelTo, int ymin, int cy) {
+ HDC hDC = c->hDC;
+ HGDIOBJ hOldFont;
+ COLORREF rgbOld, rgbBack;
+ hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
+ rgbBack = ME_GetBackColor(c->editor);
+ if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR))
+ rgbOld = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
+ else
+ rgbOld = SetTextColor(hDC, s->fmt.crTextColor);
+ ExtTextOutW(hDC, x, y, 0, NULL, szText, nChars, NULL);
+ if (width) {
+ SIZE sz;
+ GetTextExtentPoint32W(hDC, szText, nChars, &sz);
+ *width = sz.cx;
+ }
+ if (nSelFrom < nChars && nSelTo >= 0 && nSelFrom<nSelTo)
+ {
+ SIZE sz;
+ if (nSelFrom < 0) nSelFrom = 0;
+ if (nSelTo > nChars) nSelTo = nChars;
+ GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz);
+ x += sz.cx;
+ GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz);
+ PatBlt(hDC, x, ymin, sz.cx, cy, DSTINVERT);
+ }
+ SetTextColor(hDC, rgbOld);
+ ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
+}
+
+void ME_DebugWrite(HDC hDC, POINT *pt, WCHAR *szText) {
+ int align = SetTextAlign(hDC, TA_LEFT|TA_TOP);
+ HGDIOBJ hFont = SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
+ COLORREF color = SetTextColor(hDC, RGB(128,128,128));
+ TextOutW(hDC, pt->x, pt->y, szText, lstrlenW(szText));
+ SelectObject(hDC, hFont);
+ SetTextAlign(hDC, align);
+ SetTextColor(hDC, color);
+}
+
+void ME_DrawGraphics(ME_Context *c, int x, int y, ME_Run *run,
+ ME_Paragraph *para, BOOL selected) {
+ SIZE sz;
+ int xs, ys, xe, ye, h, ym, width, eyes;
+ ME_GetGraphicsSize(c->editor, run, &sz);
+ xs = run->pt.x;
+ ys = y-sz.cy;
+ xe = xs+sz.cx;
+ ye = y;
+ h = ye-ys;
+ ym = ys+h/4;
+ width = sz.cx;
+ eyes = width/8;
+ /* draw a smiling face :) */
+ Ellipse(c->hDC, xs, ys, xe, ye);
+ Ellipse(c->hDC, xs+width/8, ym, x+width/8+eyes, ym+eyes);
+ Ellipse(c->hDC, xs+7*width/8-eyes, ym, xs+7*width/8, ym+eyes);
+ MoveToEx(c->hDC, xs+width/8, ys+3*h/4-eyes, NULL);
+ LineTo(c->hDC, xs+width/8, ys+3*h/4);
+ LineTo(c->hDC, xs+7*width/8, ys+3*h/4);
+ LineTo(c->hDC, xs+7*width/8, ys+3*h/4-eyes);
+ if (selected)
+ {
+ /* descent is usually (always?) 0 for graphics */
+ PatBlt(c->hDC, x, y-run->nAscent, sz.cx, run->nAscent+run->nDescent, DSTINVERT);
+ }
+}
+
+void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Paragraph *para) {
+ ME_Run *run = &rundi->member.run;
+ int runofs = run->nCharOfs+para->nCharOfs;
+
+ if (run->nFlags & MERF_GRAPHICS) {
+ int blfrom, blto;
+ ME_GetSelection(c->editor, &blfrom, &blto);
+ ME_DrawGraphics(c, x, y, run, para, (runofs >= blfrom) && (runofs < blto));
+ } else
+ {
+ int blfrom, blto;
+ ME_DisplayItem *start = ME_FindItemBack(rundi, diStartRow);
+ ME_GetSelection(c->editor, &blfrom, &blto);
+
+ ME_DrawTextWithStyle(c, x, y,
+ run->strText->szData, ME_StrVLen(run->strText), run->style, NULL,
+ blfrom-runofs, blto-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight);
+ }
+}
+
+COLORREF ME_GetBackColor(ME_TextEditor *editor)
+{
+/* Looks like I was seriously confused
+ return GetSysColor((GetWindowLong(editor->hWnd, GWL_STYLE) & ES_READONLY) ? COLOR_3DFACE: COLOR_WINDOW);
+*/
+ if (editor->rgbBackColor == -1)
+ return GetSysColor(COLOR_WINDOW);
+ else
+ return editor->rgbBackColor;
+}
+
+void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) {
+ int align = SetTextAlign(c->hDC, TA_BASELINE);
+ ME_DisplayItem *p;
+ ME_Run *run;
+ ME_Paragraph *para = NULL;
+ RECT rc, rcPara;
+ int y = c->pt.y;
+ int height = 0, baseline = 0, no=0, pno = 0;
+ int xs, xe;
+ int visible = 0;
+ int nMargWidth = 0;
+
+ c->pt.x = c->rcView.left;
+ rcPara.left = c->rcView.left;
+ rcPara.right = c->rcView.right;
+ for (p = paragraph; p!=paragraph->member.para.next_para; p = p->next) {
+ switch(p->type) {
+ case diParagraph:
+ para = &p->member.para;
+ break;
+ case diStartRow:
+ assert(para);
+ nMargWidth = (pno==0?para->nFirstMargin:para->nLeftMargin);
+ xs = c->rcView.left+nMargWidth;
+ xe = c->rcView.right-para->nRightMargin;
+ y += height;
+ rcPara.top = y;
+ rcPara.bottom = y+p->member.row.nHeight;
+ visible = RectVisible(c->hDC, &rcPara);
+ if (visible) {
+ HBRUSH hbr;
+ /* left margin */
+ rc.left = c->rcView.left;
+ rc.right = c->rcView.left+nMargWidth;
+ rc.top = y;
+ rc.bottom = y+p->member.row.nHeight;
+ FillRect(c->hDC, &rc, c->hbrMargin);
+ /* right margin */
+ rc.left = xe;
+ rc.right = c->rcView.right;
+ FillRect(c->hDC, &rc, c->hbrMargin);
+ rc.left = c->rcView.left+para->nLeftMargin;
+ rc.right = xe;
+ hbr = CreateSolidBrush(ME_GetBackColor(c->editor));
+ FillRect(c->hDC, &rc, hbr);
+ DeleteObject(hbr);
+ }
+ if (me_debug)
+ {
+ const WCHAR wszRowDebug[] = {'r','o','w','[','%','d',']',0};
+ WCHAR buf[128];
+ POINT pt = c->pt;
+ wsprintfW(buf, wszRowDebug, no);
+ pt.y = 12+y;
+ ME_DebugWrite(c->hDC, &pt, buf);
+ }
+
+ height = p->member.row.nHeight;
+ baseline = p->member.row.nBaseline;
+ pno++;
+ break;
+ case diRun:
+ assert(para);
+ run = &p->member.run;
+ if (visible && me_debug) {
+ rc.left = c->rcView.left+run->pt.x;
+ rc.right = c->rcView.left+run->pt.x+run->nWidth;
+ rc.top = c->pt.y+run->pt.y;
+ rc.bottom = c->pt.y+run->pt.y+height;
+ TRACE("rc = (%ld, %ld, %ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ if (run->nFlags & MERF_SKIPPED)
+ DrawFocusRect(c->hDC, &rc);
+ else
+ FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
+ }
+ if (visible)
+ ME_DrawRun(c, run->pt.x, c->pt.y+run->pt.y+baseline, p, ¶graph->member.para);
+ if (me_debug)
+ {
+ /* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */
+ const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0};
+ WCHAR buf[2560];
+ POINT pt;
+ pt.x = run->pt.x;
+ pt.y = c->pt.y + run->pt.y;
+ wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData);
+ ME_DebugWrite(c->hDC, &pt, buf);
+ }
+ /* c->pt.x += p->member.run.nWidth; */
+ break;
+ default:
+ break;
+ }
+ no++;
+ }
+ SetTextAlign(c->hDC, align);
+}
+
+void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos)
+{
+ float perc = 0.0;
+ SCROLLINFO si;
+ HWND hWnd = editor->hWnd;
+ int overflow = editor->nTotalLength - editor->sizeWindow.cy;
+ si.cbSize = sizeof(SCROLLINFO);
+ si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
+ GetScrollInfo(hWnd, SB_VERT, &si);
+
+ if (ypos < 0) {
+ if (si.nMax<1) si.nMax = 1;
+ perc = 1.0*si.nPos/si.nMax;
+ ypos = perc*overflow;
+ }
+ if (ypos >= overflow && overflow > 0)
+ ypos = overflow - 1;
+
+ if (overflow > 0) {
+ EnableScrollBar(hWnd, SB_VERT, ESB_ENABLE_BOTH);
+ SetScrollRange(hWnd, SB_VERT, 0, overflow, FALSE);
+ SetScrollPos(hWnd, SB_VERT, ypos, TRUE);
+ } else {
+ EnableScrollBar(hWnd, SB_VERT, ESB_DISABLE_BOTH);
+ SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
+ SetScrollPos(hWnd, SB_VERT, 0, TRUE);
+ }
+ if (ypos != si.nPos)
+ {
+ TRACE("ScrollWindow(%d, %d, %d, %0.4f)\n", si.nPos, si.nMax, ypos, perc);
+ ScrollWindow(hWnd, 0, si.nPos - ypos, NULL, NULL);
+ UpdateWindow(hWnd);
+ }
+}
+
+int ME_GetScrollPos(ME_TextEditor *editor)
+{
+ return GetScrollPos(editor->hWnd, SB_VERT);
+}
+
+void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun)
+{
+ ME_DisplayItem *pRow = ME_FindItemBack(pRun, diStartRow);
+ ME_DisplayItem *pPara = ME_FindItemBack(pRun, diParagraph);
+ int y, yrel, yheight;
+
+ assert(pRow);
+ assert(pPara);
+
+ y = pPara->member.para.nYPos+pRow->member.row.nYPos;
+ yheight = pRow->member.row.nHeight;
+ yrel = y - ME_GetScrollPos(editor);
+ if (yrel < 0)
+ ME_UpdateScrollBar(editor, y);
+ else if (yrel + yheight > editor->sizeWindow.cy)
+ {
+ ME_UpdateScrollBar(editor, y + yheight - editor->sizeWindow.cy);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ ME_Repaint(editor);
+}
+
+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);
+}
--- /dev/null
+/*
+ * WINE RTF file reader
+ *
+ * Portions Copyright 2004 Mike McCormack for CodeWeavers
+ *
+ * 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
+ */
+
+/*
+ * Derived from RTF Tools by Paul DuBois (dubois@primate.wisc.edu)
+ * Homepage: http://www.snake.net/software/RTF/
+ * Original license follows:
+ */
+
+/*
+ * reader.c - RTF file reader. Release 1.10.
+ *
+ * ....
+ *
+ * Author: Paul DuBois dubois@primate.wisc.edu
+ *
+ * This software may be redistributed without restriction and used for
+ * any purpose whatsoever.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "rtf.h"
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(richedit);
+
+extern HANDLE me_heap;
+
+static int _RTFGetChar(RTF_Info *);
+static void _RTFGetToken (RTF_Info *);
+static void _RTFGetToken2 (RTF_Info *);
+static int GetChar (RTF_Info *);
+static void ReadFontTbl (RTF_Info *);
+static void ReadColorTbl (RTF_Info *);
+static void ReadStyleSheet (RTF_Info *);
+static void ReadInfoGroup (RTF_Info *);
+static void ReadPictGroup (RTF_Info *);
+static void ReadObjGroup (RTF_Info *);
+static void LookupInit (void);
+static void Lookup (RTF_Info *, char *);
+static int Hash (char*);
+
+static void CharSetInit (RTF_Info *);
+static void ReadCharSetMaps (RTF_Info *);
+
+
+/*
+ RTF ANSI character set (\ansi) general map
+ These are taken from the ISO-Latin-1 (ISO-8859-1) encodings, with
+ a few additions
+
+ Field 1 is the standard character name which the character value in
+ field 2 maps onto. (It doesn't mean "to produce the character in field 1,
+ use the value in field 2.)
+
+ The character value may be given either as a single character (which will be
+ converted to the ASCII value of the character), or in numeric format, either
+ in decimal or 0xyy as hex yy. Single or double quotes may be used to quote
+ characters.*/
+
+int ansi_gen[] =
+{
+ rtfSC_formula ,0x06,
+ rtfSC_nobrkhyphen ,0x1e,
+ rtfSC_opthyphen ,0x1f,
+ rtfSC_space ,' ',
+ rtfSC_exclam ,'!',
+ rtfSC_quotedbl ,'"',
+ rtfSC_numbersign ,'#',
+ rtfSC_dollar ,'$',
+ rtfSC_percent ,'%',
+ rtfSC_ampersand ,'&',
+ rtfSC_quoteright ,'\'',
+ rtfSC_parenleft ,'(',
+ rtfSC_parenright ,')',
+ rtfSC_asterisk ,'*',
+ rtfSC_plus ,'+',
+ rtfSC_comma ,',',
+ rtfSC_hyphen ,'-',
+ rtfSC_period ,'.',
+ rtfSC_slash ,'/',
+ rtfSC_zero ,'0',
+ rtfSC_one ,'1',
+ rtfSC_two ,'2',
+ rtfSC_three ,'3',
+ rtfSC_four ,'4',
+ rtfSC_five ,'5',
+ rtfSC_six ,'6',
+ rtfSC_seven ,'7',
+ rtfSC_eight ,'8',
+ rtfSC_nine ,'9',
+ rtfSC_colon ,':',
+ rtfSC_semicolon ,';',
+ rtfSC_less ,'<',
+ rtfSC_equal ,'=',
+ rtfSC_greater ,'>',
+ rtfSC_question ,'?',
+ rtfSC_at ,'@',
+ rtfSC_A ,'A',
+ rtfSC_B ,'B',
+ rtfSC_C ,'C',
+ rtfSC_D ,'D',
+ rtfSC_E ,'E',
+ rtfSC_F ,'F',
+ rtfSC_G ,'G',
+ rtfSC_H ,'H',
+ rtfSC_I ,'I',
+ rtfSC_J ,'J',
+ rtfSC_K ,'K',
+ rtfSC_L ,'L',
+ rtfSC_M ,'M',
+ rtfSC_N ,'N',
+ rtfSC_O ,'O',
+ rtfSC_P ,'P',
+ rtfSC_Q ,'Q',
+ rtfSC_R ,'R',
+ rtfSC_S ,'S',
+ rtfSC_T ,'T',
+ rtfSC_U ,'U',
+ rtfSC_V ,'V',
+ rtfSC_W ,'W',
+ rtfSC_X ,'X',
+ rtfSC_Y ,'Y',
+ rtfSC_Z ,'Z',
+ rtfSC_bracketleft ,'[',
+ rtfSC_backslash ,'\\',
+ rtfSC_bracketright ,']',
+ rtfSC_asciicircum ,'^',
+ rtfSC_underscore ,'_',
+ rtfSC_quoteleft ,'`',
+ rtfSC_a ,'a',
+ rtfSC_b ,'b',
+ rtfSC_c ,'c',
+ rtfSC_d ,'d',
+ rtfSC_e ,'e',
+ rtfSC_f ,'f',
+ rtfSC_g ,'g',
+ rtfSC_h ,'h',
+ rtfSC_i ,'i',
+ rtfSC_j ,'j',
+ rtfSC_k ,'k',
+ rtfSC_l ,'l',
+ rtfSC_m ,'m',
+ rtfSC_n ,'n',
+ rtfSC_o ,'o',
+ rtfSC_p ,'p',
+ rtfSC_q ,'q',
+ rtfSC_r ,'r',
+ rtfSC_s ,'s',
+ rtfSC_t ,'t',
+ rtfSC_u ,'u',
+ rtfSC_v ,'v',
+ rtfSC_w ,'w',
+ rtfSC_x ,'x',
+ rtfSC_y ,'y',
+ rtfSC_z ,'z',
+ rtfSC_braceleft ,'{',
+ rtfSC_bar ,'|',
+ rtfSC_braceright ,'}',
+ rtfSC_asciitilde ,'~',
+ rtfSC_nobrkspace ,0xa0,
+ rtfSC_exclamdown ,0xa1,
+ rtfSC_cent ,0xa2,
+ rtfSC_sterling ,0xa3,
+ rtfSC_currency ,0xa4,
+ rtfSC_yen ,0xa5,
+ rtfSC_brokenbar ,0xa6,
+ rtfSC_section ,0xa7,
+ rtfSC_dieresis ,0xa8,
+ rtfSC_copyright ,0xa9,
+ rtfSC_ordfeminine ,0xaa,
+ rtfSC_guillemotleft ,0xab,
+ rtfSC_logicalnot ,0xac,
+ rtfSC_opthyphen ,0xad,
+ rtfSC_registered ,0xae,
+ rtfSC_macron ,0xaf,
+ rtfSC_degree ,0xb0,
+ rtfSC_plusminus ,0xb1,
+ rtfSC_twosuperior ,0xb2,
+ rtfSC_threesuperior ,0xb3,
+ rtfSC_acute ,0xb4,
+ rtfSC_mu ,0xb5,
+ rtfSC_paragraph ,0xb6,
+ rtfSC_periodcentered ,0xb7,
+ rtfSC_cedilla ,0xb8,
+ rtfSC_onesuperior ,0xb9,
+ rtfSC_ordmasculine ,0xba,
+ rtfSC_guillemotright ,0xbb,
+ rtfSC_onequarter ,0xbc,
+ rtfSC_onehalf ,0xbd,
+ rtfSC_threequarters ,0xbe,
+ rtfSC_questiondown ,0xbf,
+ rtfSC_Agrave ,0xc0,
+ rtfSC_Aacute ,0xc1,
+ rtfSC_Acircumflex ,0xc2,
+ rtfSC_Atilde ,0xc3,
+ rtfSC_Adieresis ,0xc4,
+ rtfSC_Aring ,0xc5,
+ rtfSC_AE ,0xc6,
+ rtfSC_Ccedilla ,0xc7,
+ rtfSC_Egrave ,0xc8,
+ rtfSC_Eacute ,0xc9,
+ rtfSC_Ecircumflex ,0xca,
+ rtfSC_Edieresis ,0xcb,
+ rtfSC_Igrave ,0xcc,
+ rtfSC_Iacute ,0xcd,
+ rtfSC_Icircumflex ,0xce,
+ rtfSC_Idieresis ,0xcf,
+ rtfSC_Eth ,0xd0,
+ rtfSC_Ntilde ,0xd1,
+ rtfSC_Ograve ,0xd2,
+ rtfSC_Oacute ,0xd3,
+ rtfSC_Ocircumflex ,0xd4,
+ rtfSC_Otilde ,0xd5,
+ rtfSC_Odieresis ,0xd6,
+ rtfSC_multiply ,0xd7,
+ rtfSC_Oslash ,0xd8,
+ rtfSC_Ugrave ,0xd9,
+ rtfSC_Uacute ,0xda,
+ rtfSC_Ucircumflex ,0xdb,
+ rtfSC_Udieresis ,0xdc,
+ rtfSC_Yacute ,0xdd,
+ rtfSC_Thorn ,0xde,
+ rtfSC_germandbls ,0xdf,
+ rtfSC_agrave ,0xe0,
+ rtfSC_aacute ,0xe1,
+ rtfSC_acircumflex ,0xe2,
+ rtfSC_atilde ,0xe3,
+ rtfSC_adieresis ,0xe4,
+ rtfSC_aring ,0xe5,
+ rtfSC_ae ,0xe6,
+ rtfSC_ccedilla ,0xe7,
+ rtfSC_egrave ,0xe8,
+ rtfSC_eacute ,0xe9,
+ rtfSC_ecircumflex ,0xea,
+ rtfSC_edieresis ,0xeb,
+ rtfSC_igrave ,0xec,
+ rtfSC_iacute ,0xed,
+ rtfSC_icircumflex ,0xee,
+ rtfSC_idieresis ,0xef,
+ rtfSC_eth ,0xf0,
+ rtfSC_ntilde ,0xf1,
+ rtfSC_ograve ,0xf2,
+ rtfSC_oacute ,0xf3,
+ rtfSC_ocircumflex ,0xf4,
+ rtfSC_otilde ,0xf5,
+ rtfSC_odieresis ,0xf6,
+ rtfSC_divide ,0xf7,
+ rtfSC_oslash ,0xf8,
+ rtfSC_ugrave ,0xf9,
+ rtfSC_uacute ,0xfa,
+ rtfSC_ucircumflex ,0xfb,
+ rtfSC_udieresis ,0xfc,
+ rtfSC_yacute ,0xfd,
+ rtfSC_thorn ,0xfe,
+ rtfSC_ydieresis ,0xff
+};
+
+/*
+ * RTF ANSI character set (\ansi) Symbol font map
+ *
+ * Field 1 is the standard character name which the character value in
+ * field 2 maps onto. (It doesn't mean "to produce the character in field 1,
+ * use the value in field 2.)
+ *
+ * The character value may be given either as a single character (which will be
+ * converted to the ASCII value of the character), or in numeric format, either
+ * in decimal or 0xyy as hex yy. Single or double quotes may be used to quote
+ * characters.
+ *
+ */
+
+int ansi_sym[] =
+{
+ rtfSC_formula ,0x06,
+ rtfSC_nobrkhyphen ,0x1e,
+ rtfSC_opthyphen ,0x1f,
+ rtfSC_space ,' ',
+ rtfSC_exclam ,'!',
+ rtfSC_universal ,'"',
+ rtfSC_mathnumbersign ,'#',
+ rtfSC_existential ,'$',
+ rtfSC_percent ,'%',
+ rtfSC_ampersand ,'&',
+ rtfSC_suchthat ,'\\',
+ rtfSC_parenleft ,'(',
+ rtfSC_parenright ,')',
+ rtfSC_mathasterisk ,'*',
+ rtfSC_mathplus ,'+',
+ rtfSC_comma ,',',
+ rtfSC_mathminus ,'-',
+ rtfSC_period ,'.',
+ rtfSC_slash ,'/',
+ rtfSC_zero ,'0',
+ rtfSC_one ,'1',
+ rtfSC_two ,'2',
+ rtfSC_three ,'3',
+ rtfSC_four ,'4',
+ rtfSC_five ,'5',
+ rtfSC_six ,'6',
+ rtfSC_seven ,'7',
+ rtfSC_eight ,'8',
+ rtfSC_nine ,'9',
+ rtfSC_colon ,':',
+ rtfSC_semicolon ,';',
+ rtfSC_less ,'<',
+ rtfSC_mathequal ,'=',
+ rtfSC_greater ,'>',
+ rtfSC_question ,'?',
+ rtfSC_congruent ,'@',
+ rtfSC_Alpha ,'A',
+ rtfSC_Beta ,'B',
+ rtfSC_Chi ,'C',
+ rtfSC_Delta ,'D',
+ rtfSC_Epsilon ,'E',
+ rtfSC_Phi ,'F',
+ rtfSC_Gamma ,'G',
+ rtfSC_Eta ,'H',
+ rtfSC_Iota ,'I',
+ rtfSC_Kappa ,'K',
+ rtfSC_Lambda ,'L',
+ rtfSC_Mu ,'M',
+ rtfSC_Nu ,'N',
+ rtfSC_Omicron ,'O',
+ rtfSC_Pi ,'P',
+ rtfSC_Theta ,'Q',
+ rtfSC_Rho ,'R',
+ rtfSC_Sigma ,'S',
+ rtfSC_Tau ,'T',
+ rtfSC_Upsilon ,'U',
+ rtfSC_varsigma ,'V',
+ rtfSC_Omega ,'W',
+ rtfSC_Xi ,'X',
+ rtfSC_Psi ,'Y',
+ rtfSC_Zeta ,'Z',
+ rtfSC_bracketleft ,'[',
+ rtfSC_backslash ,'\\',
+ rtfSC_bracketright ,']',
+ rtfSC_asciicircum ,'^',
+ rtfSC_underscore ,'_',
+ rtfSC_quoteleft ,'`',
+ rtfSC_alpha ,'a',
+ rtfSC_beta ,'b',
+ rtfSC_chi ,'c',
+ rtfSC_delta ,'d',
+ rtfSC_epsilon ,'e',
+ rtfSC_phi ,'f',
+ rtfSC_gamma ,'g',
+ rtfSC_eta ,'h',
+ rtfSC_iota ,'i',
+ rtfSC_kappa ,'k',
+ rtfSC_lambda ,'l',
+ rtfSC_mu ,'m',
+ rtfSC_nu ,'n',
+ rtfSC_omicron ,'o',
+ rtfSC_pi ,'p',
+ rtfSC_theta ,'q',
+ rtfSC_rho ,'r',
+ rtfSC_sigma ,'s',
+ rtfSC_tau ,'t',
+ rtfSC_upsilon ,'u',
+ rtfSC_omega ,'w',
+ rtfSC_xi ,'x',
+ rtfSC_psi ,'y',
+ rtfSC_zeta ,'z',
+ rtfSC_braceleft ,'{',
+ rtfSC_bar ,'|',
+ rtfSC_braceright ,'}',
+ rtfSC_mathtilde ,'~'
+};
+
+/*
+ * Output sequence map for rtf2text
+ *
+ * Field 1 is the standard character name. Field 2 is the output sequence
+ * to produce for that character.
+ *
+ * The output sequence is simply a string of characters. If it contains
+ * whitespace, it may be quoted. If it contains quotes, it may be quoted
+ * with a different quote character.
+ *
+ * characters in ASCII range (32-127
+ */
+
+const char *text_map[] = {
+ "space" ," ",
+ "exclam" ,"!",
+ "quotedbl" ,"\"",
+ "numbersign" ,"#",
+ "dollar" ,"$",
+ "percent" ,"%",
+ "ampersand" ,"&",
+ "quoteright" ,"'",
+ "parenleft" ,"(",
+ "parenright" ,")",
+ "asterisk" ,"*",
+ "plus" ,"+",
+ "comma" ,",",
+ "hyphen" ,"-",
+ "period" ,".",
+ "slash" ,"/",
+ "zero" ,"0",
+ "one" ,"1",
+ "two" ,"2",
+ "three" ,"3",
+ "four" ,"4",
+ "five" ,"5",
+ "six" ,"6",
+ "seven" ,"7",
+ "eight" ,"8",
+ "nine" ,"9",
+ "colon" ,":",
+ "semicolon" ,";",
+ "less" ,"<",
+ "equal" ,"=",
+ "greater" ,">",
+ "question" ,"?",
+ "at" ,"@",
+ "A" ,"A",
+ "B" ,"B",
+ "C" ,"C",
+ "D" ,"D",
+ "E" ,"E",
+ "F" ,"F",
+ "G" ,"G",
+ "H" ,"H",
+ "I" ,"I",
+ "J" ,"J",
+ "K" ,"K",
+ "L" ,"L",
+ "M" ,"M",
+ "N" ,"N",
+ "O" ,"O",
+ "P" ,"P",
+ "Q" ,"Q",
+ "R" ,"R",
+ "S" ,"S",
+ "T" ,"T",
+ "U" ,"U",
+ "V" ,"V",
+ "W" ,"W",
+ "X" ,"X",
+ "Y" ,"Y",
+ "Z" ,"Z",
+ "bracketleft" ,"[",
+ "backslash" ,"\\",
+ "bracketright" ,"]",
+ "asciicircum" ,"^",
+ "underscore" ,"_",
+ "quoteleft" ,"`",
+ "a" ,"a",
+ "b" ,"b",
+ "c" ,"c",
+ "d" ,"d",
+ "e" ,"e",
+ "f" ,"f",
+ "g" ,"g",
+ "h" ,"h",
+ "i" ,"i",
+ "j" ,"j",
+ "k" ,"k",
+ "l" ,"l",
+ "m" ,"m",
+ "n" ,"n",
+ "o" ,"o",
+ "p" ,"p",
+ "q" ,"q",
+ "r" ,"r",
+ "s" ,"s",
+ "t" ,"t",
+ "u" ,"u",
+ "v" ,"v",
+ "w" ,"w",
+ "x" ,"x",
+ "y" ,"y",
+ "z" ,"z",
+ "braceleft" ,"{",
+ "bar" ,"|",
+ "braceright" ,"}",
+ "asciitilde" ,"~",
+ "AE" ,"AE",
+ "OE" ,"OE",
+ "acute" ,"'",
+ "ae" ,"ae",
+ "angleleft" ,"<",
+ "angleright" ,">",
+ "arrowboth" ,"<->",
+ "arrowdblboth" ,"<=>",
+ "arrowdblleft" ,"<=",
+ "arrowdblright" ,"=>",
+ "arrowleft" ,"<-",
+ "arrowright" ,"->",
+ "bullet" ,"o",
+ "cent" ,"cent",
+ "circumflex" ,"^",
+ "copyright" ,"(c)",
+ "copyrightsans" ,"(c)",
+ "degree" ,"deg.",
+ "divide" ,"/",
+ "dotlessi" ,"i",
+ "ellipsis" ,"...",
+ "emdash" ,"--",
+ "endash" ,"-",
+ "fi" ,"fi",
+ "fl" ,"fl",
+ "fraction" ,"/",
+ "germandbls" ,"ss",
+ "grave" ,"`",
+ "greaterequal" ,">=",
+ "guillemotleft" ,"<<",
+ "guillemotright" ,">>",
+ "guilsinglleft" ,"<",
+ "guilsinglright" ,">",
+ "lessequal" ,"<=",
+ "logicalnot" ,"~",
+ "mathasterisk" ,"*",
+ "mathequal" ,"=",
+ "mathminus" ,"-",
+ "mathnumbersign" ,"#",
+ "mathplus" ,"+",
+ "mathtilde" ,"~",
+ "minus" ,"-",
+ "mu" ,"u",
+ "multiply" ,"x",
+ "nobrkhyphen" ,"-",
+ "nobrkspace" ," ",
+ "notequal" ,"!=",
+ "oe" ,"oe",
+ "onehalf" ,"1/2",
+ "onequarter" ,"1/4",
+ "periodcentered" ,".",
+ "plusminus" ,"+/-",
+ "quotedblbase" ,",,",
+ "quotedblleft" ,"\"",
+ "quotedblright" ,"\"",
+ "quotesinglbase" ,",",
+ "registered" ,"reg.",
+ "registersans" ,"reg.",
+ "threequarters" ,"3/4",
+ "tilde" ,"~",
+ "trademark" ,"(TM)",
+ "trademarksans" ,"(TM)"
+};
+
+/*
+ * This array is used to map standard character names onto their numeric codes.
+ * The position of the name within the array is the code.
+ * stdcharnames.h is generated in the ../h directory.
+ */
+
+const char *stdCharName[] =
+{
+ "nothing",
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ "questiondown",
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ "AE",
+ "ordfeminine",
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ "ae",
+ "dotlessi",
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ "Aacute",
+ "Acircumflex",
+ "Adieresis",
+ "Agrave",
+ "Aring",
+ "Atilde",
+ "Ccedilla",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Egrave",
+ "Eth",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Ntilde",
+ "Oacute",
+ "Ocircumflex",
+ "Odieresis",
+ "Ograve",
+ "Otilde",
+ "Scaron",
+ "Thorn",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Ugrave",
+ "Yacute",
+ "Ydieresis",
+ "aacute",
+ "acircumflex",
+ "adieresis",
+ "agrave",
+ "aring",
+ "atilde",
+ "brokenbar",
+ "ccedilla",
+ "copyright",
+ "degree",
+ "divide",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "egrave",
+ "eth",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "igrave",
+ "logicalnot",
+ "minus",
+ "multiply",
+ "ntilde",
+ "oacute",
+ "ocircumflex",
+ "odieresis",
+ "ograve",
+ "onehalf",
+ "onequarter",
+ "onesuperior",
+ "otilde",
+ "plusminus",
+ "registered",
+ "thorn",
+ "threequarters",
+ "threesuperior",
+ "trademark",
+ "twosuperior",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "ugrave",
+ "yacute",
+ "ydieresis",
+ "Alpha",
+ "Beta",
+ "Chi",
+ "Delta",
+ "Epsilon",
+ "Phi",
+ "Gamma",
+ "Eta",
+ "Iota",
+ "Kappa",
+ "Lambda",
+ "Mu",
+ "Nu",
+ "Omicron",
+ "Pi",
+ "Theta",
+ "Rho",
+ "Sigma",
+ "Tau",
+ "Upsilon",
+ "varUpsilon",
+ "Omega",
+ "Xi",
+ "Psi",
+ "Zeta",
+ "alpha",
+ "beta",
+ "chi",
+ "delta",
+ "epsilon",
+ "phi",
+ "varphi",
+ "gamma",
+ "eta",
+ "iota",
+ "kappa",
+ "lambda",
+ "mu",
+ "nu",
+ "omicron",
+ "pi",
+ "varpi",
+ "theta",
+ "vartheta",
+ "rho",
+ "sigma",
+ "varsigma",
+ "tau",
+ "upsilon",
+ "omega",
+ "xi",
+ "psi",
+ "zeta",
+ "nobrkspace",
+ "nobrkhyphen",
+ "lessequal",
+ "greaterequal",
+ "infinity",
+ "integral",
+ "notequal",
+ "radical",
+ "radicalex",
+ "approxequal",
+ "apple",
+ "partialdiff",
+ "opthyphen",
+ "formula",
+ "lozenge",
+ "universal",
+ "existential",
+ "suchthat",
+ "congruent",
+ "therefore",
+ "perpendicular",
+ "minute",
+ "club",
+ "diamond",
+ "heart",
+ "spade",
+ "arrowboth",
+ "arrowleft",
+ "arrowup",
+ "arrowright",
+ "arrowdown",
+ "second",
+ "proportional",
+ "equivalence",
+ "arrowvertex",
+ "arrowhorizex",
+ "carriagereturn",
+ "aleph",
+ "Ifraktur",
+ "Rfraktur",
+ "weierstrass",
+ "circlemultiply",
+ "circleplus",
+ "emptyset",
+ "intersection",
+ "union",
+ "propersuperset",
+ "reflexsuperset",
+ "notsubset",
+ "propersubset",
+ "reflexsubset",
+ "element",
+ "notelement",
+ "angle",
+ "gradient",
+ "product",
+ "logicaland",
+ "logicalor",
+ "arrowdblboth",
+ "arrowdblleft",
+ "arrowdblup",
+ "arrowdblright",
+ "arrowdbldown",
+ "angleleft",
+ "registersans",
+ "copyrightsans",
+ "trademarksans",
+ "angleright",
+ "mathplus",
+ "mathminus",
+ "mathasterisk",
+ "mathnumbersign",
+ "dotmath",
+ "mathequal",
+ "mathtilde",
+ (char *) NULL
+};
+
+int _RTFGetChar(RTF_Info *info)
+{
+ int ch;
+
+ TRACE("\n");
+
+ /* if the last buffer wasn't full, it's EOF */
+ if (info->dwInputSize > 0 &&
+ info->dwInputSize == info->dwInputUsed && info->dwInputSize < sizeof(info->InputBuffer))
+ return EOF;
+ if (info->dwInputSize <= info->dwInputUsed)
+ {
+ long count = 0;
+ info->editstream.dwError = info->editstream.pfnCallback(info->editstream.dwCookie,
+ info->InputBuffer, sizeof(info->InputBuffer), &count);
+ /* if error, it's EOF */
+ if (info->editstream.dwError)
+ return EOF;
+ /* if no bytes read, it's EOF */
+ if(count == 0)
+ return EOF;
+ info->dwInputSize = count;
+ info->dwInputUsed = 0;
+ }
+ ch = info->InputBuffer[info->dwInputUsed++];
+ if (!ch)
+ return EOF;
+ return ch;
+}
+
+void RTFSetEditStream(RTF_Info *info, EDITSTREAM *es)
+{
+ TRACE("\n");
+
+ info->editstream.dwCookie = es->dwCookie;
+ info->editstream.dwError = es->dwError;
+ info->editstream.pfnCallback = es->pfnCallback;
+}
+
+/*
+ * Initialize the reader. This may be called multiple times,
+ * to read multiple files. The only thing not reset is the input
+ * stream; that must be done with RTFSetStream().
+ */
+
+void RTFInit(RTF_Info *info)
+{
+ int i;
+ RTFColor *cp;
+ RTFFont *fp;
+ RTFStyle *sp;
+ RTFStyleElt *eltList, *ep;
+
+ TRACE("\n");
+
+ if (info->rtfTextBuf == (char *) NULL) /* initialize the text buffers */
+ {
+ info->rtfTextBuf = RTFAlloc (rtfBufSiz);
+ info->pushedTextBuf = RTFAlloc (rtfBufSiz);
+ if (info->rtfTextBuf == (char *) NULL
+ || info->pushedTextBuf == (char *) NULL)
+ RTFPanic (info,"Cannot allocate text buffers.");
+ info->rtfTextBuf[0] = info->pushedTextBuf[0] = '\0';
+ }
+
+ RTFFree (info->inputName);
+ RTFFree (info->outputName);
+ info->inputName = info->outputName = (char *) NULL;
+
+ /* initialize lookup table */
+ LookupInit ();
+
+ for (i = 0; i < rtfMaxClass; i++)
+ RTFSetClassCallback (info, i, (RTFFuncPtr) NULL);
+ for (i = 0; i < rtfMaxDestination; i++)
+ RTFSetDestinationCallback (info, i, (RTFFuncPtr) NULL);
+
+ /* install built-in destination readers */
+ RTFSetDestinationCallback (info, rtfFontTbl, ReadFontTbl);
+ RTFSetDestinationCallback (info, rtfColorTbl, ReadColorTbl);
+ RTFSetDestinationCallback (info, rtfStyleSheet, ReadStyleSheet);
+ RTFSetDestinationCallback (info, rtfInfo, ReadInfoGroup);
+ RTFSetDestinationCallback (info, rtfPict, ReadPictGroup);
+ RTFSetDestinationCallback (info, rtfObject, ReadObjGroup);
+
+
+ RTFSetReadHook (info, (RTFFuncPtr) NULL);
+
+ /* dump old lists if necessary */
+
+ while (info->fontList != (RTFFont *) NULL)
+ {
+ fp = info->fontList->rtfNextFont;
+ RTFFree (info->fontList->rtfFName);
+ RTFFree ((char *) info->fontList);
+ info->fontList = fp;
+ }
+ while (info->colorList != (RTFColor *) NULL)
+ {
+ cp = info->colorList->rtfNextColor;
+ RTFFree ((char *) info->colorList);
+ info->colorList = cp;
+ }
+ while (info->styleList != (RTFStyle *) NULL)
+ {
+ sp = info->styleList->rtfNextStyle;
+ eltList = info->styleList->rtfSSEList;
+ while (eltList != (RTFStyleElt *) NULL)
+ {
+ ep = eltList->rtfNextSE;
+ RTFFree (eltList->rtfSEText);
+ RTFFree ((char *) eltList);
+ eltList = ep;
+ }
+ RTFFree (info->styleList->rtfSName);
+ RTFFree ((char *) info->styleList);
+ info->styleList = sp;
+ }
+
+ info->rtfClass = -1;
+ info->pushedClass = -1;
+ info->pushedChar = EOF;
+
+ info->rtfLineNum = 0;
+ info->rtfLinePos = 0;
+ info->prevChar = EOF;
+ info->bumpLine = 0;
+
+ CharSetInit (info);
+ info->csTop = 0;
+}
+
+/*
+ * Set or get the input or output file name. These are never guaranteed
+ * to be accurate, only insofar as the calling program makes them so.
+ */
+
+void RTFSetInputName(RTF_Info *info, char *name)
+{
+ TRACE("\n");
+
+ if ((info->inputName = RTFStrSave (name)) == (char *) NULL)
+ RTFPanic (info,"RTFSetInputName: out of memory");
+}
+
+
+char *RTFGetInputName(RTF_Info *info)
+{
+ return (info->inputName);
+}
+
+
+void RTFSetOutputName(RTF_Info *info, char *name)
+{
+ TRACE("\n");
+
+ if ((info->outputName = RTFStrSave (name)) == (char *) NULL)
+ RTFPanic (info, "RTFSetOutputName: out of memory");
+}
+
+
+char *RTFGetOutputName(RTF_Info *info)
+{
+ return (info->outputName);
+}
+
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Callback table manipulation routines
+ */
+
+
+/*
+ * Install or return a writer callback for a token class
+ */
+
+void RTFSetClassCallback(RTF_Info *info, int class, RTFFuncPtr callback)
+{
+ if (class >= 0 && class < rtfMaxClass)
+ info->ccb[class] = callback;
+}
+
+
+RTFFuncPtr RTFGetClassCallback(RTF_Info *info, int class)
+{
+ if (class >= 0 && class < rtfMaxClass)
+ return (info->ccb[class]);
+ return ((RTFFuncPtr) NULL);
+}
+
+
+/*
+ * Install or return a writer callback for a destination type
+ */
+
+void RTFSetDestinationCallback(RTF_Info *info, int dest, RTFFuncPtr callback)
+{
+ if (dest >= 0 && dest < rtfMaxDestination)
+ info->dcb[dest] = callback;
+}
+
+
+RTFFuncPtr RTFGetDestinationCallback(RTF_Info *info, int dest)
+{
+ if (dest >= 0 && dest < rtfMaxDestination)
+ return (info->dcb[dest]);
+ return ((RTFFuncPtr) NULL);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Token reading routines
+ */
+
+
+/*
+ * Read the input stream, invoking the writer's callbacks
+ * where appropriate.
+ */
+
+void RTFRead(RTF_Info *info)
+{
+ while (RTFGetToken (info) != rtfEOF)
+ RTFRouteToken (info);
+}
+
+
+/*
+ * Route a token. If it's a destination for which a reader is
+ * installed, process the destination internally, otherwise
+ * pass the token to the writer's class callback.
+ */
+
+void RTFRouteToken(RTF_Info *info)
+{
+ RTFFuncPtr p;
+
+ TRACE("\n");
+
+ if (info->rtfClass < 0 || info->rtfClass >= rtfMaxClass) /* watchdog */
+ {
+ RTFPanic (info,"Unknown class %d: %s (reader malfunction)",
+ info->rtfClass, info->rtfTextBuf);
+ }
+ if (RTFCheckCM (info, rtfControl, rtfDestination))
+ {
+ /* invoke destination-specific callback if there is one */
+ if ((p = RTFGetDestinationCallback (info, info->rtfMinor))
+ != (RTFFuncPtr) NULL)
+ {
+ (*p) (info);
+ return;
+ }
+ }
+ /* invoke class callback if there is one */
+ if ((p = RTFGetClassCallback (info, info->rtfClass)) != (RTFFuncPtr) NULL)
+ (*p) (info);
+}
+
+
+/*
+ * Skip to the end of the current group. When this returns,
+ * writers that maintain a state stack may want to call their
+ * state unstacker; global vars will still be set to the group's
+ * closing brace.
+ */
+
+void RTFSkipGroup(RTF_Info *info)
+{
+ int level = 1;
+
+ TRACE("\n");
+
+ while (RTFGetToken (info) != rtfEOF)
+ {
+ if (info->rtfClass == rtfGroup)
+ {
+ if (info->rtfMajor == rtfBeginGroup)
+ ++level;
+ else if (info->rtfMajor == rtfEndGroup)
+ {
+ if (--level < 1)
+ break; /* end of initial group */
+ }
+ }
+ }
+}
+
+
+/*
+ * Read one token. Call the read hook if there is one. The
+ * token class is the return value. Returns rtfEOF when there
+ * are no more tokens.
+ */
+
+int RTFGetToken(RTF_Info *info)
+{
+ RTFFuncPtr p;
+
+ TRACE("\n");
+
+ for (;;)
+ {
+ _RTFGetToken (info);
+ if ((p = RTFGetReadHook (info)) != (RTFFuncPtr) NULL)
+ (*p) (info); /* give read hook a look at token */
+
+ /* Silently discard newlines, carriage returns, nulls. */
+ if (!(info->rtfClass == rtfText && info->rtfFormat != SF_TEXT
+ && (info->rtfMajor == '\r' || info->rtfMajor == '\n' || info->rtfMajor == '\0')))
+ break;
+ }
+ return (info->rtfClass);
+}
+
+
+/*
+ * Install or return a token reader hook.
+ */
+
+void RTFSetReadHook(RTF_Info *info, RTFFuncPtr f)
+{
+ info->readHook = f;
+}
+
+
+RTFFuncPtr RTFGetReadHook(RTF_Info *info)
+{
+ return (info->readHook);
+}
+
+
+void RTFUngetToken(RTF_Info *info)
+{
+ TRACE("\n");
+
+ if (info->pushedClass >= 0) /* there's already an ungotten token */
+ RTFPanic (info,"cannot unget two tokens");
+ if (info->rtfClass < 0)
+ RTFPanic (info,"no token to unget");
+ info->pushedClass = info->rtfClass;
+ info->pushedMajor = info->rtfMajor;
+ info->pushedMinor = info->rtfMinor;
+ info->pushedParam = info->rtfParam;
+ (void) strcpy (info->pushedTextBuf, info->rtfTextBuf);
+}
+
+
+int RTFPeekToken(RTF_Info *info)
+{
+ _RTFGetToken (info);
+ RTFUngetToken (info);
+ return (info->rtfClass);
+}
+
+
+static void _RTFGetToken(RTF_Info *info)
+{
+ RTFFont *fp;
+
+ TRACE("\n");
+
+ if (info->rtfFormat == SF_TEXT) {
+ info->rtfMajor = GetChar (info);
+ info->rtfMinor = rtfSC_nothing;
+ info->rtfParam = rtfNoParam;
+ info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
+ if (info->rtfMajor == EOF)
+ info->rtfClass = rtfEOF;
+ else
+ info->rtfClass = rtfText;
+ return;
+ }
+
+ /* first check for pushed token from RTFUngetToken() */
+
+ if (info->pushedClass >= 0)
+ {
+ info->rtfClass = info->pushedClass;
+ info->rtfMajor = info->pushedMajor;
+ info->rtfMinor = info->pushedMinor;
+ info->rtfParam = info->pushedParam;
+ (void) strcpy (info->rtfTextBuf, info->pushedTextBuf);
+ info->rtfTextLen = strlen (info->rtfTextBuf);
+ info->pushedClass = -1;
+ return;
+ }
+
+ /*
+ * Beyond this point, no token is ever seen twice, which is
+ * important, e.g., for making sure no "}" pops the font stack twice.
+ */
+
+ _RTFGetToken2 (info);
+ if (info->rtfClass == rtfText) /* map RTF char to standard code */
+ info->rtfMinor = RTFMapChar (info, info->rtfMajor);
+
+ /*
+ * If auto-charset stuff is activated, see if anything needs doing,
+ * like reading the charset maps or switching between them.
+ */
+
+ if (info->autoCharSetFlags == 0)
+ return;
+
+ if ((info->autoCharSetFlags & rtfReadCharSet)
+ && RTFCheckCM (info, rtfControl, rtfCharSet))
+ {
+ ReadCharSetMaps (info);
+ }
+ else if ((info->autoCharSetFlags & rtfSwitchCharSet)
+ && RTFCheckCMM (info, rtfControl, rtfCharAttr, rtfFontNum))
+ {
+ if ((fp = RTFGetFont (info, info->rtfParam)) != (RTFFont *) NULL)
+ {
+ if (strncmp (fp->rtfFName, "Symbol", 6) == 0)
+ info->curCharSet = rtfCSSymbol;
+ else
+ info->curCharSet = rtfCSGeneral;
+ RTFSetCharSet (info, info->curCharSet);
+ }
+ }
+ else if ((info->autoCharSetFlags & rtfSwitchCharSet) && info->rtfClass == rtfGroup)
+ {
+ switch (info->rtfMajor)
+ {
+ case rtfBeginGroup:
+ if (info->csTop >= maxCSStack)
+ RTFPanic (info, "_RTFGetToken: stack overflow");
+ info->csStack[info->csTop++] = info->curCharSet;
+ break;
+ case rtfEndGroup:
+ /*
+ * If stack top is 1 at this point, we are ending the
+ * group started by the initial {, which ends the
+ * RTF stream
+ */
+ if (info->csTop <= 0)
+ RTFPanic (info,"_RTFGetToken: stack underflow");
+ else if (info->csTop == 1)
+ info->rtfClass = rtfEOF;
+ else
+ {
+ info->curCharSet = info->csStack[--info->csTop];
+ RTFSetCharSet (info, info->curCharSet);
+ }
+ break;
+ }
+ }
+}
+
+
+/* this shouldn't be called anywhere but from _RTFGetToken() */
+
+static void _RTFGetToken2(RTF_Info *info)
+{
+ int sign;
+ int c;
+
+ TRACE("\n");
+
+ /* initialize token vars */
+
+ info->rtfClass = rtfUnknown;
+ info->rtfParam = rtfNoParam;
+ info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
+
+ /* get first character, which may be a pushback from previous token */
+
+ if (info->pushedChar != EOF)
+ {
+ c = info->pushedChar;
+ info->rtfTextBuf[info->rtfTextLen++] = c;
+ info->rtfTextBuf[info->rtfTextLen] = '\0';
+ info->pushedChar = EOF;
+ }
+ else if ((c = GetChar (info)) == EOF)
+ {
+ info->rtfClass = rtfEOF;
+ return;
+ }
+
+ if (c == '{')
+ {
+ info->rtfClass = rtfGroup;
+ info->rtfMajor = rtfBeginGroup;
+ return;
+ }
+ if (c == '}')
+ {
+ info->rtfClass = rtfGroup;
+ info->rtfMajor = rtfEndGroup;
+ return;
+ }
+ if (c != '\\')
+ {
+ /*
+ * Two possibilities here:
+ * 1) ASCII 9, effectively like \tab control symbol
+ * 2) literal text char
+ */
+ if (c == '\t') /* ASCII 9 */
+ {
+ info->rtfClass = rtfControl;
+ info->rtfMajor = rtfSpecialChar;
+ info->rtfMinor = rtfTab;
+ }
+ else
+ {
+ info->rtfClass = rtfText;
+ info->rtfMajor = c;
+ }
+ return;
+ }
+ if ((c = GetChar (info)) == EOF)
+ {
+ /* early eof, whoops (class is rtfUnknown) */
+ return;
+ }
+ if (!isalpha (c))
+ {
+ /*
+ * Three possibilities here:
+ * 1) hex encoded text char, e.g., \'d5, \'d3
+ * 2) special escaped text char, e.g., \{, \}
+ * 3) control symbol, e.g., \_, \-, \|, \<10>
+ */
+ if (c == '\'') /* hex char */
+ {
+ int c2;
+
+ if ((c = GetChar (info)) != EOF && (c2 = GetChar (info)) != EOF)
+ {
+ /* should do isxdigit check! */
+ info->rtfClass = rtfText;
+ info->rtfMajor = RTFCharToHex (c) * 16
+ + RTFCharToHex (c2);
+ return;
+ }
+ /* early eof, whoops (class is rtfUnknown) */
+ return;
+ }
+
+ /* escaped char */
+ /*if (index (":{}\\", c) != (char *) NULL)*/ /* escaped char */
+ if (c == ':' || c == '{' || c == '}' || c == '\\')
+ {
+ info->rtfClass = rtfText;
+ info->rtfMajor = c;
+ return;
+ }
+
+ /* control symbol */
+ Lookup (info, info->rtfTextBuf); /* sets class, major, minor */
+ return;
+ }
+ /* control word */
+ while (isalpha (c))
+ {
+ if ((c = GetChar (info)) == EOF)
+ break;
+ }
+
+ /*
+ * At this point, the control word is all collected, so the
+ * major/minor numbers are determined before the parameter
+ * (if any) is scanned. There will be one too many characters
+ * in the buffer, though, so fix up before and restore after
+ * looking up.
+ */
+
+ if (c != EOF)
+ info->rtfTextBuf[info->rtfTextLen-1] = '\0';
+ Lookup (info, info->rtfTextBuf); /* sets class, major, minor */
+ if (c != EOF)
+ info->rtfTextBuf[info->rtfTextLen-1] = c;
+
+ /*
+ * Should be looking at first digit of parameter if there
+ * is one, unless it's negative. In that case, next char
+ * is '-', so need to gobble next char, and remember sign.
+ */
+
+ sign = 1;
+ if (c == '-')
+ {
+ sign = -1;
+ c = GetChar (info);
+ }
+ if (c != EOF && isdigit (c))
+ {
+ info->rtfParam = 0;
+ while (isdigit (c)) /* gobble parameter */
+ {
+ info->rtfParam = info->rtfParam * 10 + c - '0';
+ if ((c = GetChar (info)) == EOF)
+ break;
+ }
+ info->rtfParam *= sign;
+ }
+ /*
+ * If control symbol delimiter was a blank, gobble it.
+ * Otherwise the character is first char of next token, so
+ * push it back for next call. In either case, delete the
+ * delimiter from the token buffer.
+ */
+ if (c != EOF)
+ {
+ if (c != ' ')
+ info->pushedChar = c;
+ info->rtfTextBuf[--info->rtfTextLen] = '\0';
+ }
+}
+
+
+/*
+ * Read the next character from the input. This handles setting the
+ * current line and position-within-line variables. Those variable are
+ * set correctly whether lines end with CR, LF, or CRLF (the last being
+ * the tricky case).
+ *
+ * bumpLine indicates whether the line number should be incremented on
+ * the *next* input character.
+ */
+
+
+static int GetChar(RTF_Info *info)
+{
+ int c;
+ int oldBumpLine;
+
+ TRACE("\n");
+
+ if ((c = _RTFGetChar(info)) != EOF)
+ {
+ info->rtfTextBuf[info->rtfTextLen++] = c;
+ info->rtfTextBuf[info->rtfTextLen] = '\0';
+ }
+ if (info->prevChar == EOF)
+ info->bumpLine = 1;
+ oldBumpLine = info->bumpLine; /* non-zero if prev char was line ending */
+ info->bumpLine = 0;
+ if (c == '\r')
+ info->bumpLine = 1;
+ else if (c == '\n')
+ {
+ info->bumpLine = 1;
+ if (info->prevChar == '\r') /* oops, previous \r wasn't */
+ oldBumpLine = 0; /* really a line ending */
+ }
+ ++info->rtfLinePos;
+ if (oldBumpLine) /* were we supposed to increment the */
+ { /* line count on this char? */
+ ++info->rtfLineNum;
+ info->rtfLinePos = 1;
+ }
+ info->prevChar = c;
+ return (c);
+}
+
+
+/*
+ * Synthesize a token by setting the global variables to the
+ * values supplied. Typically this is followed with a call
+ * to RTFRouteToken().
+ *
+ * If a param value other than rtfNoParam is passed, it becomes
+ * part of the token text.
+ */
+
+void RTFSetToken(RTF_Info *info, int class, int major, int minor, int param, const char *text)
+{
+ TRACE("\n");
+
+ info->rtfClass = class;
+ info->rtfMajor = major;
+ info->rtfMinor = minor;
+ info->rtfParam = param;
+ if (param == rtfNoParam)
+ (void) strcpy (info->rtfTextBuf, text);
+ else
+ sprintf (info->rtfTextBuf, "%s%d", text, param);
+ info->rtfTextLen = strlen (info->rtfTextBuf);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Routines to handle mapping of RTF character sets
+ * onto standard characters.
+ *
+ * RTFStdCharCode(name) given char name, produce numeric code
+ * RTFStdCharName(code) given char code, return name
+ * RTFMapChar(c) map input (RTF) char code to std code
+ * RTFSetCharSet(id) select given charset map
+ * RTFGetCharSet() get current charset map
+ *
+ * See ../h/README for more information about charset names and codes.
+ */
+
+
+/*
+ * Initialize charset stuff.
+ */
+
+static void CharSetInit(RTF_Info *info)
+{
+ TRACE("\n");
+
+ info->autoCharSetFlags = (rtfReadCharSet | rtfSwitchCharSet);
+ RTFFree (info->genCharSetFile);
+ info->genCharSetFile = (char *) NULL;
+ info->haveGenCharSet = 0;
+ RTFFree (info->symCharSetFile);
+ info->symCharSetFile = (char *) NULL;
+ info->haveSymCharSet = 0;
+ info->curCharSet = rtfCSGeneral;
+ info->curCharCode = info->genCharCode;
+}
+
+
+/*
+ * Specify the name of a file to be read when auto-charset-file reading is
+ * done.
+ */
+
+void RTFSetCharSetMap (RTF_Info *info, char *name, int csId)
+{
+ TRACE("\n");
+
+ if ((name = RTFStrSave (name)) == (char *) NULL) /* make copy */
+ RTFPanic (info,"RTFSetCharSetMap: out of memory");
+ switch (csId)
+ {
+ case rtfCSGeneral:
+ RTFFree (info->genCharSetFile); /* free any previous value */
+ info->genCharSetFile = name;
+ break;
+ case rtfCSSymbol:
+ RTFFree (info->symCharSetFile); /* free any previous value */
+ info->symCharSetFile = name;
+ break;
+ }
+}
+
+
+/*
+ * Do auto-charset-file reading.
+ * will always use the ansi charset no mater what the value
+ * of the rtfTextBuf is.
+ *
+ * TODO: add support for other charset in the future.
+ *
+ */
+
+static void ReadCharSetMaps(RTF_Info *info)
+{
+ char buf[rtfBufSiz];
+
+ TRACE("\n");
+
+ if (info->genCharSetFile != (char *) NULL)
+ (void) strcpy (buf, info->genCharSetFile);
+ else
+ sprintf (buf, "%s-gen", &info->rtfTextBuf[1]);
+ if (RTFReadCharSetMap (info, rtfCSGeneral) == 0)
+ RTFPanic (info,"ReadCharSetMaps: Cannot read charset map %s", buf);
+ if (info->symCharSetFile != (char *) NULL)
+ (void) strcpy (buf, info->symCharSetFile);
+ else
+ sprintf (buf, "%s-sym", &info->rtfTextBuf[1]);
+ if (RTFReadCharSetMap (info, rtfCSSymbol) == 0)
+ RTFPanic (info,"ReadCharSetMaps: Cannot read charset map %s", buf);
+}
+
+
+
+/*
+ * Convert a CharSetMap (character_name, character) into
+ * this form : array[character_ident] = character;
+ */
+
+int RTFReadCharSetMap(RTF_Info *info, int csId)
+{
+ int *stdCodeArray;
+ unsigned int i;
+
+ TRACE("\n");
+
+ switch (csId)
+ {
+ default:
+ return (0); /* illegal charset id */
+ case rtfCSGeneral:
+
+ info->haveGenCharSet = 1;
+ stdCodeArray = info->genCharCode;
+ for (i = 0; i < charSetSize; i++)
+ {
+ stdCodeArray[i] = rtfSC_nothing;
+ }
+
+ for ( i = 0 ; i< sizeof(ansi_gen)/(sizeof(int));i+=2)
+ {
+ stdCodeArray[ ansi_gen[i+1] ] = ansi_gen[i];
+ }
+ break;
+
+ case rtfCSSymbol:
+
+ info->haveSymCharSet = 1;
+ stdCodeArray = info->symCharCode;
+ for (i = 0; i < charSetSize; i++)
+ {
+ stdCodeArray[i] = rtfSC_nothing;
+ }
+
+ for ( i = 0 ; i< sizeof(ansi_sym)/(sizeof(int));i+=2)
+ {
+ stdCodeArray[ ansi_sym[i+1] ] = ansi_sym[i];
+ }
+ break;
+ }
+
+ return (1);
+}
+
+
+/*
+ * Given a standard character name (a string), find its code (a number).
+ * Return -1 if name is unknown.
+ */
+
+int RTFStdCharCode(RTF_Info *info, const char *name)
+{
+ int i;
+
+ TRACE("\n");
+
+ for (i = 0; i < rtfSC_MaxChar; i++)
+ {
+ if (strcmp (name, stdCharName[i]) == 0)
+ return (i);
+ }
+ return (-1);
+}
+
+
+/*
+ * Given a standard character code (a number), find its name (a string).
+ * Return NULL if code is unknown.
+ */
+
+const char *RTFStdCharName(RTF_Info *info, int code)
+{
+ if (code < 0 || code >= rtfSC_MaxChar)
+ return ((char *) NULL);
+ return (stdCharName[code]);
+}
+
+
+/*
+ * Given an RTF input character code, find standard character code.
+ * The translator should read the appropriate charset maps when it finds a
+ * charset control. However, the file might not contain one. In this
+ * case, no map will be available. When the first attempt is made to
+ * map a character under these circumstances, RTFMapChar() assumes ANSI
+ * and reads the map as necessary.
+ */
+
+int RTFMapChar(RTF_Info *info, int c)
+{
+ TRACE("\n");
+
+ switch (info->curCharSet)
+ {
+ case rtfCSGeneral:
+ if (!info->haveGenCharSet)
+ {
+ if (RTFReadCharSetMap (info, rtfCSGeneral) == 0)
+ RTFPanic (info,"RTFMapChar: cannot read ansi-gen");
+ }
+ break;
+ case rtfCSSymbol:
+ if (!info->haveSymCharSet)
+ {
+ if (RTFReadCharSetMap (info, rtfCSSymbol) == 0)
+ RTFPanic (info,"RTFMapChar: cannot read ansi-sym");
+ }
+ break;
+ }
+ if (c < 0 || c >= charSetSize)
+ return (rtfSC_nothing);
+ return (info->curCharCode[c]);
+}
+
+
+/*
+ * Set the current character set. If csId is illegal, uses general charset.
+ */
+
+void RTFSetCharSet(RTF_Info *info, int csId)
+{
+ TRACE("\n");
+
+ switch (csId)
+ {
+ default: /* use general if csId unknown */
+ case rtfCSGeneral:
+ info->curCharCode = info->genCharCode;
+ info->curCharSet = csId;
+ break;
+ case rtfCSSymbol:
+ info->curCharCode = info->symCharCode;
+ info->curCharSet = csId;
+ break;
+ }
+}
+
+
+int RTFGetCharSet(RTF_Info *info)
+{
+ return (info->curCharSet);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Special destination readers. They gobble the destination so the
+ * writer doesn't have to deal with them. That's wrong for any
+ * translator that wants to process any of these itself. In that
+ * case, these readers should be overridden by installing a different
+ * destination callback.
+ *
+ * NOTE: The last token read by each of these reader will be the
+ * destination's terminating '}', which will then be the current token.
+ * That '}' token is passed to RTFRouteToken() - the writer has already
+ * seen the '{' that began the destination group, and may have pushed a
+ * state; it also needs to know at the end of the group that a state
+ * should be popped.
+ *
+ * It's important that rtf.h and the control token lookup table list
+ * as many symbols as possible, because these destination readers
+ * unfortunately make strict assumptions about the input they expect,
+ * and a token of class rtfUnknown will throw them off easily.
+ */
+
+
+/*
+ * Read { \fonttbl ... } destination. Old font tables don't have
+ * braces around each table entry; try to adjust for that.
+ */
+
+static void ReadFontTbl(RTF_Info *info)
+{
+ RTFFont *fp = NULL;
+ char buf[rtfBufSiz], *bp;
+ int old = -1;
+ const char *fn = "ReadFontTbl";
+
+ TRACE("\n");
+
+ for (;;)
+ {
+ (void) RTFGetToken (info);
+ if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
+ break;
+ if (old < 0) /* first entry - determine tbl type */
+ {
+ if (RTFCheckCMM (info, rtfControl, rtfCharAttr, rtfFontNum))
+ old = 1; /* no brace */
+ else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
+ old = 0; /* brace */
+ else /* can't tell! */
+ RTFPanic (info, "%s: Cannot determine format", fn);
+ }
+ if (old == 0) /* need to find "{" here */
+ {
+ if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
+ RTFPanic (info, "%s: missing \"{\"", fn);
+ (void) RTFGetToken (info); /* yes, skip to next token */
+ }
+ if ((fp = New (RTFFont)) == (RTFFont *) NULL)
+ RTFPanic (info, "%s: cannot allocate font entry", fn);
+
+ fp->rtfNextFont = info->fontList;
+ info->fontList = fp;
+
+ fp->rtfFName = (char *) NULL;
+ fp->rtfFAltName = (char *) NULL;
+ fp->rtfFNum = -1;
+ fp->rtfFFamily = 0;
+ fp->rtfFCharSet = 0;
+ fp->rtfFPitch = 0;
+ fp->rtfFType = 0;
+ fp->rtfFCodePage = 0;
+
+ while (info->rtfClass != rtfEOF
+ && !RTFCheckCM (info, rtfText, ';')
+ && !RTFCheckCM (info, rtfGroup, rtfEndGroup))
+ {
+ if (info->rtfClass == rtfControl)
+ {
+ switch (info->rtfMajor)
+ {
+ default:
+ /* ignore token but announce it */
+ RTFMsg (info,"%s: unknown token \"%s\"\n",
+ fn, info->rtfTextBuf);
+ break;
+ case rtfFontFamily:
+ fp->rtfFFamily = info->rtfMinor;
+ break;
+ case rtfCharAttr:
+ switch (info->rtfMinor)
+ {
+ default:
+ break; /* ignore unknown? */
+ case rtfFontNum:
+ fp->rtfFNum = info->rtfParam;
+ break;
+ }
+ break;
+ case rtfFontAttr:
+ switch (info->rtfMinor)
+ {
+ default:
+ break; /* ignore unknown? */
+ case rtfFontCharSet:
+ fp->rtfFCharSet = info->rtfParam;
+ break;
+ case rtfFontPitch:
+ fp->rtfFPitch = info->rtfParam;
+ break;
+ case rtfFontCodePage:
+ fp->rtfFCodePage = info->rtfParam;
+ break;
+ case rtfFTypeNil:
+ case rtfFTypeTrueType:
+ fp->rtfFType = info->rtfParam;
+ break;
+ }
+ break;
+ }
+ }
+ else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup)) /* dest */
+ {
+ RTFSkipGroup (info); /* ignore for now */
+ }
+ else if (info->rtfClass == rtfText) /* font name */
+ {
+ bp = buf;
+ while (info->rtfClass == rtfText
+ && !RTFCheckCM (info, rtfText, ';'))
+ {
+ *bp++ = info->rtfMajor;
+ (void) RTFGetToken (info);
+ }
+
+ /* FIX: in some cases the <fontinfo> isn't finished with a semi-column */
+ if(RTFCheckCM (info, rtfGroup, rtfEndGroup))
+ {
+ RTFUngetToken (info);
+ }
+ *bp = '\0';
+ fp->rtfFName = RTFStrSave (buf);
+ if (fp->rtfFName == (char *) NULL)
+ RTFPanic (info, "%s: cannot allocate font name", fn);
+ /* already have next token; don't read one */
+ /* at bottom of loop */
+ continue;
+ }
+ else
+ {
+ /* ignore token but announce it */
+ RTFMsg (info, "%s: unknown token \"%s\"\n",
+ fn,info->rtfTextBuf);
+ }
+ (void) RTFGetToken (info);
+ }
+ if (old == 0) /* need to see "}" here */
+ {
+ (void) RTFGetToken (info);
+ if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
+ RTFPanic (info, "%s: missing \"}\"", fn);
+ }
+ }
+ if (fp->rtfFNum == -1)
+ RTFPanic (info,"%s: missing font number", fn);
+/*
+ * Could check other pieces of structure here, too, I suppose.
+ */
+ RTFRouteToken (info); /* feed "}" back to router */
+}
+
+
+/*
+ * The color table entries have color values of -1 if
+ * the default color should be used for the entry (only
+ * a semi-colon is given in the definition, no color values).
+ * There will be a problem if a partial entry (1 or 2 but
+ * not 3 color values) is given. The possibility is ignored
+ * here.
+ */
+
+static void ReadColorTbl(RTF_Info *info)
+{
+ RTFColor *cp;
+ int cnum = 0;
+ const char *fn = "ReadColorTbl";
+
+ TRACE("\n");
+
+ for (;;)
+ {
+ (void) RTFGetToken (info);
+ if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
+ break;
+ if ((cp = New (RTFColor)) == (RTFColor *) NULL)
+ RTFPanic (info,"%s: cannot allocate color entry", fn);
+ cp->rtfCNum = cnum++;
+ cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = -1;
+ cp->rtfNextColor = info->colorList;
+ info->colorList = cp;
+ while (RTFCheckCM (info, rtfControl, rtfColorName))
+ {
+ switch (info->rtfMinor)
+ {
+ case rtfRed: cp->rtfCRed = info->rtfParam; break;
+ case rtfGreen: cp->rtfCGreen = info->rtfParam; break;
+ case rtfBlue: cp->rtfCBlue = info->rtfParam; break;
+ }
+ RTFGetToken (info);
+ }
+ if (!RTFCheckCM (info, rtfText, (int) ';'))
+ RTFPanic (info,"%s: malformed entry", fn);
+ }
+ RTFRouteToken (info); /* feed "}" back to router */
+}
+
+
+/*
+ * The "Normal" style definition doesn't contain any style number,
+ * all others do. Normal style is given style rtfNormalStyleNum.
+ */
+
+static void ReadStyleSheet(RTF_Info *info)
+{
+ RTFStyle *sp;
+ RTFStyleElt *sep, *sepLast;
+ char buf[rtfBufSiz], *bp;
+ const char *fn = "ReadStyleSheet";
+
+ TRACE("\n");
+
+ for (;;)
+ {
+ (void) RTFGetToken (info);
+ if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
+ break;
+ if ((sp = New (RTFStyle)) == (RTFStyle *) NULL)
+ RTFPanic (info,"%s: cannot allocate stylesheet entry", fn);
+ sp->rtfSName = (char *) NULL;
+ sp->rtfSNum = -1;
+ sp->rtfSType = rtfParStyle;
+ sp->rtfSAdditive = 0;
+ sp->rtfSBasedOn = rtfNoStyleNum;
+ sp->rtfSNextPar = -1;
+ sp->rtfSSEList = sepLast = (RTFStyleElt *) NULL;
+ sp->rtfNextStyle = info->styleList;
+ sp->rtfExpanding = 0;
+ info->styleList = sp;
+ if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
+ RTFPanic (info,"%s: missing \"{\"", fn);
+ for (;;)
+ {
+ (void) RTFGetToken (info);
+ if (info->rtfClass == rtfEOF
+ || RTFCheckCM (info, rtfText, ';'))
+ break;
+ if (info->rtfClass == rtfControl)
+ {
+ if (RTFCheckMM (info, rtfSpecialChar, rtfOptDest))
+ continue; /* ignore "\*" */
+ if (RTFCheckMM (info, rtfParAttr, rtfStyleNum))
+ {
+ sp->rtfSNum = info->rtfParam;
+ sp->rtfSType = rtfParStyle;
+ continue;
+ }
+ if (RTFCheckMM (info, rtfCharAttr, rtfCharStyleNum))
+ {
+ sp->rtfSNum = info->rtfParam;
+ sp->rtfSType = rtfCharStyle;
+ continue;
+ }
+ if (RTFCheckMM (info, rtfSectAttr, rtfSectStyleNum))
+ {
+ sp->rtfSNum = info->rtfParam;
+ sp->rtfSType = rtfSectStyle;
+ continue;
+ }
+ if (RTFCheckMM (info, rtfStyleAttr, rtfBasedOn))
+ {
+ sp->rtfSBasedOn = info->rtfParam;
+ continue;
+ }
+ if (RTFCheckMM (info, rtfStyleAttr, rtfAdditive))
+ {
+ sp->rtfSAdditive = 1;
+ continue;
+ }
+ if (RTFCheckMM (info, rtfStyleAttr, rtfNext))
+ {
+ sp->rtfSNextPar = info->rtfParam;
+ continue;
+ }
+ if ((sep = New (RTFStyleElt)) == (RTFStyleElt *) NULL)
+ RTFPanic (info,"%s: cannot allocate style element", fn);
+ sep->rtfSEClass = info->rtfClass;
+ sep->rtfSEMajor = info->rtfMajor;
+ sep->rtfSEMinor = info->rtfMinor;
+ sep->rtfSEParam = info->rtfParam;
+ if ((sep->rtfSEText = RTFStrSave (info->rtfTextBuf))
+ == (char *) NULL)
+ RTFPanic (info,"%s: cannot allocate style element text", fn);
+ if (sepLast == (RTFStyleElt *) NULL)
+ sp->rtfSSEList = sep; /* first element */
+ else /* add to end */
+ sepLast->rtfNextSE = sep;
+ sep->rtfNextSE = (RTFStyleElt *) NULL;
+ sepLast = sep;
+ }
+ else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
+ {
+ /*
+ * This passes over "{\*\keycode ... }, among
+ * other things. A temporary (perhaps) hack.
+ */
+ RTFSkipGroup (info);
+ continue;
+ }
+ else if (info->rtfClass == rtfText) /* style name */
+ {
+ bp = buf;
+ while (info->rtfClass == rtfText)
+ {
+ if (info->rtfMajor == ';')
+ {
+ /* put back for "for" loop */
+ (void) RTFUngetToken (info);
+ break;
+ }
+ *bp++ = info->rtfMajor;
+ (void) RTFGetToken (info);
+ }
+ *bp = '\0';
+ if ((sp->rtfSName = RTFStrSave (buf)) == (char *) NULL)
+ RTFPanic (info, "%s: cannot allocate style name", fn);
+ }
+ else /* unrecognized */
+ {
+ /* ignore token but announce it */
+ RTFMsg (info, "%s: unknown token \"%s\"\n",
+ fn, info->rtfTextBuf);
+ }
+ }
+ (void) RTFGetToken (info);
+ if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
+ RTFPanic (info, "%s: missing \"}\"", fn);
+
+ /*
+ * Check over the style structure. A name is a must.
+ * If no style number was specified, check whether it's the
+ * Normal style (in which case it's given style number
+ * rtfNormalStyleNum). Note that some "normal" style names
+ * just begin with "Normal" and can have other stuff following,
+ * e.g., "Normal,Times 10 point". Ugh.
+ *
+ * Some German RTF writers use "Standard" instead of "Normal".
+ */
+ if (sp->rtfSName == (char *) NULL)
+ RTFPanic (info,"%s: missing style name", fn);
+ if (sp->rtfSNum < 0)
+ {
+ if (strncmp (buf, "Normal", 6) != 0
+ && strncmp (buf, "Standard", 8) != 0)
+ RTFPanic (info,"%s: missing style number", fn);
+ sp->rtfSNum = rtfNormalStyleNum;
+ }
+ if (sp->rtfSNextPar == -1) /* if \snext not given, */
+ sp->rtfSNextPar = sp->rtfSNum; /* next is itself */
+ }
+ RTFRouteToken (info); /* feed "}" back to router */
+}
+
+
+static void ReadInfoGroup(RTF_Info *info)
+{
+ RTFSkipGroup (info);
+ RTFRouteToken (info); /* feed "}" back to router */
+}
+
+
+static void ReadPictGroup(RTF_Info *info)
+{
+ RTFSkipGroup (info);
+ RTFRouteToken (info); /* feed "}" back to router */
+}
+
+
+static void ReadObjGroup(RTF_Info *info)
+{
+ RTFSkipGroup (info);
+ RTFRouteToken (info); /* feed "}" back to router */
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Routines to return pieces of stylesheet, or font or color tables.
+ * References to style 0 are mapped onto the Normal style.
+ */
+
+
+RTFStyle *RTFGetStyle(RTF_Info *info, int num)
+{
+ RTFStyle *s;
+
+ if (num == -1)
+ return (info->styleList);
+ for (s = info->styleList; s != (RTFStyle *) NULL; s = s->rtfNextStyle)
+ {
+ if (s->rtfSNum == num)
+ break;
+ }
+ return (s); /* NULL if not found */
+}
+
+
+RTFFont *RTFGetFont(RTF_Info *info, int num)
+{
+ RTFFont *f;
+
+ if (num == -1)
+ return (info->fontList);
+ for (f = info->fontList; f != (RTFFont *) NULL; f = f->rtfNextFont)
+ {
+ if (f->rtfFNum == num)
+ break;
+ }
+ return (f); /* NULL if not found */
+}
+
+
+RTFColor *RTFGetColor(RTF_Info *info, int num)
+{
+ RTFColor *c;
+
+ if (num == -1)
+ return (info->colorList);
+ for (c = info->colorList; c != (RTFColor *) NULL; c = c->rtfNextColor)
+ {
+ if (c->rtfCNum == num)
+ break;
+ }
+ return (c); /* NULL if not found */
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/*
+ * Expand style n, if there is such a style.
+ */
+
+void RTFExpandStyle(RTF_Info *info, int n)
+{
+ RTFStyle *s;
+ RTFStyleElt *se;
+
+ TRACE("\n");
+
+ if (n == -1 || (s = RTFGetStyle (info, n)) == (RTFStyle *) NULL)
+ return;
+ if (s->rtfExpanding != 0)
+ RTFPanic (info,"Style expansion loop, style %d", n);
+ s->rtfExpanding = 1; /* set expansion flag for loop detection */
+ /*
+ * Expand "based-on" style (unless it's the same as the current
+ * style -- Normal style usually gives itself as its own based-on
+ * style). Based-on style expansion is done by synthesizing
+ * the token that the writer needs to see in order to trigger
+ * another style expansion, and feeding to token back through
+ * the router so the writer sees it.
+ */
+ if (n != s->rtfSBasedOn)
+ {
+ RTFSetToken (info, rtfControl, rtfParAttr, rtfStyleNum,
+ s->rtfSBasedOn, "\\s");
+ RTFRouteToken (info);
+ }
+ /*
+ * Now route the tokens unique to this style. RTFSetToken()
+ * isn't used because it would add the param value to the end
+ * of the token text, which already has it in.
+ */
+ for (se = s->rtfSSEList; se != (RTFStyleElt *) NULL; se = se->rtfNextSE)
+ {
+ info->rtfClass = se->rtfSEClass;
+ info->rtfMajor = se->rtfSEMajor;
+ info->rtfMinor = se->rtfSEMinor;
+ info->rtfParam = se->rtfSEParam;
+ (void) strcpy (info->rtfTextBuf, se->rtfSEText);
+ info->rtfTextLen = strlen (info->rtfTextBuf);
+ RTFRouteToken (info);
+ }
+ s->rtfExpanding = 0; /* done - clear expansion flag */
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Control symbol lookup routines
+ */
+
+
+typedef struct RTFKey RTFKey;
+
+struct RTFKey
+{
+ int rtfKMajor; /* major number */
+ int rtfKMinor; /* minor number */
+ const char *rtfKStr; /* symbol name */
+ int rtfKHash; /* symbol name hash value */
+};
+
+/*
+ * A minor number of -1 means the token has no minor number
+ * (all valid minor numbers are >= 0).
+ */
+
+static RTFKey rtfKey[] =
+{
+ /*
+ * Special characters
+ */
+
+ { rtfSpecialChar, rtfIIntVersion, "vern", 0 },
+ { rtfSpecialChar, rtfICreateTime, "creatim", 0 },
+ { rtfSpecialChar, rtfIRevisionTime, "revtim", 0 },
+ { rtfSpecialChar, rtfIPrintTime, "printim", 0 },
+ { rtfSpecialChar, rtfIBackupTime, "buptim", 0 },
+ { rtfSpecialChar, rtfIEditTime, "edmins", 0 },
+ { rtfSpecialChar, rtfIYear, "yr", 0 },
+ { rtfSpecialChar, rtfIMonth, "mo", 0 },
+ { rtfSpecialChar, rtfIDay, "dy", 0 },
+ { rtfSpecialChar, rtfIHour, "hr", 0 },
+ { rtfSpecialChar, rtfIMinute, "min", 0 },
+ { rtfSpecialChar, rtfISecond, "sec", 0 },
+ { rtfSpecialChar, rtfINPages, "nofpages", 0 },
+ { rtfSpecialChar, rtfINWords, "nofwords", 0 },
+ { rtfSpecialChar, rtfINChars, "nofchars", 0 },
+ { rtfSpecialChar, rtfIIntID, "id", 0 },
+
+ { rtfSpecialChar, rtfCurHeadDate, "chdate", 0 },
+ { rtfSpecialChar, rtfCurHeadDateLong, "chdpl", 0 },
+ { rtfSpecialChar, rtfCurHeadDateAbbrev, "chdpa", 0 },
+ { rtfSpecialChar, rtfCurHeadTime, "chtime", 0 },
+ { rtfSpecialChar, rtfCurHeadPage, "chpgn", 0 },
+ { rtfSpecialChar, rtfSectNum, "sectnum", 0 },
+ { rtfSpecialChar, rtfCurFNote, "chftn", 0 },
+ { rtfSpecialChar, rtfCurAnnotRef, "chatn", 0 },
+ { rtfSpecialChar, rtfFNoteSep, "chftnsep", 0 },
+ { rtfSpecialChar, rtfFNoteCont, "chftnsepc", 0 },
+ { rtfSpecialChar, rtfCell, "cell", 0 },
+ { rtfSpecialChar, rtfRow, "row", 0 },
+ { rtfSpecialChar, rtfPar, "par", 0 },
+ /* newline and carriage return are synonyms for */
+ /* \par when they are preceded by a \ character */
+ { rtfSpecialChar, rtfPar, "\n", 0 },
+ { rtfSpecialChar, rtfPar, "\r", 0 },
+ { rtfSpecialChar, rtfSect, "sect", 0 },
+ { rtfSpecialChar, rtfPage, "page", 0 },
+ { rtfSpecialChar, rtfColumn, "column", 0 },
+ { rtfSpecialChar, rtfLine, "line", 0 },
+ { rtfSpecialChar, rtfSoftPage, "softpage", 0 },
+ { rtfSpecialChar, rtfSoftColumn, "softcol", 0 },
+ { rtfSpecialChar, rtfSoftLine, "softline", 0 },
+ { rtfSpecialChar, rtfSoftLineHt, "softlheight", 0 },
+ { rtfSpecialChar, rtfTab, "tab", 0 },
+ { rtfSpecialChar, rtfEmDash, "emdash", 0 },
+ { rtfSpecialChar, rtfEnDash, "endash", 0 },
+ { rtfSpecialChar, rtfEmSpace, "emspace", 0 },
+ { rtfSpecialChar, rtfEnSpace, "enspace", 0 },
+ { rtfSpecialChar, rtfBullet, "bullet", 0 },
+ { rtfSpecialChar, rtfLQuote, "lquote", 0 },
+ { rtfSpecialChar, rtfRQuote, "rquote", 0 },
+ { rtfSpecialChar, rtfLDblQuote, "ldblquote", 0 },
+ { rtfSpecialChar, rtfRDblQuote, "rdblquote", 0 },
+ { rtfSpecialChar, rtfFormula, "|", 0 },
+ { rtfSpecialChar, rtfNoBrkSpace, "~", 0 },
+ { rtfSpecialChar, rtfNoReqHyphen, "-", 0 },
+ { rtfSpecialChar, rtfNoBrkHyphen, "_", 0 },
+ { rtfSpecialChar, rtfOptDest, "*", 0 },
+ { rtfSpecialChar, rtfLTRMark, "ltrmark", 0 },
+ { rtfSpecialChar, rtfRTLMark, "rtlmark", 0 },
+ { rtfSpecialChar, rtfNoWidthJoiner, "zwj", 0 },
+ { rtfSpecialChar, rtfNoWidthNonJoiner, "zwnj", 0 },
+ /* is this valid? */
+ { rtfSpecialChar, rtfCurHeadPict, "chpict", 0 },
+
+ /*
+ * Character formatting attributes
+ */
+
+ { rtfCharAttr, rtfPlain, "plain", 0 },
+ { rtfCharAttr, rtfBold, "b", 0 },
+ { rtfCharAttr, rtfAllCaps, "caps", 0 },
+ { rtfCharAttr, rtfDeleted, "deleted", 0 },
+ { rtfCharAttr, rtfSubScript, "dn", 0 },
+ { rtfCharAttr, rtfSubScrShrink, "sub", 0 },
+ { rtfCharAttr, rtfNoSuperSub, "nosupersub", 0 },
+ { rtfCharAttr, rtfExpand, "expnd", 0 },
+ { rtfCharAttr, rtfExpandTwips, "expndtw", 0 },
+ { rtfCharAttr, rtfKerning, "kerning", 0 },
+ { rtfCharAttr, rtfFontNum, "f", 0 },
+ { rtfCharAttr, rtfFontSize, "fs", 0 },
+ { rtfCharAttr, rtfItalic, "i", 0 },
+ { rtfCharAttr, rtfOutline, "outl", 0 },
+ { rtfCharAttr, rtfRevised, "revised", 0 },
+ { rtfCharAttr, rtfRevAuthor, "revauth", 0 },
+ { rtfCharAttr, rtfRevDTTM, "revdttm", 0 },
+ { rtfCharAttr, rtfSmallCaps, "scaps", 0 },
+ { rtfCharAttr, rtfShadow, "shad", 0 },
+ { rtfCharAttr, rtfStrikeThru, "strike", 0 },
+ { rtfCharAttr, rtfUnderline, "ul", 0 },
+ { rtfCharAttr, rtfDotUnderline, "uld", 0 },
+ { rtfCharAttr, rtfDbUnderline, "uldb", 0 },
+ { rtfCharAttr, rtfNoUnderline, "ulnone", 0 },
+ { rtfCharAttr, rtfWordUnderline, "ulw", 0 },
+ { rtfCharAttr, rtfSuperScript, "up", 0 },
+ { rtfCharAttr, rtfSuperScrShrink, "super", 0 },
+ { rtfCharAttr, rtfInvisible, "v", 0 },
+ { rtfCharAttr, rtfForeColor, "cf", 0 },
+ { rtfCharAttr, rtfBackColor, "cb", 0 },
+ { rtfCharAttr, rtfRTLChar, "rtlch", 0 },
+ { rtfCharAttr, rtfLTRChar, "ltrch", 0 },
+ { rtfCharAttr, rtfCharStyleNum, "cs", 0 },
+ { rtfCharAttr, rtfCharCharSet, "cchs", 0 },
+ { rtfCharAttr, rtfLanguage, "lang", 0 },
+ /* this has disappeared from spec 1.2 */
+ { rtfCharAttr, rtfGray, "gray", 0 },
+
+ /*
+ * Paragraph formatting attributes
+ */
+
+ { rtfParAttr, rtfParDef, "pard", 0 },
+ { rtfParAttr, rtfStyleNum, "s", 0 },
+ { rtfParAttr, rtfHyphenate, "hyphpar", 0 },
+ { rtfParAttr, rtfInTable, "intbl", 0 },
+ { rtfParAttr, rtfKeep, "keep", 0 },
+ { rtfParAttr, rtfNoWidowControl, "nowidctlpar", 0 },
+ { rtfParAttr, rtfKeepNext, "keepn", 0 },
+ { rtfParAttr, rtfOutlineLevel, "level", 0 },
+ { rtfParAttr, rtfNoLineNum, "noline", 0 },
+ { rtfParAttr, rtfPBBefore, "pagebb", 0 },
+ { rtfParAttr, rtfSideBySide, "sbys", 0 },
+ { rtfParAttr, rtfQuadLeft, "ql", 0 },
+ { rtfParAttr, rtfQuadRight, "qr", 0 },
+ { rtfParAttr, rtfQuadJust, "qj", 0 },
+ { rtfParAttr, rtfQuadCenter, "qc", 0 },
+ { rtfParAttr, rtfFirstIndent, "fi", 0 },
+ { rtfParAttr, rtfLeftIndent, "li", 0 },
+ { rtfParAttr, rtfRightIndent, "ri", 0 },
+ { rtfParAttr, rtfSpaceBefore, "sb", 0 },
+ { rtfParAttr, rtfSpaceAfter, "sa", 0 },
+ { rtfParAttr, rtfSpaceBetween, "sl", 0 },
+ { rtfParAttr, rtfSpaceMultiply, "slmult", 0 },
+
+ { rtfParAttr, rtfSubDocument, "subdocument", 0 },
+
+ { rtfParAttr, rtfRTLPar, "rtlpar", 0 },
+ { rtfParAttr, rtfLTRPar, "ltrpar", 0 },
+
+ { rtfParAttr, rtfTabPos, "tx", 0 },
+ /*
+ * FrameMaker writes \tql (to mean left-justified tab, apparently)
+ * although it's not in the spec. It's also redundant, since lj
+ * tabs are the default.
+ */
+ { rtfParAttr, rtfTabLeft, "tql", 0 },
+ { rtfParAttr, rtfTabRight, "tqr", 0 },
+ { rtfParAttr, rtfTabCenter, "tqc", 0 },
+ { rtfParAttr, rtfTabDecimal, "tqdec", 0 },
+ { rtfParAttr, rtfTabBar, "tb", 0 },
+ { rtfParAttr, rtfLeaderDot, "tldot", 0 },
+ { rtfParAttr, rtfLeaderHyphen, "tlhyph", 0 },
+ { rtfParAttr, rtfLeaderUnder, "tlul", 0 },
+ { rtfParAttr, rtfLeaderThick, "tlth", 0 },
+ { rtfParAttr, rtfLeaderEqual, "tleq", 0 },
+
+ { rtfParAttr, rtfParLevel, "pnlvl", 0 },
+ { rtfParAttr, rtfParBullet, "pnlvlblt", 0 },
+ { rtfParAttr, rtfParSimple, "pnlvlbody", 0 },
+ { rtfParAttr, rtfParNumCont, "pnlvlcont", 0 },
+ { rtfParAttr, rtfParNumOnce, "pnnumonce", 0 },
+ { rtfParAttr, rtfParNumAcross, "pnacross", 0 },
+ { rtfParAttr, rtfParHangIndent, "pnhang", 0 },
+ { rtfParAttr, rtfParNumRestart, "pnrestart", 0 },
+ { rtfParAttr, rtfParNumCardinal, "pncard", 0 },
+ { rtfParAttr, rtfParNumDecimal, "pndec", 0 },
+ { rtfParAttr, rtfParNumULetter, "pnucltr", 0 },
+ { rtfParAttr, rtfParNumURoman, "pnucrm", 0 },
+ { rtfParAttr, rtfParNumLLetter, "pnlcltr", 0 },
+ { rtfParAttr, rtfParNumLRoman, "pnlcrm", 0 },
+ { rtfParAttr, rtfParNumOrdinal, "pnord", 0 },
+ { rtfParAttr, rtfParNumOrdinalText, "pnordt", 0 },
+ { rtfParAttr, rtfParNumBold, "pnb", 0 },
+ { rtfParAttr, rtfParNumItalic, "pni", 0 },
+ { rtfParAttr, rtfParNumAllCaps, "pncaps", 0 },
+ { rtfParAttr, rtfParNumSmallCaps, "pnscaps", 0 },
+ { rtfParAttr, rtfParNumUnder, "pnul", 0 },
+ { rtfParAttr, rtfParNumDotUnder, "pnuld", 0 },
+ { rtfParAttr, rtfParNumDbUnder, "pnuldb", 0 },
+ { rtfParAttr, rtfParNumNoUnder, "pnulnone", 0 },
+ { rtfParAttr, rtfParNumWordUnder, "pnulw", 0 },
+ { rtfParAttr, rtfParNumStrikethru, "pnstrike", 0 },
+ { rtfParAttr, rtfParNumForeColor, "pncf", 0 },
+ { rtfParAttr, rtfParNumFont, "pnf", 0 },
+ { rtfParAttr, rtfParNumFontSize, "pnfs", 0 },
+ { rtfParAttr, rtfParNumIndent, "pnindent", 0 },
+ { rtfParAttr, rtfParNumSpacing, "pnsp", 0 },
+ { rtfParAttr, rtfParNumInclPrev, "pnprev", 0 },
+ { rtfParAttr, rtfParNumCenter, "pnqc", 0 },
+ { rtfParAttr, rtfParNumLeft, "pnql", 0 },
+ { rtfParAttr, rtfParNumRight, "pnqr", 0 },
+ { rtfParAttr, rtfParNumStartAt, "pnstart", 0 },
+
+ { rtfParAttr, rtfBorderTop, "brdrt", 0 },
+ { rtfParAttr, rtfBorderBottom, "brdrb", 0 },
+ { rtfParAttr, rtfBorderLeft, "brdrl", 0 },
+ { rtfParAttr, rtfBorderRight, "brdrr", 0 },
+ { rtfParAttr, rtfBorderBetween, "brdrbtw", 0 },
+ { rtfParAttr, rtfBorderBar, "brdrbar", 0 },
+ { rtfParAttr, rtfBorderBox, "box", 0 },
+ { rtfParAttr, rtfBorderSingle, "brdrs", 0 },
+ { rtfParAttr, rtfBorderThick, "brdrth", 0 },
+ { rtfParAttr, rtfBorderShadow, "brdrsh", 0 },
+ { rtfParAttr, rtfBorderDouble, "brdrdb", 0 },
+ { rtfParAttr, rtfBorderDot, "brdrdot", 0 },
+ { rtfParAttr, rtfBorderDot, "brdrdash", 0 },
+ { rtfParAttr, rtfBorderHair, "brdrhair", 0 },
+ { rtfParAttr, rtfBorderWidth, "brdrw", 0 },
+ { rtfParAttr, rtfBorderColor, "brdrcf", 0 },
+ { rtfParAttr, rtfBorderSpace, "brsp", 0 },
+
+ { rtfParAttr, rtfShading, "shading", 0 },
+ { rtfParAttr, rtfBgPatH, "bghoriz", 0 },
+ { rtfParAttr, rtfBgPatV, "bgvert", 0 },
+ { rtfParAttr, rtfFwdDiagBgPat, "bgfdiag", 0 },
+ { rtfParAttr, rtfBwdDiagBgPat, "bgbdiag", 0 },
+ { rtfParAttr, rtfHatchBgPat, "bgcross", 0 },
+ { rtfParAttr, rtfDiagHatchBgPat, "bgdcross", 0 },
+ { rtfParAttr, rtfDarkBgPatH, "bgdkhoriz", 0 },
+ { rtfParAttr, rtfDarkBgPatV, "bgdkvert", 0 },
+ { rtfParAttr, rtfFwdDarkBgPat, "bgdkfdiag", 0 },
+ { rtfParAttr, rtfBwdDarkBgPat, "bgdkbdiag", 0 },
+ { rtfParAttr, rtfDarkHatchBgPat, "bgdkcross", 0 },
+ { rtfParAttr, rtfDarkDiagHatchBgPat, "bgdkdcross", 0 },
+ { rtfParAttr, rtfBgPatLineColor, "cfpat", 0 },
+ { rtfParAttr, rtfBgPatColor, "cbpat", 0 },
+
+ /*
+ * Section formatting attributes
+ */
+
+ { rtfSectAttr, rtfSectDef, "sectd", 0 },
+ { rtfSectAttr, rtfENoteHere, "endnhere", 0 },
+ { rtfSectAttr, rtfPrtBinFirst, "binfsxn", 0 },
+ { rtfSectAttr, rtfPrtBin, "binsxn", 0 },
+ { rtfSectAttr, rtfSectStyleNum, "ds", 0 },
+
+ { rtfSectAttr, rtfNoBreak, "sbknone", 0 },
+ { rtfSectAttr, rtfColBreak, "sbkcol", 0 },
+ { rtfSectAttr, rtfPageBreak, "sbkpage", 0 },
+ { rtfSectAttr, rtfEvenBreak, "sbkeven", 0 },
+ { rtfSectAttr, rtfOddBreak, "sbkodd", 0 },
+
+ { rtfSectAttr, rtfColumns, "cols", 0 },
+ { rtfSectAttr, rtfColumnSpace, "colsx", 0 },
+ { rtfSectAttr, rtfColumnNumber, "colno", 0 },
+ { rtfSectAttr, rtfColumnSpRight, "colsr", 0 },
+ { rtfSectAttr, rtfColumnWidth, "colw", 0 },
+ { rtfSectAttr, rtfColumnLine, "linebetcol", 0 },
+
+ { rtfSectAttr, rtfLineModulus, "linemod", 0 },
+ { rtfSectAttr, rtfLineDist, "linex", 0 },
+ { rtfSectAttr, rtfLineStarts, "linestarts", 0 },
+ { rtfSectAttr, rtfLineRestart, "linerestart", 0 },
+ { rtfSectAttr, rtfLineRestartPg, "lineppage", 0 },
+ { rtfSectAttr, rtfLineCont, "linecont", 0 },
+
+ { rtfSectAttr, rtfSectPageWid, "pgwsxn", 0 },
+ { rtfSectAttr, rtfSectPageHt, "pghsxn", 0 },
+ { rtfSectAttr, rtfSectMarginLeft, "marglsxn", 0 },
+ { rtfSectAttr, rtfSectMarginRight, "margrsxn", 0 },
+ { rtfSectAttr, rtfSectMarginTop, "margtsxn", 0 },
+ { rtfSectAttr, rtfSectMarginBottom, "margbsxn", 0 },
+ { rtfSectAttr, rtfSectMarginGutter, "guttersxn", 0 },
+ { rtfSectAttr, rtfSectLandscape, "lndscpsxn", 0 },
+ { rtfSectAttr, rtfTitleSpecial, "titlepg", 0 },
+ { rtfSectAttr, rtfHeaderY, "headery", 0 },
+ { rtfSectAttr, rtfFooterY, "footery", 0 },
+
+ { rtfSectAttr, rtfPageStarts, "pgnstarts", 0 },
+ { rtfSectAttr, rtfPageCont, "pgncont", 0 },
+ { rtfSectAttr, rtfPageRestart, "pgnrestart", 0 },
+ { rtfSectAttr, rtfPageNumRight, "pgnx", 0 },
+ { rtfSectAttr, rtfPageNumTop, "pgny", 0 },
+ { rtfSectAttr, rtfPageDecimal, "pgndec", 0 },
+ { rtfSectAttr, rtfPageURoman, "pgnucrm", 0 },
+ { rtfSectAttr, rtfPageLRoman, "pgnlcrm", 0 },
+ { rtfSectAttr, rtfPageULetter, "pgnucltr", 0 },
+ { rtfSectAttr, rtfPageLLetter, "pgnlcltr", 0 },
+ { rtfSectAttr, rtfPageNumHyphSep, "pgnhnsh", 0 },
+ { rtfSectAttr, rtfPageNumSpaceSep, "pgnhnsp", 0 },
+ { rtfSectAttr, rtfPageNumColonSep, "pgnhnsc", 0 },
+ { rtfSectAttr, rtfPageNumEmdashSep, "pgnhnsm", 0 },
+ { rtfSectAttr, rtfPageNumEndashSep, "pgnhnsn", 0 },
+
+ { rtfSectAttr, rtfTopVAlign, "vertalt", 0 },
+ /* misspelled as "vertal" in specification 1.0 */
+ { rtfSectAttr, rtfBottomVAlign, "vertalb", 0 },
+ { rtfSectAttr, rtfCenterVAlign, "vertalc", 0 },
+ { rtfSectAttr, rtfJustVAlign, "vertalj", 0 },
+
+ { rtfSectAttr, rtfRTLSect, "rtlsect", 0 },
+ { rtfSectAttr, rtfLTRSect, "ltrsect", 0 },
+
+ /* I've seen these in an old spec, but not in real files... */
+ /*rtfSectAttr, rtfNoBreak, "nobreak", 0,*/
+ /*rtfSectAttr, rtfColBreak, "colbreak", 0,*/
+ /*rtfSectAttr, rtfPageBreak, "pagebreak", 0,*/
+ /*rtfSectAttr, rtfEvenBreak, "evenbreak", 0,*/
+ /*rtfSectAttr, rtfOddBreak, "oddbreak", 0,*/
+
+ /*
+ * Document formatting attributes
+ */
+
+ { rtfDocAttr, rtfDefTab, "deftab", 0 },
+ { rtfDocAttr, rtfHyphHotZone, "hyphhotz", 0 },
+ { rtfDocAttr, rtfHyphConsecLines, "hyphconsec", 0 },
+ { rtfDocAttr, rtfHyphCaps, "hyphcaps", 0 },
+ { rtfDocAttr, rtfHyphAuto, "hyphauto", 0 },
+ { rtfDocAttr, rtfLineStart, "linestart", 0 },
+ { rtfDocAttr, rtfFracWidth, "fracwidth", 0 },
+ /* \makeback was given in old version of spec, it's now */
+ /* listed as \makebackup */
+ { rtfDocAttr, rtfMakeBackup, "makeback", 0 },
+ { rtfDocAttr, rtfMakeBackup, "makebackup", 0 },
+ { rtfDocAttr, rtfRTFDefault, "defformat", 0 },
+ { rtfDocAttr, rtfPSOverlay, "psover", 0 },
+ { rtfDocAttr, rtfDocTemplate, "doctemp", 0 },
+ { rtfDocAttr, rtfDefLanguage, "deflang", 0 },
+
+ { rtfDocAttr, rtfFENoteType, "fet", 0 },
+ { rtfDocAttr, rtfFNoteEndSect, "endnotes", 0 },
+ { rtfDocAttr, rtfFNoteEndDoc, "enddoc", 0 },
+ { rtfDocAttr, rtfFNoteText, "ftntj", 0 },
+ { rtfDocAttr, rtfFNoteBottom, "ftnbj", 0 },
+ { rtfDocAttr, rtfENoteEndSect, "aendnotes", 0 },
+ { rtfDocAttr, rtfENoteEndDoc, "aenddoc", 0 },
+ { rtfDocAttr, rtfENoteText, "aftntj", 0 },
+ { rtfDocAttr, rtfENoteBottom, "aftnbj", 0 },
+ { rtfDocAttr, rtfFNoteStart, "ftnstart", 0 },
+ { rtfDocAttr, rtfENoteStart, "aftnstart", 0 },
+ { rtfDocAttr, rtfFNoteRestartPage, "ftnrstpg", 0 },
+ { rtfDocAttr, rtfFNoteRestart, "ftnrestart", 0 },
+ { rtfDocAttr, rtfFNoteRestartCont, "ftnrstcont", 0 },
+ { rtfDocAttr, rtfENoteRestart, "aftnrestart", 0 },
+ { rtfDocAttr, rtfENoteRestartCont, "aftnrstcont", 0 },
+ { rtfDocAttr, rtfFNoteNumArabic, "ftnnar", 0 },
+ { rtfDocAttr, rtfFNoteNumLLetter, "ftnnalc", 0 },
+ { rtfDocAttr, rtfFNoteNumULetter, "ftnnauc", 0 },
+ { rtfDocAttr, rtfFNoteNumLRoman, "ftnnrlc", 0 },
+ { rtfDocAttr, rtfFNoteNumURoman, "ftnnruc", 0 },
+ { rtfDocAttr, rtfFNoteNumChicago, "ftnnchi", 0 },
+ { rtfDocAttr, rtfENoteNumArabic, "aftnnar", 0 },
+ { rtfDocAttr, rtfENoteNumLLetter, "aftnnalc", 0 },
+ { rtfDocAttr, rtfENoteNumULetter, "aftnnauc", 0 },
+ { rtfDocAttr, rtfENoteNumLRoman, "aftnnrlc", 0 },
+ { rtfDocAttr, rtfENoteNumURoman, "aftnnruc", 0 },
+ { rtfDocAttr, rtfENoteNumChicago, "aftnnchi", 0 },
+
+ { rtfDocAttr, rtfPaperWidth, "paperw", 0 },
+ { rtfDocAttr, rtfPaperHeight, "paperh", 0 },
+ { rtfDocAttr, rtfPaperSize, "psz", 0 },
+ { rtfDocAttr, rtfLeftMargin, "margl", 0 },
+ { rtfDocAttr, rtfRightMargin, "margr", 0 },
+ { rtfDocAttr, rtfTopMargin, "margt", 0 },
+ { rtfDocAttr, rtfBottomMargin, "margb", 0 },
+ { rtfDocAttr, rtfFacingPage, "facingp", 0 },
+ { rtfDocAttr, rtfGutterWid, "gutter", 0 },
+ { rtfDocAttr, rtfMirrorMargin, "margmirror", 0 },
+ { rtfDocAttr, rtfLandscape, "landscape", 0 },
+ { rtfDocAttr, rtfPageStart, "pgnstart", 0 },
+ { rtfDocAttr, rtfWidowCtrl, "widowctrl", 0 },
+
+ { rtfDocAttr, rtfLinkStyles, "linkstyles", 0 },
+
+ { rtfDocAttr, rtfNoAutoTabIndent, "notabind", 0 },
+ { rtfDocAttr, rtfWrapSpaces, "wraptrsp", 0 },
+ { rtfDocAttr, rtfPrintColorsBlack, "prcolbl", 0 },
+ { rtfDocAttr, rtfNoExtraSpaceRL, "noextrasprl", 0 },
+ { rtfDocAttr, rtfNoColumnBalance, "nocolbal", 0 },
+ { rtfDocAttr, rtfCvtMailMergeQuote, "cvmme", 0 },
+ { rtfDocAttr, rtfSuppressTopSpace, "sprstsp", 0 },
+ { rtfDocAttr, rtfSuppressPreParSpace, "sprsspbf", 0 },
+ { rtfDocAttr, rtfCombineTblBorders, "otblrul", 0 },
+ { rtfDocAttr, rtfTranspMetafiles, "transmf", 0 },
+ { rtfDocAttr, rtfSwapBorders, "swpbdr", 0 },
+ { rtfDocAttr, rtfShowHardBreaks, "brkfrm", 0 },
+
+ { rtfDocAttr, rtfFormProtected, "formprot", 0 },
+ { rtfDocAttr, rtfAllProtected, "allprot", 0 },
+ { rtfDocAttr, rtfFormShading, "formshade", 0 },
+ { rtfDocAttr, rtfFormDisplay, "formdisp", 0 },
+ { rtfDocAttr, rtfPrintData, "printdata", 0 },
+
+ { rtfDocAttr, rtfRevProtected, "revprot", 0 },
+ { rtfDocAttr, rtfRevisions, "revisions", 0 },
+ { rtfDocAttr, rtfRevDisplay, "revprop", 0 },
+ { rtfDocAttr, rtfRevBar, "revbar", 0 },
+
+ { rtfDocAttr, rtfAnnotProtected, "annotprot", 0 },
+
+ { rtfDocAttr, rtfRTLDoc, "rtldoc", 0 },
+ { rtfDocAttr, rtfLTRDoc, "ltrdoc", 0 },
+
+ /*
+ * Style attributes
+ */
+
+ { rtfStyleAttr, rtfAdditive, "additive", 0 },
+ { rtfStyleAttr, rtfBasedOn, "sbasedon", 0 },
+ { rtfStyleAttr, rtfNext, "snext", 0 },
+
+ /*
+ * Picture attributes
+ */
+
+ { rtfPictAttr, rtfMacQD, "macpict", 0 },
+ { rtfPictAttr, rtfPMMetafile, "pmmetafile", 0 },
+ { rtfPictAttr, rtfWinMetafile, "wmetafile", 0 },
+ { rtfPictAttr, rtfDevIndBitmap, "dibitmap", 0 },
+ { rtfPictAttr, rtfWinBitmap, "wbitmap", 0 },
+ { rtfPictAttr, rtfPixelBits, "wbmbitspixel", 0 },
+ { rtfPictAttr, rtfBitmapPlanes, "wbmplanes", 0 },
+ { rtfPictAttr, rtfBitmapWid, "wbmwidthbytes", 0 },
+
+ { rtfPictAttr, rtfPicWid, "picw", 0 },
+ { rtfPictAttr, rtfPicHt, "pich", 0 },
+ { rtfPictAttr, rtfPicGoalWid, "picwgoal", 0 },
+ { rtfPictAttr, rtfPicGoalHt, "pichgoal", 0 },
+ /* these two aren't in the spec, but some writers emit them */
+ { rtfPictAttr, rtfPicGoalWid, "picwGoal", 0 },
+ { rtfPictAttr, rtfPicGoalHt, "pichGoal", 0 },
+ { rtfPictAttr, rtfPicScaleX, "picscalex", 0 },
+ { rtfPictAttr, rtfPicScaleY, "picscaley", 0 },
+ { rtfPictAttr, rtfPicScaled, "picscaled", 0 },
+ { rtfPictAttr, rtfPicCropTop, "piccropt", 0 },
+ { rtfPictAttr, rtfPicCropBottom, "piccropb", 0 },
+ { rtfPictAttr, rtfPicCropLeft, "piccropl", 0 },
+ { rtfPictAttr, rtfPicCropRight, "piccropr", 0 },
+
+ { rtfPictAttr, rtfPicMFHasBitmap, "picbmp", 0 },
+ { rtfPictAttr, rtfPicMFBitsPerPixel, "picbpp", 0 },
+
+ { rtfPictAttr, rtfPicBinary, "bin", 0 },
+
+ /*
+ * NeXT graphic attributes
+ */
+
+ { rtfNeXTGrAttr, rtfNeXTGWidth, "width", 0 },
+ { rtfNeXTGrAttr, rtfNeXTGHeight, "height", 0 },
+
+ /*
+ * Destinations
+ */
+
+ { rtfDestination, rtfFontTbl, "fonttbl", 0 },
+ { rtfDestination, rtfFontAltName, "falt", 0 },
+ { rtfDestination, rtfEmbeddedFont, "fonteb", 0 },
+ { rtfDestination, rtfFontFile, "fontfile", 0 },
+ { rtfDestination, rtfFileTbl, "filetbl", 0 },
+ { rtfDestination, rtfFileInfo, "file", 0 },
+ { rtfDestination, rtfColorTbl, "colortbl", 0 },
+ { rtfDestination, rtfStyleSheet, "stylesheet", 0 },
+ { rtfDestination, rtfKeyCode, "keycode", 0 },
+ { rtfDestination, rtfRevisionTbl, "revtbl", 0 },
+ { rtfDestination, rtfInfo, "info", 0 },
+ { rtfDestination, rtfITitle, "title", 0 },
+ { rtfDestination, rtfISubject, "subject", 0 },
+ { rtfDestination, rtfIAuthor, "author", 0 },
+ { rtfDestination, rtfIOperator, "operator", 0 },
+ { rtfDestination, rtfIKeywords, "keywords", 0 },
+ { rtfDestination, rtfIComment, "comment", 0 },
+ { rtfDestination, rtfIVersion, "version", 0 },
+ { rtfDestination, rtfIDoccomm, "doccomm", 0 },
+ /* \verscomm may not exist -- was seen in earlier spec version */
+ { rtfDestination, rtfIVerscomm, "verscomm", 0 },
+ { rtfDestination, rtfNextFile, "nextfile", 0 },
+ { rtfDestination, rtfTemplate, "template", 0 },
+ { rtfDestination, rtfFNSep, "ftnsep", 0 },
+ { rtfDestination, rtfFNContSep, "ftnsepc", 0 },
+ { rtfDestination, rtfFNContNotice, "ftncn", 0 },
+ { rtfDestination, rtfENSep, "aftnsep", 0 },
+ { rtfDestination, rtfENContSep, "aftnsepc", 0 },
+ { rtfDestination, rtfENContNotice, "aftncn", 0 },
+ { rtfDestination, rtfPageNumLevel, "pgnhn", 0 },
+ { rtfDestination, rtfParNumLevelStyle, "pnseclvl", 0 },
+ { rtfDestination, rtfHeader, "header", 0 },
+ { rtfDestination, rtfFooter, "footer", 0 },
+ { rtfDestination, rtfHeaderLeft, "headerl", 0 },
+ { rtfDestination, rtfHeaderRight, "headerr", 0 },
+ { rtfDestination, rtfHeaderFirst, "headerf", 0 },
+ { rtfDestination, rtfFooterLeft, "footerl", 0 },
+ { rtfDestination, rtfFooterRight, "footerr", 0 },
+ { rtfDestination, rtfFooterFirst, "footerf", 0 },
+ { rtfDestination, rtfParNumText, "pntext", 0 },
+ { rtfDestination, rtfParNumbering, "pn", 0 },
+ { rtfDestination, rtfParNumTextAfter, "pntexta", 0 },
+ { rtfDestination, rtfParNumTextBefore, "pntextb", 0 },
+ { rtfDestination, rtfBookmarkStart, "bkmkstart", 0 },
+ { rtfDestination, rtfBookmarkEnd, "bkmkend", 0 },
+ { rtfDestination, rtfPict, "pict", 0 },
+ { rtfDestination, rtfObject, "object", 0 },
+ { rtfDestination, rtfObjClass, "objclass", 0 },
+ { rtfDestination, rtfObjName, "objname", 0 },
+ { rtfObjAttr, rtfObjTime, "objtime", 0 },
+ { rtfDestination, rtfObjData, "objdata", 0 },
+ { rtfDestination, rtfObjAlias, "objalias", 0 },
+ { rtfDestination, rtfObjSection, "objsect", 0 },
+ /* objitem and objtopic aren't documented in the spec! */
+ { rtfDestination, rtfObjItem, "objitem", 0 },
+ { rtfDestination, rtfObjTopic, "objtopic", 0 },
+ { rtfDestination, rtfObjResult, "result", 0 },
+ { rtfDestination, rtfDrawObject, "do", 0 },
+ { rtfDestination, rtfFootnote, "footnote", 0 },
+ { rtfDestination, rtfAnnotRefStart, "atrfstart", 0 },
+ { rtfDestination, rtfAnnotRefEnd, "atrfend", 0 },
+ { rtfDestination, rtfAnnotID, "atnid", 0 },
+ { rtfDestination, rtfAnnotAuthor, "atnauthor", 0 },
+ { rtfDestination, rtfAnnotation, "annotation", 0 },
+ { rtfDestination, rtfAnnotRef, "atnref", 0 },
+ { rtfDestination, rtfAnnotTime, "atntime", 0 },
+ { rtfDestination, rtfAnnotIcon, "atnicn", 0 },
+ { rtfDestination, rtfField, "field", 0 },
+ { rtfDestination, rtfFieldInst, "fldinst", 0 },
+ { rtfDestination, rtfFieldResult, "fldrslt", 0 },
+ { rtfDestination, rtfDataField, "datafield", 0 },
+ { rtfDestination, rtfIndex, "xe", 0 },
+ { rtfDestination, rtfIndexText, "txe", 0 },
+ { rtfDestination, rtfIndexRange, "rxe", 0 },
+ { rtfDestination, rtfTOC, "tc", 0 },
+ { rtfDestination, rtfNeXTGraphic, "NeXTGraphic", 0 },
+
+ /*
+ * Font families
+ */
+
+ { rtfFontFamily, rtfFFNil, "fnil", 0 },
+ { rtfFontFamily, rtfFFRoman, "froman", 0 },
+ { rtfFontFamily, rtfFFSwiss, "fswiss", 0 },
+ { rtfFontFamily, rtfFFModern, "fmodern", 0 },
+ { rtfFontFamily, rtfFFScript, "fscript", 0 },
+ { rtfFontFamily, rtfFFDecor, "fdecor", 0 },
+ { rtfFontFamily, rtfFFTech, "ftech", 0 },
+ { rtfFontFamily, rtfFFBidirectional, "fbidi", 0 },
+
+ /*
+ * Font attributes
+ */
+
+ { rtfFontAttr, rtfFontCharSet, "fcharset", 0 },
+ { rtfFontAttr, rtfFontPitch, "fprq", 0 },
+ { rtfFontAttr, rtfFontCodePage, "cpg", 0 },
+ { rtfFontAttr, rtfFTypeNil, "ftnil", 0 },
+ { rtfFontAttr, rtfFTypeTrueType, "fttruetype", 0 },
+
+ /*
+ * File table attributes
+ */
+
+ { rtfFileAttr, rtfFileNum, "fid", 0 },
+ { rtfFileAttr, rtfFileRelPath, "frelative", 0 },
+ { rtfFileAttr, rtfFileOSNum, "fosnum", 0 },
+
+ /*
+ * File sources
+ */
+
+ { rtfFileSource, rtfSrcMacintosh, "fvalidmac", 0 },
+ { rtfFileSource, rtfSrcDOS, "fvaliddos", 0 },
+ { rtfFileSource, rtfSrcNTFS, "fvalidntfs", 0 },
+ { rtfFileSource, rtfSrcHPFS, "fvalidhpfs", 0 },
+ { rtfFileSource, rtfSrcNetwork, "fnetwork", 0 },
+
+ /*
+ * Color names
+ */
+
+ { rtfColorName, rtfRed, "red", 0 },
+ { rtfColorName, rtfGreen, "green", 0 },
+ { rtfColorName, rtfBlue, "blue", 0 },
+
+ /*
+ * Charset names
+ */
+
+ { rtfCharSet, rtfMacCharSet, "mac", 0 },
+ { rtfCharSet, rtfAnsiCharSet, "ansi", 0 },
+ { rtfCharSet, rtfPcCharSet, "pc", 0 },
+ { rtfCharSet, rtfPcaCharSet, "pca", 0 },
+
+ /*
+ * Table attributes
+ */
+
+ { rtfTblAttr, rtfRowDef, "trowd", 0 },
+ { rtfTblAttr, rtfRowGapH, "trgaph", 0 },
+ { rtfTblAttr, rtfCellPos, "cellx", 0 },
+ { rtfTblAttr, rtfMergeRngFirst, "clmgf", 0 },
+ { rtfTblAttr, rtfMergePrevious, "clmrg", 0 },
+
+ { rtfTblAttr, rtfRowLeft, "trql", 0 },
+ { rtfTblAttr, rtfRowRight, "trqr", 0 },
+ { rtfTblAttr, rtfRowCenter, "trqc", 0 },
+ { rtfTblAttr, rtfRowLeftEdge, "trleft", 0 },
+ { rtfTblAttr, rtfRowHt, "trrh", 0 },
+ { rtfTblAttr, rtfRowHeader, "trhdr", 0 },
+ { rtfTblAttr, rtfRowKeep, "trkeep", 0 },
+
+ { rtfTblAttr, rtfRTLRow, "rtlrow", 0 },
+ { rtfTblAttr, rtfLTRRow, "ltrrow", 0 },
+
+ { rtfTblAttr, rtfRowBordTop, "trbrdrt", 0 },
+ { rtfTblAttr, rtfRowBordLeft, "trbrdrl", 0 },
+ { rtfTblAttr, rtfRowBordBottom, "trbrdrb", 0 },
+ { rtfTblAttr, rtfRowBordRight, "trbrdrr", 0 },
+ { rtfTblAttr, rtfRowBordHoriz, "trbrdrh", 0 },
+ { rtfTblAttr, rtfRowBordVert, "trbrdrv", 0 },
+
+ { rtfTblAttr, rtfCellBordBottom, "clbrdrb", 0 },
+ { rtfTblAttr, rtfCellBordTop, "clbrdrt", 0 },
+ { rtfTblAttr, rtfCellBordLeft, "clbrdrl", 0 },
+ { rtfTblAttr, rtfCellBordRight, "clbrdrr", 0 },
+
+ { rtfTblAttr, rtfCellShading, "clshdng", 0 },
+ { rtfTblAttr, rtfCellBgPatH, "clbghoriz", 0 },
+ { rtfTblAttr, rtfCellBgPatV, "clbgvert", 0 },
+ { rtfTblAttr, rtfCellFwdDiagBgPat, "clbgfdiag", 0 },
+ { rtfTblAttr, rtfCellBwdDiagBgPat, "clbgbdiag", 0 },
+ { rtfTblAttr, rtfCellHatchBgPat, "clbgcross", 0 },
+ { rtfTblAttr, rtfCellDiagHatchBgPat, "clbgdcross", 0 },
+ /*
+ * The spec lists "clbgdkhor", but the corresponding non-cell
+ * control is "bgdkhoriz". At any rate Macintosh Word seems
+ * to accept both "clbgdkhor" and "clbgdkhoriz".
+ */
+ { rtfTblAttr, rtfCellDarkBgPatH, "clbgdkhoriz", 0 },
+ { rtfTblAttr, rtfCellDarkBgPatH, "clbgdkhor", 0 },
+ { rtfTblAttr, rtfCellDarkBgPatV, "clbgdkvert", 0 },
+ { rtfTblAttr, rtfCellFwdDarkBgPat, "clbgdkfdiag", 0 },
+ { rtfTblAttr, rtfCellBwdDarkBgPat, "clbgdkbdiag", 0 },
+ { rtfTblAttr, rtfCellDarkHatchBgPat, "clbgdkcross", 0 },
+ { rtfTblAttr, rtfCellDarkDiagHatchBgPat, "clbgdkdcross", 0 },
+ { rtfTblAttr, rtfCellBgPatLineColor, "clcfpat", 0 },
+ { rtfTblAttr, rtfCellBgPatColor, "clcbpat", 0 },
+
+ /*
+ * Field attributes
+ */
+
+ { rtfFieldAttr, rtfFieldDirty, "flddirty", 0 },
+ { rtfFieldAttr, rtfFieldEdited, "fldedit", 0 },
+ { rtfFieldAttr, rtfFieldLocked, "fldlock", 0 },
+ { rtfFieldAttr, rtfFieldPrivate, "fldpriv", 0 },
+ { rtfFieldAttr, rtfFieldAlt, "fldalt", 0 },
+
+ /*
+ * Positioning attributes
+ */
+
+ { rtfPosAttr, rtfAbsWid, "absw", 0 },
+ { rtfPosAttr, rtfAbsHt, "absh", 0 },
+
+ { rtfPosAttr, rtfRPosMargH, "phmrg", 0 },
+ { rtfPosAttr, rtfRPosPageH, "phpg", 0 },
+ { rtfPosAttr, rtfRPosColH, "phcol", 0 },
+ { rtfPosAttr, rtfPosX, "posx", 0 },
+ { rtfPosAttr, rtfPosNegX, "posnegx", 0 },
+ { rtfPosAttr, rtfPosXCenter, "posxc", 0 },
+ { rtfPosAttr, rtfPosXInside, "posxi", 0 },
+ { rtfPosAttr, rtfPosXOutSide, "posxo", 0 },
+ { rtfPosAttr, rtfPosXRight, "posxr", 0 },
+ { rtfPosAttr, rtfPosXLeft, "posxl", 0 },
+
+ { rtfPosAttr, rtfRPosMargV, "pvmrg", 0 },
+ { rtfPosAttr, rtfRPosPageV, "pvpg", 0 },
+ { rtfPosAttr, rtfRPosParaV, "pvpara", 0 },
+ { rtfPosAttr, rtfPosY, "posy", 0 },
+ { rtfPosAttr, rtfPosNegY, "posnegy", 0 },
+ { rtfPosAttr, rtfPosYInline, "posyil", 0 },
+ { rtfPosAttr, rtfPosYTop, "posyt", 0 },
+ { rtfPosAttr, rtfPosYCenter, "posyc", 0 },
+ { rtfPosAttr, rtfPosYBottom, "posyb", 0 },
+
+ { rtfPosAttr, rtfNoWrap, "nowrap", 0 },
+ { rtfPosAttr, rtfDistFromTextAll, "dxfrtext", 0 },
+ { rtfPosAttr, rtfDistFromTextX, "dfrmtxtx", 0 },
+ { rtfPosAttr, rtfDistFromTextY, "dfrmtxty", 0 },
+ /* \dyfrtext no longer exists in spec 1.2, apparently */
+ /* replaced by \dfrmtextx and \dfrmtexty. */
+ { rtfPosAttr, rtfTextDistY, "dyfrtext", 0 },
+
+ { rtfPosAttr, rtfDropCapLines, "dropcapli", 0 },
+ { rtfPosAttr, rtfDropCapType, "dropcapt", 0 },
+
+ /*
+ * Object controls
+ */
+
+ { rtfObjAttr, rtfObjEmb, "objemb", 0 },
+ { rtfObjAttr, rtfObjLink, "objlink", 0 },
+ { rtfObjAttr, rtfObjAutoLink, "objautlink", 0 },
+ { rtfObjAttr, rtfObjSubscriber, "objsub", 0 },
+ { rtfObjAttr, rtfObjPublisher, "objpub", 0 },
+ { rtfObjAttr, rtfObjICEmb, "objicemb", 0 },
+
+ { rtfObjAttr, rtfObjLinkSelf, "linkself", 0 },
+ { rtfObjAttr, rtfObjLock, "objupdate", 0 },
+ { rtfObjAttr, rtfObjUpdate, "objlock", 0 },
+
+ { rtfObjAttr, rtfObjHt, "objh", 0 },
+ { rtfObjAttr, rtfObjWid, "objw", 0 },
+ { rtfObjAttr, rtfObjSetSize, "objsetsize", 0 },
+ { rtfObjAttr, rtfObjAlign, "objalign", 0 },
+ { rtfObjAttr, rtfObjTransposeY, "objtransy", 0 },
+ { rtfObjAttr, rtfObjCropTop, "objcropt", 0 },
+ { rtfObjAttr, rtfObjCropBottom, "objcropb", 0 },
+ { rtfObjAttr, rtfObjCropLeft, "objcropl", 0 },
+ { rtfObjAttr, rtfObjCropRight, "objcropr", 0 },
+ { rtfObjAttr, rtfObjScaleX, "objscalex", 0 },
+ { rtfObjAttr, rtfObjScaleY, "objscaley", 0 },
+
+ { rtfObjAttr, rtfObjResRTF, "rsltrtf", 0 },
+ { rtfObjAttr, rtfObjResPict, "rsltpict", 0 },
+ { rtfObjAttr, rtfObjResBitmap, "rsltbmp", 0 },
+ { rtfObjAttr, rtfObjResText, "rslttxt", 0 },
+ { rtfObjAttr, rtfObjResMerge, "rsltmerge", 0 },
+
+ { rtfObjAttr, rtfObjBookmarkPubObj, "bkmkpub", 0 },
+ { rtfObjAttr, rtfObjPubAutoUpdate, "pubauto", 0 },
+
+ /*
+ * Associated character formatting attributes
+ */
+
+ { rtfACharAttr, rtfACBold, "ab", 0 },
+ { rtfACharAttr, rtfACAllCaps, "caps", 0 },
+ { rtfACharAttr, rtfACForeColor, "acf", 0 },
+ { rtfACharAttr, rtfACSubScript, "adn", 0 },
+ { rtfACharAttr, rtfACExpand, "aexpnd", 0 },
+ { rtfACharAttr, rtfACFontNum, "af", 0 },
+ { rtfACharAttr, rtfACFontSize, "afs", 0 },
+ { rtfACharAttr, rtfACItalic, "ai", 0 },
+ { rtfACharAttr, rtfACLanguage, "alang", 0 },
+ { rtfACharAttr, rtfACOutline, "aoutl", 0 },
+ { rtfACharAttr, rtfACSmallCaps, "ascaps", 0 },
+ { rtfACharAttr, rtfACShadow, "ashad", 0 },
+ { rtfACharAttr, rtfACStrikeThru, "astrike", 0 },
+ { rtfACharAttr, rtfACUnderline, "aul", 0 },
+ { rtfACharAttr, rtfACDotUnderline, "auld", 0 },
+ { rtfACharAttr, rtfACDbUnderline, "auldb", 0 },
+ { rtfACharAttr, rtfACNoUnderline, "aulnone", 0 },
+ { rtfACharAttr, rtfACWordUnderline, "aulw", 0 },
+ { rtfACharAttr, rtfACSuperScript, "aup", 0 },
+
+ /*
+ * Footnote attributes
+ */
+
+ { rtfFNoteAttr, rtfFNAlt, "ftnalt", 0 },
+
+ /*
+ * Key code attributes
+ */
+
+ { rtfKeyCodeAttr, rtfAltKey, "alt", 0 },
+ { rtfKeyCodeAttr, rtfShiftKey, "shift", 0 },
+ { rtfKeyCodeAttr, rtfControlKey, "ctrl", 0 },
+ { rtfKeyCodeAttr, rtfFunctionKey, "fn", 0 },
+
+ /*
+ * Bookmark attributes
+ */
+
+ { rtfBookmarkAttr, rtfBookmarkFirstCol, "bkmkcolf", 0 },
+ { rtfBookmarkAttr, rtfBookmarkLastCol, "bkmkcoll", 0 },
+
+ /*
+ * Index entry attributes
+ */
+
+ { rtfIndexAttr, rtfIndexNumber, "xef", 0 },
+ { rtfIndexAttr, rtfIndexBold, "bxe", 0 },
+ { rtfIndexAttr, rtfIndexItalic, "ixe", 0 },
+
+ /*
+ * Table of contents attributes
+ */
+
+ { rtfTOCAttr, rtfTOCType, "tcf", 0 },
+ { rtfTOCAttr, rtfTOCLevel, "tcl", 0 },
+
+ /*
+ * Drawing object attributes
+ */
+
+ { rtfDrawAttr, rtfDrawLock, "dolock", 0 },
+ { rtfDrawAttr, rtfDrawPageRelX, "doxpage", 0 },
+ { rtfDrawAttr, rtfDrawColumnRelX, "dobxcolumn", 0 },
+ { rtfDrawAttr, rtfDrawMarginRelX, "dobxmargin", 0 },
+ { rtfDrawAttr, rtfDrawPageRelY, "dobypage", 0 },
+ { rtfDrawAttr, rtfDrawColumnRelY, "dobycolumn", 0 },
+ { rtfDrawAttr, rtfDrawMarginRelY, "dobymargin", 0 },
+ { rtfDrawAttr, rtfDrawHeight, "dobhgt", 0 },
+
+ { rtfDrawAttr, rtfDrawBeginGroup, "dpgroup", 0 },
+ { rtfDrawAttr, rtfDrawGroupCount, "dpcount", 0 },
+ { rtfDrawAttr, rtfDrawEndGroup, "dpendgroup", 0 },
+ { rtfDrawAttr, rtfDrawArc, "dparc", 0 },
+ { rtfDrawAttr, rtfDrawCallout, "dpcallout", 0 },
+ { rtfDrawAttr, rtfDrawEllipse, "dpellipse", 0 },
+ { rtfDrawAttr, rtfDrawLine, "dpline", 0 },
+ { rtfDrawAttr, rtfDrawPolygon, "dppolygon", 0 },
+ { rtfDrawAttr, rtfDrawPolyLine, "dppolyline", 0 },
+ { rtfDrawAttr, rtfDrawRect, "dprect", 0 },
+ { rtfDrawAttr, rtfDrawTextBox, "dptxbx", 0 },
+
+ { rtfDrawAttr, rtfDrawOffsetX, "dpx", 0 },
+ { rtfDrawAttr, rtfDrawSizeX, "dpxsize", 0 },
+ { rtfDrawAttr, rtfDrawOffsetY, "dpy", 0 },
+ { rtfDrawAttr, rtfDrawSizeY, "dpysize", 0 },
+
+ { rtfDrawAttr, rtfCOAngle, "dpcoa", 0 },
+ { rtfDrawAttr, rtfCOAccentBar, "dpcoaccent", 0 },
+ { rtfDrawAttr, rtfCOBestFit, "dpcobestfit", 0 },
+ { rtfDrawAttr, rtfCOBorder, "dpcoborder", 0 },
+ { rtfDrawAttr, rtfCOAttachAbsDist, "dpcodabs", 0 },
+ { rtfDrawAttr, rtfCOAttachBottom, "dpcodbottom", 0 },
+ { rtfDrawAttr, rtfCOAttachCenter, "dpcodcenter", 0 },
+ { rtfDrawAttr, rtfCOAttachTop, "dpcodtop", 0 },
+ { rtfDrawAttr, rtfCOLength, "dpcolength", 0 },
+ { rtfDrawAttr, rtfCONegXQuadrant, "dpcominusx", 0 },
+ { rtfDrawAttr, rtfCONegYQuadrant, "dpcominusy", 0 },
+ { rtfDrawAttr, rtfCOOffset, "dpcooffset", 0 },
+ { rtfDrawAttr, rtfCOAttachSmart, "dpcosmarta", 0 },
+ { rtfDrawAttr, rtfCODoubleLine, "dpcotdouble", 0 },
+ { rtfDrawAttr, rtfCORightAngle, "dpcotright", 0 },
+ { rtfDrawAttr, rtfCOSingleLine, "dpcotsingle", 0 },
+ { rtfDrawAttr, rtfCOTripleLine, "dpcottriple", 0 },
+
+ { rtfDrawAttr, rtfDrawTextBoxMargin, "dptxbxmar", 0 },
+ { rtfDrawAttr, rtfDrawTextBoxText, "dptxbxtext", 0 },
+ { rtfDrawAttr, rtfDrawRoundRect, "dproundr", 0 },
+
+ { rtfDrawAttr, rtfDrawPointX, "dpptx", 0 },
+ { rtfDrawAttr, rtfDrawPointY, "dppty", 0 },
+ { rtfDrawAttr, rtfDrawPolyCount, "dppolycount", 0 },
+
+ { rtfDrawAttr, rtfDrawArcFlipX, "dparcflipx", 0 },
+ { rtfDrawAttr, rtfDrawArcFlipY, "dparcflipy", 0 },
+
+ { rtfDrawAttr, rtfDrawLineBlue, "dplinecob", 0 },
+ { rtfDrawAttr, rtfDrawLineGreen, "dplinecog", 0 },
+ { rtfDrawAttr, rtfDrawLineRed, "dplinecor", 0 },
+ { rtfDrawAttr, rtfDrawLinePalette, "dplinepal", 0 },
+ { rtfDrawAttr, rtfDrawLineDashDot, "dplinedado", 0 },
+ { rtfDrawAttr, rtfDrawLineDashDotDot, "dplinedadodo", 0 },
+ { rtfDrawAttr, rtfDrawLineDash, "dplinedash", 0 },
+ { rtfDrawAttr, rtfDrawLineDot, "dplinedot", 0 },
+ { rtfDrawAttr, rtfDrawLineGray, "dplinegray", 0 },
+ { rtfDrawAttr, rtfDrawLineHollow, "dplinehollow", 0 },
+ { rtfDrawAttr, rtfDrawLineSolid, "dplinesolid", 0 },
+ { rtfDrawAttr, rtfDrawLineWidth, "dplinew", 0 },
+
+ { rtfDrawAttr, rtfDrawHollowEndArrow, "dpaendhol", 0 },
+ { rtfDrawAttr, rtfDrawEndArrowLength, "dpaendl", 0 },
+ { rtfDrawAttr, rtfDrawSolidEndArrow, "dpaendsol", 0 },
+ { rtfDrawAttr, rtfDrawEndArrowWidth, "dpaendw", 0 },
+ { rtfDrawAttr, rtfDrawHollowStartArrow,"dpastarthol", 0 },
+ { rtfDrawAttr, rtfDrawStartArrowLength,"dpastartl", 0 },
+ { rtfDrawAttr, rtfDrawSolidStartArrow, "dpastartsol", 0 },
+ { rtfDrawAttr, rtfDrawStartArrowWidth, "dpastartw", 0 },
+
+ { rtfDrawAttr, rtfDrawBgFillBlue, "dpfillbgcb", 0 },
+ { rtfDrawAttr, rtfDrawBgFillGreen, "dpfillbgcg", 0 },
+ { rtfDrawAttr, rtfDrawBgFillRed, "dpfillbgcr", 0 },
+ { rtfDrawAttr, rtfDrawBgFillPalette, "dpfillbgpal", 0 },
+ { rtfDrawAttr, rtfDrawBgFillGray, "dpfillbggray", 0 },
+ { rtfDrawAttr, rtfDrawFgFillBlue, "dpfillfgcb", 0 },
+ { rtfDrawAttr, rtfDrawFgFillGreen, "dpfillfgcg", 0 },
+ { rtfDrawAttr, rtfDrawFgFillRed, "dpfillfgcr", 0 },
+ { rtfDrawAttr, rtfDrawFgFillPalette, "dpfillfgpal", 0 },
+ { rtfDrawAttr, rtfDrawFgFillGray, "dpfillfggray", 0 },
+ { rtfDrawAttr, rtfDrawFillPatIndex, "dpfillpat", 0 },
+
+ { rtfDrawAttr, rtfDrawShadow, "dpshadow", 0 },
+ { rtfDrawAttr, rtfDrawShadowXOffset, "dpshadx", 0 },
+ { rtfDrawAttr, rtfDrawShadowYOffset, "dpshady", 0 },
+
+ { rtfVersion, -1, "rtf", 0 },
+ { rtfDefFont, -1, "deff", 0 },
+
+ { 0, -1, (char *) NULL, 0 }
+};
+
+
+/*
+ * Initialize lookup table hash values. Only need to do this once.
+ */
+
+static void LookupInit(void)
+{
+ static int inited = 0;
+ RTFKey *rp;
+
+ if (inited == 0)
+ {
+ for (rp = rtfKey; rp->rtfKStr != (char *) NULL; rp++)
+ rp->rtfKHash = Hash ((char*)rp->rtfKStr);
+ ++inited;
+ }
+}
+
+
+/*
+ * Determine major and minor number of control token. If it's
+ * not found, the class turns into rtfUnknown.
+ */
+
+static void Lookup(RTF_Info *info, char *s)
+{
+ RTFKey *rp;
+ int hash;
+
+ TRACE("\n");
+ ++s; /* skip over the leading \ character */
+ hash = Hash (s);
+ for (rp = rtfKey; rp->rtfKStr != (char *) NULL; rp++)
+ {
+ if (hash == rp->rtfKHash && strcmp (s, rp->rtfKStr) == 0)
+ {
+ info->rtfClass = rtfControl;
+ info->rtfMajor = rp->rtfKMajor;
+ info->rtfMinor = rp->rtfKMinor;
+ return;
+ }
+ }
+ info->rtfClass = rtfUnknown;
+}
+
+
+/*
+ * Compute hash value of symbol
+ */
+
+static int Hash(char *s)
+{
+ char c;
+ int val = 0;
+
+ while ((c = *s++) != '\0')
+ val += (int) c;
+ return (val);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Memory allocation routines
+ */
+
+
+/*
+ * Return pointer to block of size bytes, or NULL if there's
+ * not enough memory available.
+ *
+ * This is called through RTFAlloc(), a define which coerces the
+ * argument to int. This avoids the persistent problem of allocation
+ * failing under THINK C when a long is passed.
+ */
+
+char *_RTFAlloc(int size)
+{
+ return HeapAlloc(me_heap, 0, size);
+}
+
+
+/*
+ * Saves a string on the heap and returns a pointer to it.
+ */
+
+
+char *RTFStrSave(char *s)
+{
+ char *p;
+
+ if ((p = RTFAlloc ((int) (strlen (s) + 1))) == (char *) NULL)
+ return ((char *) NULL);
+ return (strcpy (p, s));
+}
+
+
+void RTFFree(char *p)
+{
+ HeapFree(me_heap, 0, p);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/*
+ * Token comparison routines
+ */
+
+int RTFCheckCM(RTF_Info *info, int class, int major)
+{
+ return (info->rtfClass == class && info->rtfMajor == major);
+}
+
+
+int RTFCheckCMM(RTF_Info *info, int class, int major, int minor)
+{
+ return (info->rtfClass == class && info->rtfMajor == major && info->rtfMinor == minor);
+}
+
+
+int RTFCheckMM(RTF_Info *info, int major, int minor)
+{
+ return (info->rtfMajor == major && info->rtfMinor == minor);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+int RTFCharToHex(char c)
+{
+ if (isupper (c))
+ c = tolower (c);
+ if (isdigit (c))
+ return (c - '0'); /* '0'..'9' */
+ return (c - 'a' + 10); /* 'a'..'f' */
+}
+
+
+int RTFHexToChar(int i)
+{
+ if (i < 10)
+ return (i + '0');
+ return (i - 10 + 'a');
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * RTFReadOutputMap() -- Read output translation map
+ */
+
+/*
+ * Read in an array describing the relation between the standard character set
+ * and an RTF translator's corresponding output sequences. Each line consists
+ * of a standard character name and the output sequence for that character.
+ *
+ * outMap is an array of strings into which the sequences should be placed.
+ * It should be declared like this in the calling program:
+ *
+ * char *outMap[rtfSC_MaxChar];
+ *
+ * reinit should be non-zero if outMap should be initialized
+ * zero otherwise.
+ *
+ */
+
+int RTFReadOutputMap(RTF_Info *info, char *outMap[], int reinit)
+{
+ unsigned int i;
+ int stdCode;
+
+ if (reinit)
+ {
+ for (i = 0; i < rtfSC_MaxChar; i++)
+ {
+ outMap[i] = (char *) NULL;
+ }
+ }
+
+ for (i=0 ;i< sizeof(text_map)/sizeof(char*); i+=2)
+ {
+ const char *name = text_map[i];
+ const char *seq = text_map[i+1];
+ stdCode = RTFStdCharCode( info, name );
+ outMap[stdCode] = (char*)seq;
+ }
+
+ return (1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Open a library file.
+ */
+
+
+void RTFSetOpenLibFileProc(RTF_Info *info, FILE *(*proc)())
+{
+ info->libFileOpen = proc;
+}
+
+
+FILE *RTFOpenLibFile (RTF_Info *info, char *file, char *mode)
+{
+ if (info->libFileOpen == NULL)
+ return ((FILE *) NULL);
+ return ((*info->libFileOpen) (file, mode));
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Print message. Default is to send message to stderr
+ * but this may be overridden with RTFSetMsgProc().
+ *
+ * Message should include linefeeds as necessary. If the default
+ * function is overridden, the overriding function may want to
+ * map linefeeds to another line ending character or sequence if
+ * the host system doesn't use linefeeds.
+ */
+
+
+void RTFMsg (RTF_Info *info, const char *fmt, ...)
+{
+ char buf[rtfBufSiz];
+
+ va_list args;
+ va_start (args,fmt);
+ vsprintf (buf, fmt, args);
+ va_end (args);
+ MESSAGE( "%s", buf);
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/*
+ * Process termination. Print error message and exit. Also prints
+ * current token, and current input line number and position within
+ * line if any input has been read from the current file. (No input
+ * has been read if prevChar is EOF).
+ */
+
+static void DefaultPanicProc(RTF_Info *info, char *s)
+{
+ MESSAGE( "%s", s);
+ /*exit (1);*/
+}
+
+
+
+void RTFPanic(RTF_Info *info, const char *fmt, ...)
+{
+ char buf[rtfBufSiz];
+
+ va_list args;
+ va_start (args,fmt);
+ vsprintf (buf, fmt, args);
+ va_end (args);
+ (void) strcat (buf, "\n");
+ if (info->prevChar != EOF && info->rtfTextBuf != (char *) NULL)
+ {
+ sprintf (buf + strlen (buf),
+ "Last token read was \"%s\" near line %ld, position %d.\n",
+ info->rtfTextBuf, info->rtfLineNum, info->rtfLinePos);
+ }
+ DefaultPanicProc(info, buf);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * originally from RTF tools' text-writer.c
+ *
+ * text-writer -- RTF-to-text translation writer code.
+ *
+ * Read RTF input, write text of document (text extraction).
+ */
+
+static void TextClass (RTF_Info *info);
+static void ControlClass (RTF_Info *info);
+static void Destination (RTF_Info *info);
+static void SpecialChar (RTF_Info *info);
+static void PutStdChar (RTF_Info *info, int stdCode);
+static void PutLitChar (RTF_Info *info, int c);
+static void PutLitStr (RTF_Info *info, char *s);
+
+/*
+ * Initialize the writer.
+ */
+
+void
+WriterInit (RTF_Info *info )
+{
+ RTFReadOutputMap (info, info->outMap,1);
+}
+
+
+int
+BeginFile (RTF_Info *info )
+{
+ /* install class callbacks */
+
+ RTFSetClassCallback (info, rtfText, TextClass);
+ RTFSetClassCallback (info, rtfControl, ControlClass);
+
+ return (1);
+}
+
+
+/*
+ * Write out a character. rtfMajor contains the input character, rtfMinor
+ * contains the corresponding standard character code.
+ *
+ * If the input character isn't in the charset map, try to print some
+ * representation of it.
+ */
+
+static void
+TextClass (RTF_Info *info)
+{
+ char buf[rtfBufSiz];
+
+ TRACE("\n");
+
+ if (info->rtfFormat == SF_TEXT)
+ PutLitChar (info, info->rtfMajor);
+ else if (info->rtfMinor != rtfSC_nothing)
+ PutStdChar (info, info->rtfMinor);
+ else
+ {
+ if (info->rtfMajor < 128) /* in ASCII range */
+ sprintf (buf, "[[%c]]", info->rtfMajor);
+ else
+ sprintf (buf, "[[\\'%02x]]", info->rtfMajor);
+ PutLitStr (info, buf);
+ }
+}
+
+
+static void
+ControlClass (RTF_Info *info)
+{
+ TRACE("\n");
+
+ switch (info->rtfMajor)
+ {
+ case rtfDestination:
+ Destination (info);
+ break;
+ case rtfSpecialChar:
+ SpecialChar (info);
+ break;
+ }
+}
+
+
+/*
+ * This function notices destinations that should be ignored
+ * and skips to their ends. This keeps, for instance, picture
+ * data from being considered as plain text.
+ */
+
+static void
+Destination (RTF_Info *info)
+{
+ TRACE("\n");
+
+ switch (info->rtfMinor)
+ {
+ case rtfPict:
+ case rtfFNContSep:
+ case rtfFNContNotice:
+ case rtfInfo:
+ case rtfIndexRange:
+ case rtfITitle:
+ case rtfISubject:
+ case rtfIAuthor:
+ case rtfIOperator:
+ case rtfIKeywords:
+ case rtfIComment:
+ case rtfIVersion:
+ case rtfIDoccomm:
+ RTFSkipGroup (info);
+ break;
+ }
+}
+
+
+/*
+ * The reason these use the rtfSC_xxx thingies instead of just writing
+ * out ' ', '-', '"', etc., is so that the mapping for these characters
+ * can be controlled by the text-map file.
+ */
+
+void SpecialChar (RTF_Info *info)
+{
+
+ TRACE("\n");
+
+ switch (info->rtfMinor)
+ {
+ case rtfPage:
+ case rtfSect:
+ case rtfRow:
+ case rtfLine:
+ case rtfPar:
+ PutLitChar (info, '\n');
+ break;
+ case rtfCell:
+ PutStdChar (info, rtfSC_space); /* make sure cells are separated */
+ break;
+ case rtfNoBrkSpace:
+ PutStdChar (info, rtfSC_nobrkspace);
+ break;
+ case rtfTab:
+ PutLitChar (info, '\t');
+ break;
+ case rtfNoBrkHyphen:
+ PutStdChar (info, rtfSC_nobrkhyphen);
+ break;
+ case rtfBullet:
+ PutStdChar (info, rtfSC_bullet);
+ break;
+ case rtfEmDash:
+ PutStdChar (info, rtfSC_emdash);
+ break;
+ case rtfEnDash:
+ PutStdChar (info, rtfSC_endash);
+ break;
+ case rtfLQuote:
+ PutStdChar (info, rtfSC_quoteleft);
+ break;
+ case rtfRQuote:
+ PutStdChar (info, rtfSC_quoteright);
+ break;
+ case rtfLDblQuote:
+ PutStdChar (info, rtfSC_quotedblleft);
+ break;
+ case rtfRDblQuote:
+ PutStdChar (info, rtfSC_quotedblright);
+ break;
+ }
+}
+
+
+/*
+ * Eventually this should keep track of the destination of the
+ * current state and only write text when in the initial state.
+ *
+ * If the output sequence is unspecified in the output map, write
+ * the character's standard name instead. This makes map deficiencies
+ * obvious and provides incentive to fix it. :-)
+ */
+
+void PutStdChar (RTF_Info *info, int stdCode)
+{
+
+ char *oStr = (char *) NULL;
+ char buf[rtfBufSiz];
+
+/* if (stdCode == rtfSC_nothing)
+ RTFPanic ("Unknown character code, logic error\n");
+*/
+ TRACE("\n");
+
+ oStr = info->outMap[stdCode];
+ if (oStr == (char *) NULL) /* no output sequence in map */
+ {
+ sprintf (buf, "[[%s]]", RTFStdCharName (info, stdCode));
+ oStr = buf;
+ }
+ PutLitStr (info, oStr);
+}
+
+void PutLitChar (RTF_Info *info, int c)
+{
+ if( info->dwOutputCount >= ( sizeof info->OutputBuffer - 1 ) )
+ RTFFlushOutputBuffer( info );
+ info->OutputBuffer[info->dwOutputCount++] = c;
+}
+
+void RTFFlushOutputBuffer( RTF_Info *info )
+{
+ info->OutputBuffer[info->dwOutputCount] = 0;
+ SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) info->OutputBuffer );
+ info->dwOutputCount = 0;
+}
+
+static void PutLitStr (RTF_Info *info, char *str )
+{
+ int len = strlen( str );
+
+ if( ( len + info->dwOutputCount + 1 ) > sizeof info->OutputBuffer )
+ RTFFlushOutputBuffer( info );
+ if( ( len + 1 ) >= sizeof info->OutputBuffer )
+ {
+ SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) str );
+ return;
+ }
+ strcpy( &info->OutputBuffer[info->dwOutputCount], str );
+ info->dwOutputCount += len;
+}
--- /dev/null
+2 extern IID_IRichEditOle
+3 extern IID_IRichEditOleCallback
+4 stdcall CreateTextServices(ptr ptr ptr)
+5 extern IID_ITextServices
+6 extern IID_ITextHost
+7 extern IID_ITextHost2
+8 stdcall REExtendedRegisterClass()
+9 stdcall RichEdit10ANSIWndProc(ptr long long long)
+10 stdcall RichEditANSIWndProc(ptr long long long)
--- /dev/null
+/*
+ * RichEdit GUIDs and OLE interface
+ *
+ * Copyright 2004 by Krzysztof Foltman
+ * Copyright 2004 Aric Stewart
+ *
+ * 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 <stdarg.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "ole2.h"
+#include "richole.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(richedit);
+
+typedef struct IRichEditOleImpl {
+ IRichEditOleVtbl *lpVtbl;
+ DWORD ref;
+} IRichEditOleImpl;
+
+/* there is no way to be consistent across different sets of headers - mingw, Wine, Win32 SDK*/
+
+/* FIXME: the next 6 lines should be in textserv.h */
+#define TEXTSERV_GUID(name, l, w1, w2, b1, b2) \
+ GUID name = { l, w1, w2, {b1, b2, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}}
+
+TEXTSERV_GUID(IID_ITextServices, 0x8d33f740, 0xcf58, 0x11ce, 0xa8, 0x9d);
+TEXTSERV_GUID(IID_ITextHost, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e);
+TEXTSERV_GUID(IID_ITextHost2, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e);
+
+static HRESULT WINAPI
+IRichEditOle_fnQueryInterface(IRichEditOle *me, REFIID riid, LPVOID *ppvObj)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+
+ TRACE("%p %s\n", This, debugstr_guid(riid) );
+
+ if (IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IRichEditOle))
+ {
+ IRichEditOle_AddRef(me);
+ *ppvObj = (LPVOID) This;
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI
+IRichEditOle_fnAddRef(IRichEditOle *me)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ ULONG ref = InterlockedIncrement( &This->ref );
+
+ TRACE("%p ref = %lu\n", This, ref);
+
+ return ref;
+}
+
+static ULONG WINAPI
+IRichEditOle_fnRelease(IRichEditOle *me)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ ULONG ref = InterlockedDecrement(&This->ref);
+
+ TRACE ("%p ref=%lu\n", This, ref);
+
+ if (!ref)
+ {
+ TRACE ("Destroying %p\n", This);
+ HeapFree(GetProcessHeap(),0,This);
+ }
+ return ref;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnActivateAs(IRichEditOle *me, REFCLSID rclsid, REFCLSID rclsidAs)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnContextSensitiveHelp(IRichEditOle *me, BOOL fEnterMode)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnConvertObject(IRichEditOle *me, LONG iob,
+ REFCLSID rclsidNew, LPCSTR lpstrUserTypeNew)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnGetClientSite(IRichEditOle *me,
+ LPOLECLIENTSITE *lplpolesite)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnGetClipboardData(IRichEditOle *me, CHARRANGE *lpchrg,
+ DWORD reco, LPDATAOBJECT *lplpdataobj)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static LONG WINAPI IRichEditOle_fnGetLinkCount(IRichEditOle *me)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnGetObject(IRichEditOle *me, LONG iob,
+ REOBJECT *lpreobject, DWORD dwFlags)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static LONG WINAPI
+IRichEditOle_fnGetObjectCount(IRichEditOle *me)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnHandsOffStorage(IRichEditOle *me, LONG iob)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnImportDataObject(IRichEditOle *me, LPDATAOBJECT lpdataobj,
+ CLIPFORMAT cf, HGLOBAL hMetaPict)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnInPlaceDeactivate(IRichEditOle *me)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnInsertObject(IRichEditOle *me, REOBJECT *lpreobject)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI IRichEditOle_fnSaveCompleted(IRichEditOle *me, LONG iob,
+ LPSTORAGE lpstg)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnSetDvaspect(IRichEditOle *me, LONG iob, DWORD dvaspect)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI IRichEditOle_fnSetHostNames(IRichEditOle *me,
+ LPCSTR lpstrContainerApp, LPCSTR lpstrContainerObj)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IRichEditOle_fnSetLinkAvailable(IRichEditOle *me, LONG iob, BOOL fAvailable)
+{
+ IRichEditOleImpl *This = (IRichEditOleImpl *)me;
+ FIXME("stub %p\n",This);
+ return E_NOTIMPL;
+}
+
+static IRichEditOleVtbl revt = {
+ IRichEditOle_fnQueryInterface,
+ IRichEditOle_fnAddRef,
+ IRichEditOle_fnRelease,
+ IRichEditOle_fnGetClientSite,
+ IRichEditOle_fnGetObjectCount,
+ IRichEditOle_fnGetLinkCount,
+ IRichEditOle_fnGetObject,
+ IRichEditOle_fnInsertObject,
+ IRichEditOle_fnConvertObject,
+ IRichEditOle_fnActivateAs,
+ IRichEditOle_fnSetHostNames,
+ IRichEditOle_fnSetLinkAvailable,
+ IRichEditOle_fnSetDvaspect,
+ IRichEditOle_fnHandsOffStorage,
+ IRichEditOle_fnSaveCompleted,
+ IRichEditOle_fnInPlaceDeactivate,
+ IRichEditOle_fnContextSensitiveHelp,
+ IRichEditOle_fnGetClipboardData,
+ IRichEditOle_fnImportDataObject
+};
+
+LRESULT CreateIRichEditOle(LPVOID *ppObj)
+{
+ IRichEditOleImpl *reo;
+
+ reo = HeapAlloc(GetProcessHeap(), 0, sizeof(IRichEditOleImpl));
+ if (!reo)
+ return 0;
+
+ reo->lpVtbl = &revt;
+ reo->ref = 1;
+ TRACE("Created %p\n",reo);
+ *ppObj = (LPVOID) reo;
+
+ return 1;
+}
--- /dev/null
+/*
+ * RichEdit - Operations on rows of text (rows are recreated during
+ * wrapping and are used for displaying the document, they don't keep any
+ * true document content; delete all rows, rewrap all paragraphs and
+ * you get them back).
+ *
+ * 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"
+
+ME_DisplayItem *ME_FindRowStart(ME_Context *c, ME_DisplayItem *item,
+ int nRelPos) {
+ ME_DisplayItem *para = ME_GetParagraph(item);
+ ME_MustBeWrapped(c, para);
+ if(nRelPos>=0) { /* if this or preceding row */
+ while(nRelPos<=0) {
+ ME_DisplayItem *item2 = ME_FindItemBack(item, diStartRowOrParagraph);
+ if (item2->type == diParagraph)
+ {
+ if (item2->member.para.prev_para == NULL)
+ return item;
+ /* if skipping to the preceding paragraph, ensure it's wrapped */
+ ME_MustBeWrapped(c, item2->member.para.prev_para);
+ item = item2;
+ continue;
+ }
+ else if (item2->type == diStartRow)
+ {
+ nRelPos++;
+ if (nRelPos>0)
+ return item;
+ item = item2;
+ continue;
+ }
+ assert(0 == "bug in FindItemBack(item, diStartRowOrParagraph)");
+ item = item2;
+ }
+ return item;
+ }
+ while(nRelPos>0) { /* if one of the next rows */
+ ME_DisplayItem *item2 = ME_FindItemFwd(item, diStartRowOrParagraph);
+ if (!item2)
+ return item;
+ if (item2->type == diParagraph)
+ {
+ if (item2->member.para.next_para == NULL)
+ return item;
+ continue;
+ }
+ item = item2;
+ nRelPos--;
+ }
+ return item;
+}
+
+/* I'm sure these functions would simplify some code in caret ops etc,
+ * I just didn't remember them when I wrote that code
+ */
+
+ME_DisplayItem *ME_RowStart(ME_DisplayItem *item) {
+ return ME_FindItemBackOrHere(item, diStartRow);
+}
+
+ME_DisplayItem *ME_RowEnd(ME_DisplayItem *item) {
+ ME_DisplayItem *item2 = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
+ if (!item2) return NULL;
+ return ME_FindItemBack(item, diRun);
+}
--- /dev/null
+#ifndef _RTF
+#define _RTF
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "richedit.h"
+
+
+/* The following defines are automatically generated. Do not edit. */
+
+
+/* These must be sequential beginning from zero */
+
+#define rtfSC_nothing 0
+#define rtfSC_space 1
+#define rtfSC_exclam 2
+#define rtfSC_quotedbl 3
+#define rtfSC_numbersign 4
+#define rtfSC_dollar 5
+#define rtfSC_percent 6
+#define rtfSC_ampersand 7
+#define rtfSC_quoteright 8
+#define rtfSC_parenleft 9
+#define rtfSC_parenright 10
+#define rtfSC_asterisk 11
+#define rtfSC_plus 12
+#define rtfSC_comma 13
+#define rtfSC_hyphen 14
+#define rtfSC_period 15
+#define rtfSC_slash 16
+#define rtfSC_zero 17
+#define rtfSC_one 18
+#define rtfSC_two 19
+#define rtfSC_three 20
+#define rtfSC_four 21
+#define rtfSC_five 22
+#define rtfSC_six 23
+#define rtfSC_seven 24
+#define rtfSC_eight 25
+#define rtfSC_nine 26
+#define rtfSC_colon 27
+#define rtfSC_semicolon 28
+#define rtfSC_less 29
+#define rtfSC_equal 30
+#define rtfSC_greater 31
+#define rtfSC_question 32
+#define rtfSC_at 33
+#define rtfSC_A 34
+#define rtfSC_B 35
+#define rtfSC_C 36
+#define rtfSC_D 37
+#define rtfSC_E 38
+#define rtfSC_F 39
+#define rtfSC_G 40
+#define rtfSC_H 41
+#define rtfSC_I 42
+#define rtfSC_J 43
+#define rtfSC_K 44
+#define rtfSC_L 45
+#define rtfSC_M 46
+#define rtfSC_N 47
+#define rtfSC_O 48
+#define rtfSC_P 49
+#define rtfSC_Q 50
+#define rtfSC_R 51
+#define rtfSC_S 52
+#define rtfSC_T 53
+#define rtfSC_U 54
+#define rtfSC_V 55
+#define rtfSC_W 56
+#define rtfSC_X 57
+#define rtfSC_Y 58
+#define rtfSC_Z 59
+#define rtfSC_bracketleft 60
+#define rtfSC_backslash 61
+#define rtfSC_bracketright 62
+#define rtfSC_asciicircum 63
+#define rtfSC_underscore 64
+#define rtfSC_quoteleft 65
+#define rtfSC_a 66
+#define rtfSC_b 67
+#define rtfSC_c 68
+#define rtfSC_d 69
+#define rtfSC_e 70
+#define rtfSC_f 71
+#define rtfSC_g 72
+#define rtfSC_h 73
+#define rtfSC_i 74
+#define rtfSC_j 75
+#define rtfSC_k 76
+#define rtfSC_l 77
+#define rtfSC_m 78
+#define rtfSC_n 79
+#define rtfSC_o 80
+#define rtfSC_p 81
+#define rtfSC_q 82
+#define rtfSC_r 83
+#define rtfSC_s 84
+#define rtfSC_t 85
+#define rtfSC_u 86
+#define rtfSC_v 87
+#define rtfSC_w 88
+#define rtfSC_x 89
+#define rtfSC_y 90
+#define rtfSC_z 91
+#define rtfSC_braceleft 92
+#define rtfSC_bar 93
+#define rtfSC_braceright 94
+#define rtfSC_asciitilde 95
+#define rtfSC_exclamdown 96
+#define rtfSC_cent 97
+#define rtfSC_sterling 98
+#define rtfSC_fraction 99
+#define rtfSC_yen 100
+#define rtfSC_florin 101
+#define rtfSC_section 102
+#define rtfSC_currency 103
+#define rtfSC_quotedblleft 104
+#define rtfSC_guillemotleft 105
+#define rtfSC_guilsinglleft 106
+#define rtfSC_guilsinglright 107
+#define rtfSC_fi 108
+#define rtfSC_fl 109
+#define rtfSC_endash 110
+#define rtfSC_dagger 111
+#define rtfSC_daggerdbl 112
+#define rtfSC_periodcentered 113
+#define rtfSC_paragraph 114
+#define rtfSC_bullet 115
+#define rtfSC_quotesinglbase 116
+#define rtfSC_quotedblbase 117
+#define rtfSC_quotedblright 118
+#define rtfSC_guillemotright 119
+#define rtfSC_ellipsis 120
+#define rtfSC_perthousand 121
+#define rtfSC_questiondown 122
+#define rtfSC_grave 123
+#define rtfSC_acute 124
+#define rtfSC_circumflex 125
+#define rtfSC_tilde 126
+#define rtfSC_macron 127
+#define rtfSC_breve 128
+#define rtfSC_dotaccent 129
+#define rtfSC_dieresis 130
+#define rtfSC_ring 131
+#define rtfSC_cedilla 132
+#define rtfSC_hungarumlaut 133
+#define rtfSC_ogonek 134
+#define rtfSC_caron 135
+#define rtfSC_emdash 136
+#define rtfSC_AE 137
+#define rtfSC_ordfeminine 138
+#define rtfSC_Lslash 139
+#define rtfSC_Oslash 140
+#define rtfSC_OE 141
+#define rtfSC_ordmasculine 142
+#define rtfSC_ae 143
+#define rtfSC_dotlessi 144
+#define rtfSC_lslash 145
+#define rtfSC_oslash 146
+#define rtfSC_oe 147
+#define rtfSC_germandbls 148
+#define rtfSC_Aacute 149
+#define rtfSC_Acircumflex 150
+#define rtfSC_Adieresis 151
+#define rtfSC_Agrave 152
+#define rtfSC_Aring 153
+#define rtfSC_Atilde 154
+#define rtfSC_Ccedilla 155
+#define rtfSC_Eacute 156
+#define rtfSC_Ecircumflex 157
+#define rtfSC_Edieresis 158
+#define rtfSC_Egrave 159
+#define rtfSC_Eth 160
+#define rtfSC_Iacute 161
+#define rtfSC_Icircumflex 162
+#define rtfSC_Idieresis 163
+#define rtfSC_Igrave 164
+#define rtfSC_Ntilde 165
+#define rtfSC_Oacute 166
+#define rtfSC_Ocircumflex 167
+#define rtfSC_Odieresis 168
+#define rtfSC_Ograve 169
+#define rtfSC_Otilde 170
+#define rtfSC_Scaron 171
+#define rtfSC_Thorn 172
+#define rtfSC_Uacute 173
+#define rtfSC_Ucircumflex 174
+#define rtfSC_Udieresis 175
+#define rtfSC_Ugrave 176
+#define rtfSC_Yacute 177
+#define rtfSC_Ydieresis 178
+#define rtfSC_aacute 179
+#define rtfSC_acircumflex 180
+#define rtfSC_adieresis 181
+#define rtfSC_agrave 182
+#define rtfSC_aring 183
+#define rtfSC_atilde 184
+#define rtfSC_brokenbar 185
+#define rtfSC_ccedilla 186
+#define rtfSC_copyright 187
+#define rtfSC_degree 188
+#define rtfSC_divide 189
+#define rtfSC_eacute 190
+#define rtfSC_ecircumflex 191
+#define rtfSC_edieresis 192
+#define rtfSC_egrave 193
+#define rtfSC_eth 194
+#define rtfSC_iacute 195
+#define rtfSC_icircumflex 196
+#define rtfSC_idieresis 197
+#define rtfSC_igrave 198
+#define rtfSC_logicalnot 199
+#define rtfSC_minus 200
+#define rtfSC_multiply 201
+#define rtfSC_ntilde 202
+#define rtfSC_oacute 203
+#define rtfSC_ocircumflex 204
+#define rtfSC_odieresis 205
+#define rtfSC_ograve 206
+#define rtfSC_onehalf 207
+#define rtfSC_onequarter 208
+#define rtfSC_onesuperior 209
+#define rtfSC_otilde 210
+#define rtfSC_plusminus 211
+#define rtfSC_registered 212
+#define rtfSC_thorn 213
+#define rtfSC_threequarters 214
+#define rtfSC_threesuperior 215
+#define rtfSC_trademark 216
+#define rtfSC_twosuperior 217
+#define rtfSC_uacute 218
+#define rtfSC_ucircumflex 219
+#define rtfSC_udieresis 220
+#define rtfSC_ugrave 221
+#define rtfSC_yacute 222
+#define rtfSC_ydieresis 223
+#define rtfSC_Alpha 224
+#define rtfSC_Beta 225
+#define rtfSC_Chi 226
+#define rtfSC_Delta 227
+#define rtfSC_Epsilon 228
+#define rtfSC_Phi 229
+#define rtfSC_Gamma 230
+#define rtfSC_Eta 231
+#define rtfSC_Iota 232
+#define rtfSC_Kappa 233
+#define rtfSC_Lambda 234
+#define rtfSC_Mu 235
+#define rtfSC_Nu 236
+#define rtfSC_Omicron 237
+#define rtfSC_Pi 238
+#define rtfSC_Theta 239
+#define rtfSC_Rho 240
+#define rtfSC_Sigma 241
+#define rtfSC_Tau 242
+#define rtfSC_Upsilon 243
+#define rtfSC_varUpsilon 244
+#define rtfSC_Omega 245
+#define rtfSC_Xi 246
+#define rtfSC_Psi 247
+#define rtfSC_Zeta 248
+#define rtfSC_alpha 249
+#define rtfSC_beta 250
+#define rtfSC_chi 251
+#define rtfSC_delta 252
+#define rtfSC_epsilon 253
+#define rtfSC_phi 254
+#define rtfSC_varphi 255
+#define rtfSC_gamma 256
+#define rtfSC_eta 257
+#define rtfSC_iota 258
+#define rtfSC_kappa 259
+#define rtfSC_lambda 260
+#define rtfSC_mu 261
+#define rtfSC_nu 262
+#define rtfSC_omicron 263
+#define rtfSC_pi 264
+#define rtfSC_varpi 265
+#define rtfSC_theta 266
+#define rtfSC_vartheta 267
+#define rtfSC_rho 268
+#define rtfSC_sigma 269
+#define rtfSC_varsigma 270
+#define rtfSC_tau 271
+#define rtfSC_upsilon 272
+#define rtfSC_omega 273
+#define rtfSC_xi 274
+#define rtfSC_psi 275
+#define rtfSC_zeta 276
+#define rtfSC_nobrkspace 277
+#define rtfSC_nobrkhyphen 278
+#define rtfSC_lessequal 279
+#define rtfSC_greaterequal 280
+#define rtfSC_infinity 281
+#define rtfSC_integral 282
+#define rtfSC_notequal 283
+#define rtfSC_radical 284
+#define rtfSC_radicalex 285
+#define rtfSC_approxequal 286
+#define rtfSC_apple 287
+#define rtfSC_partialdiff 288
+#define rtfSC_opthyphen 289
+#define rtfSC_formula 290
+#define rtfSC_lozenge 291
+#define rtfSC_universal 292
+#define rtfSC_existential 293
+#define rtfSC_suchthat 294
+#define rtfSC_congruent 295
+#define rtfSC_therefore 296
+#define rtfSC_perpendicular 297
+#define rtfSC_minute 298
+#define rtfSC_club 299
+#define rtfSC_diamond 300
+#define rtfSC_heart 301
+#define rtfSC_spade 302
+#define rtfSC_arrowboth 303
+#define rtfSC_arrowleft 304
+#define rtfSC_arrowup 305
+#define rtfSC_arrowright 306
+#define rtfSC_arrowdown 307
+#define rtfSC_second 308
+#define rtfSC_proportional 309
+#define rtfSC_equivalence 310
+#define rtfSC_arrowvertex 311
+#define rtfSC_arrowhorizex 312
+#define rtfSC_carriagereturn 313
+#define rtfSC_aleph 314
+#define rtfSC_Ifraktur 315
+#define rtfSC_Rfraktur 316
+#define rtfSC_weierstrass 317
+#define rtfSC_circlemultiply 318
+#define rtfSC_circleplus 319
+#define rtfSC_emptyset 320
+#define rtfSC_intersection 321
+#define rtfSC_union 322
+#define rtfSC_propersuperset 323
+#define rtfSC_reflexsuperset 324
+#define rtfSC_notsubset 325
+#define rtfSC_propersubset 326
+#define rtfSC_reflexsubset 327
+#define rtfSC_element 328
+#define rtfSC_notelement 329
+#define rtfSC_angle 330
+#define rtfSC_gradient 331
+#define rtfSC_product 332
+#define rtfSC_logicaland 333
+#define rtfSC_logicalor 334
+#define rtfSC_arrowdblboth 335
+#define rtfSC_arrowdblleft 336
+#define rtfSC_arrowdblup 337
+#define rtfSC_arrowdblright 338
+#define rtfSC_arrowdbldown 339
+#define rtfSC_angleleft 340
+#define rtfSC_registersans 341
+#define rtfSC_copyrightsans 342
+#define rtfSC_trademarksans 343
+#define rtfSC_angleright 344
+#define rtfSC_mathplus 345
+#define rtfSC_mathminus 346
+#define rtfSC_mathasterisk 347
+#define rtfSC_mathnumbersign 348
+#define rtfSC_dotmath 349
+#define rtfSC_mathequal 350
+#define rtfSC_mathtilde 351
+
+#define rtfSC_MaxChar 352
+/*
+ * rtf.h - RTF document processing stuff. Release 1.10.
+ */
+
+
+/*
+ * Twentieths of a point (twips) per inch (Many RTF measurements
+ * are in twips per inch (tpi) units). Assumes 72 points/inch.
+ */
+
+# define rtfTpi 1440
+
+/*
+ * RTF buffer size (avoids BUFSIZ, which differs across systems)
+ */
+
+# define rtfBufSiz 1024
+
+/*
+ * Tokens are associated with up to three classification numbers:
+ *
+ * Class number: Broadest (least detailed) breakdown. For programs
+ * that only care about gross token distinctions.
+ * Major/minor numbers: Within their class, tokens have a major
+ * number, and may also have a minor number to further
+ * distinquish tokens with the same major number.
+ *
+ * *** Class, major and minor token numbers are all >= 0 ***
+ *
+ * Tokens that can't be classified are put in the "unknown" class.
+ * For such, the major and minor numbers are meaningless, although
+ * rtfTextBuf may be of interest then.
+ *
+ * Text tokens are a single character, and the major number indicates
+ * the character value (note: can be non-ascii, i.e., greater than 127).
+ * There is no minor number.
+ *
+ * Control symbols may have a parameter value, which will be found in
+ * rtfParam. If no parameter was given, rtfParam = rtfNoParam.
+ *
+ * RTFGetToken() return value is the class number, but it sets all the
+ * global token vars.
+ *
+ * rtfEOF is a fake token used by the reader; the writer never sees
+ * it (except in the token reader hook, if it installs one).
+ */
+
+
+# ifdef THINK_C
+# define rtfNoParam (-32768) /* 16-bit max. neg. value */
+# endif
+# ifndef rtfNoParam
+# define rtfNoParam (-1000000)
+# endif
+
+
+
+
+/*
+ * For some reason, the no-style number is 222
+ */
+
+# define rtfNoStyleNum 222
+# define rtfNormalStyleNum 0
+
+
+/*
+ * Token classes (must be zero-based and sequential)
+ */
+
+# define rtfUnknown 0
+# define rtfGroup 1
+# define rtfText 2
+# define rtfControl 3
+# define rtfEOF 4
+# define rtfMaxClass 5 /* highest class + 1 */
+
+/*
+ * Group class major numbers
+ */
+
+# define rtfBeginGroup 0
+# define rtfEndGroup 1
+
+/*
+ * Control class major and minor numbers.
+ */
+
+# define rtfVersion 0
+
+# define rtfDefFont 1
+
+# define rtfCharSet 2
+# define rtfAnsiCharSet 0
+# define rtfMacCharSet 1
+# define rtfPcCharSet 2
+# define rtfPcaCharSet 3
+
+
+/* destination minor numbers should be zero-based and sequential */
+
+# define rtfDestination 3
+# define rtfFontTbl 0
+# define rtfFontAltName 1 /* new in 1.10 */
+# define rtfEmbeddedFont 2 /* new in 1.10 */
+# define rtfFontFile 3 /* new in 1.10 */
+# define rtfFileTbl 4 /* new in 1.10 */
+# define rtfFileInfo 5 /* new in 1.10 */
+# define rtfColorTbl 6
+# define rtfStyleSheet 7
+# define rtfKeyCode 8
+# define rtfRevisionTbl 9 /* new in 1.10 */
+# define rtfInfo 10
+# define rtfITitle 11
+# define rtfISubject 12
+# define rtfIAuthor 13
+# define rtfIOperator 14
+# define rtfIKeywords 15
+# define rtfIComment 16
+# define rtfIVersion 17
+# define rtfIDoccomm 18
+# define rtfIVerscomm 19
+# define rtfNextFile 20 /* reclassified in 1.10 */
+# define rtfTemplate 21 /* reclassified in 1.10 */
+# define rtfFNSep 22
+# define rtfFNContSep 23
+# define rtfFNContNotice 24
+# define rtfENSep 25 /* new in 1.10 */
+# define rtfENContSep 26 /* new in 1.10 */
+# define rtfENContNotice 27 /* new in 1.10 */
+# define rtfPageNumLevel 28 /* new in 1.10 */
+# define rtfParNumLevelStyle 29 /* new in 1.10 */
+# define rtfHeader 30
+# define rtfFooter 31
+# define rtfHeaderLeft 32
+# define rtfHeaderRight 33
+# define rtfHeaderFirst 34
+# define rtfFooterLeft 35
+# define rtfFooterRight 36
+# define rtfFooterFirst 37
+# define rtfParNumText 38 /* new in 1.10 */
+# define rtfParNumbering 39 /* new in 1.10 */
+# define rtfParNumTextAfter 40 /* new in 1.10 */
+# define rtfParNumTextBefore 41 /* new in 1.10 */
+# define rtfBookmarkStart 42
+# define rtfBookmarkEnd 43
+# define rtfPict 44
+# define rtfObject 45
+# define rtfObjClass 46
+# define rtfObjName 47
+# define rtfObjTime 48 /* new in 1.10 */
+# define rtfObjData 49
+# define rtfObjAlias 50
+# define rtfObjSection 51
+# define rtfObjResult 52
+# define rtfObjItem 53 /* new in 1.10 */
+# define rtfObjTopic 54 /* new in 1.10 */
+# define rtfDrawObject 55 /* new in 1.10 */
+# define rtfFootnote 56
+# define rtfAnnotRefStart 57 /* new in 1.10 */
+# define rtfAnnotRefEnd 58 /* new in 1.10 */
+# define rtfAnnotID 59 /* reclassified in 1.10 */
+# define rtfAnnotAuthor 60 /* new in 1.10 */
+# define rtfAnnotation 61 /* reclassified in 1.10 */
+# define rtfAnnotRef 62 /* new in 1.10 */
+# define rtfAnnotTime 63 /* new in 1.10 */
+# define rtfAnnotIcon 64 /* new in 1.10 */
+# define rtfField 65
+# define rtfFieldInst 66
+# define rtfFieldResult 67
+# define rtfDataField 68 /* new in 1.10 */
+# define rtfIndex 69
+# define rtfIndexText 70
+# define rtfIndexRange 71
+# define rtfTOC 72
+# define rtfNeXTGraphic 73
+# define rtfMaxDestination 74 /* highest dest + 1 */
+
+# define rtfFontFamily 4
+# define rtfFFNil 0
+# define rtfFFRoman 1
+# define rtfFFSwiss 2
+# define rtfFFModern 3
+# define rtfFFScript 4
+# define rtfFFDecor 5
+# define rtfFFTech 6
+# define rtfFFBidirectional 7 /* new in 1.10 */
+
+# define rtfColorName 5
+# define rtfRed 0
+# define rtfGreen 1
+# define rtfBlue 2
+
+# define rtfSpecialChar 6
+ /* special chars seen in \info destination */
+# define rtfIIntVersion 0
+# define rtfICreateTime 1
+# define rtfIRevisionTime 2
+# define rtfIPrintTime 3
+# define rtfIBackupTime 4
+# define rtfIEditTime 5
+# define rtfIYear 6
+# define rtfIMonth 7
+# define rtfIDay 8
+# define rtfIHour 9
+# define rtfIMinute 10
+# define rtfISecond 11 /* new in 1.10 */
+# define rtfINPages 12
+# define rtfINWords 13
+# define rtfINChars 14
+# define rtfIIntID 15
+ /* other special chars */
+# define rtfCurHeadDate 16
+# define rtfCurHeadDateLong 17
+# define rtfCurHeadDateAbbrev 18
+# define rtfCurHeadTime 19
+# define rtfCurHeadPage 20
+# define rtfSectNum 21 /* new in 1.10 */
+# define rtfCurFNote 22
+# define rtfCurAnnotRef 23
+# define rtfFNoteSep 24
+# define rtfFNoteCont 25
+# define rtfCell 26
+# define rtfRow 27
+# define rtfPar 28
+# define rtfSect 29
+# define rtfPage 30
+# define rtfColumn 31
+# define rtfLine 32
+# define rtfSoftPage 33 /* new in 1.10 */
+# define rtfSoftColumn 34 /* new in 1.10 */
+# define rtfSoftLine 35 /* new in 1.10 */
+# define rtfSoftLineHt 36 /* new in 1.10 */
+# define rtfTab 37
+# define rtfEmDash 38
+# define rtfEnDash 39
+# define rtfEmSpace 40 /* new in 1.10 */
+# define rtfEnSpace 41 /* new in 1.10 */
+# define rtfBullet 42
+# define rtfLQuote 43
+# define rtfRQuote 44
+# define rtfLDblQuote 45
+# define rtfRDblQuote 46
+# define rtfFormula 47
+# define rtfNoBrkSpace 49
+# define rtfNoReqHyphen 50
+# define rtfNoBrkHyphen 51
+# define rtfOptDest 52
+# define rtfLTRMark 53 /* new in 1.10 */
+# define rtfRTLMark 54 /* new in 1.10 */
+# define rtfNoWidthJoiner 55 /* new in 1.10 */
+# define rtfNoWidthNonJoiner 56 /* new in 1.10 */
+# define rtfCurHeadPict 57 /* valid? */
+/*# define rtfCurAnnot 58*/ /* apparently not used */
+
+# define rtfStyleAttr 7
+# define rtfAdditive 0 /* new in 1.10 */
+# define rtfBasedOn 1
+# define rtfNext 2
+
+# define rtfDocAttr 8
+# define rtfDefTab 0
+# define rtfHyphHotZone 1
+# define rtfHyphConsecLines 2 /* new in 1.10 */
+# define rtfHyphCaps 3 /* new in 1.10 */
+# define rtfHyphAuto 4 /* new in 1.10 */
+# define rtfLineStart 5
+# define rtfFracWidth 6
+# define rtfMakeBackup 7
+# define rtfRTFDefault 8
+# define rtfPSOverlay 9
+# define rtfDocTemplate 10 /* new in 1.10 */
+# define rtfDefLanguage 11
+# define rtfFENoteType 12 /* new in 1.10 */
+# define rtfFNoteEndSect 13
+# define rtfFNoteEndDoc 14
+# define rtfFNoteText 15
+# define rtfFNoteBottom 16
+# define rtfENoteEndSect 17 /* new in 1.10 */
+# define rtfENoteEndDoc 18 /* new in 1.10 */
+# define rtfENoteText 19 /* new in 1.10 */
+# define rtfENoteBottom 20 /* new in 1.10 */
+# define rtfFNoteStart 21
+# define rtfENoteStart 22 /* new in 1.10 */
+# define rtfFNoteRestartPage 23 /* new in 1.10 */
+# define rtfFNoteRestart 24
+# define rtfFNoteRestartCont 25 /* new in 1.10 */
+# define rtfENoteRestart 26 /* new in 1.10 */
+# define rtfENoteRestartCont 27 /* new in 1.10 */
+# define rtfFNoteNumArabic 28 /* new in 1.10 */
+# define rtfFNoteNumLLetter 29 /* new in 1.10 */
+# define rtfFNoteNumULetter 30 /* new in 1.10 */
+# define rtfFNoteNumLRoman 31 /* new in 1.10 */
+# define rtfFNoteNumURoman 32 /* new in 1.10 */
+# define rtfFNoteNumChicago 33 /* new in 1.10 */
+# define rtfENoteNumArabic 34 /* new in 1.10 */
+# define rtfENoteNumLLetter 35 /* new in 1.10 */
+# define rtfENoteNumULetter 36 /* new in 1.10 */
+# define rtfENoteNumLRoman 37 /* new in 1.10 */
+# define rtfENoteNumURoman 38 /* new in 1.10 */
+# define rtfENoteNumChicago 39 /* new in 1.10 */
+# define rtfPaperWidth 40
+# define rtfPaperHeight 41
+# define rtfPaperSize 42 /* new in 1.10 */
+# define rtfLeftMargin 43
+# define rtfRightMargin 44
+# define rtfTopMargin 45
+# define rtfBottomMargin 46
+# define rtfFacingPage 47
+# define rtfGutterWid 48
+# define rtfMirrorMargin 49
+# define rtfLandscape 50
+# define rtfPageStart 51
+# define rtfWidowCtrl 52
+# define rtfLinkStyles 53 /* new in 1.10 */
+# define rtfNoAutoTabIndent 54 /* new in 1.10 */
+# define rtfWrapSpaces 55 /* new in 1.10 */
+# define rtfPrintColorsBlack 56 /* new in 1.10 */
+# define rtfNoExtraSpaceRL 57 /* new in 1.10 */
+# define rtfNoColumnBalance 58 /* new in 1.10 */
+# define rtfCvtMailMergeQuote 59 /* new in 1.10 */
+# define rtfSuppressTopSpace 60 /* new in 1.10 */
+# define rtfSuppressPreParSpace 61 /* new in 1.10 */
+# define rtfCombineTblBorders 62 /* new in 1.10 */
+# define rtfTranspMetafiles 63 /* new in 1.10 */
+# define rtfSwapBorders 64 /* new in 1.10 */
+# define rtfShowHardBreaks 65 /* new in 1.10 */
+# define rtfFormProtected 66 /* new in 1.10 */
+# define rtfAllProtected 67 /* new in 1.10 */
+# define rtfFormShading 68 /* new in 1.10 */
+# define rtfFormDisplay 69 /* new in 1.10 */
+# define rtfPrintData 70 /* new in 1.10 */
+# define rtfRevProtected 71 /* new in 1.10 */
+# define rtfRevisions 72
+# define rtfRevDisplay 73
+# define rtfRevBar 74
+# define rtfAnnotProtected 75 /* new in 1.10 */
+# define rtfRTLDoc 76 /* new in 1.10 */
+# define rtfLTRDoc 77 /* new in 1.10 */
+
+# define rtfSectAttr 9
+# define rtfSectDef 0
+# define rtfENoteHere 1
+# define rtfPrtBinFirst 2
+# define rtfPrtBin 3
+# define rtfSectStyleNum 4 /* new in 1.10 */
+# define rtfNoBreak 5
+# define rtfColBreak 6
+# define rtfPageBreak 7
+# define rtfEvenBreak 8
+# define rtfOddBreak 9
+# define rtfColumns 10
+# define rtfColumnSpace 11
+# define rtfColumnNumber 12 /* new in 1.10 */
+# define rtfColumnSpRight 13 /* new in 1.10 */
+# define rtfColumnWidth 14 /* new in 1.10 */
+# define rtfColumnLine 15
+# define rtfLineModulus 16
+# define rtfLineDist 17
+# define rtfLineStarts 18
+# define rtfLineRestart 19
+# define rtfLineRestartPg 20
+# define rtfLineCont 21
+# define rtfSectPageWid 22
+# define rtfSectPageHt 23
+# define rtfSectMarginLeft 24
+# define rtfSectMarginRight 25
+# define rtfSectMarginTop 26
+# define rtfSectMarginBottom 27
+# define rtfSectMarginGutter 28
+# define rtfSectLandscape 29
+# define rtfTitleSpecial 30
+# define rtfHeaderY 31
+# define rtfFooterY 32
+# define rtfPageStarts 33
+# define rtfPageCont 34
+# define rtfPageRestart 35
+# define rtfPageNumRight 36 /* renamed in 1.10 */
+# define rtfPageNumTop 37
+# define rtfPageDecimal 38
+# define rtfPageURoman 39
+# define rtfPageLRoman 40
+# define rtfPageULetter 41
+# define rtfPageLLetter 42
+# define rtfPageNumHyphSep 43 /* new in 1.10 */
+# define rtfPageNumSpaceSep 44 /* new in 1.10 */
+# define rtfPageNumColonSep 45 /* new in 1.10 */
+# define rtfPageNumEmdashSep 46 /* new in 1.10 */
+# define rtfPageNumEndashSep 47 /* new in 1.10 */
+# define rtfTopVAlign 48
+# define rtfBottomVAlign 49
+# define rtfCenterVAlign 50
+# define rtfJustVAlign 51
+# define rtfRTLSect 52 /* new in 1.10 */
+# define rtfLTRSect 53 /* new in 1.10 */
+
+# define rtfTblAttr 10
+# define rtfRowDef 0
+# define rtfRowGapH 1
+# define rtfCellPos 2
+# define rtfMergeRngFirst 3
+# define rtfMergePrevious 4
+# define rtfRowLeft 5
+# define rtfRowRight 6
+# define rtfRowCenter 7
+# define rtfRowLeftEdge 8
+# define rtfRowHt 9
+# define rtfRowHeader 10 /* new in 1.10 */
+# define rtfRowKeep 11 /* new in 1.10 */
+# define rtfRTLRow 12 /* new in 1.10 */
+# define rtfLTRRow 13 /* new in 1.10 */
+# define rtfRowBordTop 14 /* new in 1.10 */
+# define rtfRowBordLeft 15 /* new in 1.10 */
+# define rtfRowBordBottom 16 /* new in 1.10 */
+# define rtfRowBordRight 17 /* new in 1.10 */
+# define rtfRowBordHoriz 18 /* new in 1.10 */
+# define rtfRowBordVert 19 /* new in 1.10 */
+# define rtfCellBordBottom 20
+# define rtfCellBordTop 21
+# define rtfCellBordLeft 22
+# define rtfCellBordRight 23
+# define rtfCellShading 24
+# define rtfCellBgPatH 25
+# define rtfCellBgPatV 26
+# define rtfCellFwdDiagBgPat 27
+# define rtfCellBwdDiagBgPat 28
+# define rtfCellHatchBgPat 29
+# define rtfCellDiagHatchBgPat 30
+# define rtfCellDarkBgPatH 31
+# define rtfCellDarkBgPatV 32
+# define rtfCellFwdDarkBgPat 33
+# define rtfCellBwdDarkBgPat 34
+# define rtfCellDarkHatchBgPat 35
+# define rtfCellDarkDiagHatchBgPat 36
+# define rtfCellBgPatLineColor 37
+# define rtfCellBgPatColor 38
+
+# define rtfParAttr 11
+# define rtfParDef 0
+# define rtfStyleNum 1
+# define rtfHyphenate 2 /* new in 1.10 */
+# define rtfInTable 3
+# define rtfKeep 4
+# define rtfNoWidowControl 5 /* new in 1.10 */
+# define rtfKeepNext 6
+# define rtfOutlineLevel 7 /* new in 1.10 */
+# define rtfNoLineNum 8
+# define rtfPBBefore 9
+# define rtfSideBySide 10
+# define rtfQuadLeft 11
+# define rtfQuadRight 12
+# define rtfQuadJust 13
+# define rtfQuadCenter 14
+# define rtfFirstIndent 15
+# define rtfLeftIndent 16
+# define rtfRightIndent 17
+# define rtfSpaceBefore 18
+# define rtfSpaceAfter 19
+# define rtfSpaceBetween 20
+# define rtfSpaceMultiply 21 /* new in 1.10 */
+# define rtfSubDocument 22 /* new in 1.10 */
+# define rtfRTLPar 23 /* new in 1.10 */
+# define rtfLTRPar 24 /* new in 1.10 */
+# define rtfTabPos 25
+# define rtfTabLeft 26 /* new in 1.10 */
+# define rtfTabRight 27
+# define rtfTabCenter 28
+# define rtfTabDecimal 29
+# define rtfTabBar 30
+# define rtfLeaderDot 31
+# define rtfLeaderHyphen 32
+# define rtfLeaderUnder 33
+# define rtfLeaderThick 34
+# define rtfLeaderEqual 35
+# define rtfParLevel 36 /* new in 1.10 */
+# define rtfParBullet 37 /* new in 1.10 */
+# define rtfParSimple 38 /* new in 1.10 */
+# define rtfParNumCont 39 /* new in 1.10 */
+# define rtfParNumOnce 40 /* new in 1.10 */
+# define rtfParNumAcross 41 /* new in 1.10 */
+# define rtfParHangIndent 42 /* new in 1.10 */
+# define rtfParNumRestart 43 /* new in 1.10 */
+# define rtfParNumCardinal 44 /* new in 1.10 */
+# define rtfParNumDecimal 45 /* new in 1.10 */
+# define rtfParNumULetter 46 /* new in 1.10 */
+# define rtfParNumURoman 47 /* new in 1.10 */
+# define rtfParNumLLetter 48 /* new in 1.10 */
+# define rtfParNumLRoman 49 /* new in 1.10 */
+# define rtfParNumOrdinal 50 /* new in 1.10 */
+# define rtfParNumOrdinalText 51 /* new in 1.10 */
+# define rtfParNumBold 52 /* new in 1.10 */
+# define rtfParNumItalic 53 /* new in 1.10 */
+# define rtfParNumAllCaps 54 /* new in 1.10 */
+# define rtfParNumSmallCaps 55 /* new in 1.10 */
+# define rtfParNumUnder 56 /* new in 1.10 */
+# define rtfParNumDotUnder 57 /* new in 1.10 */
+# define rtfParNumDbUnder 58 /* new in 1.10 */
+# define rtfParNumNoUnder 59 /* new in 1.10 */
+# define rtfParNumWordUnder 60 /* new in 1.10 */
+# define rtfParNumStrikethru 61 /* new in 1.10 */
+# define rtfParNumForeColor 62 /* new in 1.10 */
+# define rtfParNumFont 63 /* new in 1.10 */
+# define rtfParNumFontSize 64 /* new in 1.10 */
+# define rtfParNumIndent 65 /* new in 1.10 */
+# define rtfParNumSpacing 66 /* new in 1.10 */
+# define rtfParNumInclPrev 67 /* new in 1.10 */
+# define rtfParNumCenter 68 /* new in 1.10 */
+# define rtfParNumLeft 69 /* new in 1.10 */
+# define rtfParNumRight 70 /* new in 1.10 */
+# define rtfParNumStartAt 71 /* new in 1.10 */
+# define rtfBorderTop 72
+# define rtfBorderBottom 73
+# define rtfBorderLeft 74
+# define rtfBorderRight 75
+# define rtfBorderBetween 76
+# define rtfBorderBar 77
+# define rtfBorderBox 78
+# define rtfBorderSingle 79
+# define rtfBorderThick 80
+# define rtfBorderShadow 81
+# define rtfBorderDouble 82
+# define rtfBorderDot 83
+# define rtfBorderDash 84 /* new in 1.10 */
+# define rtfBorderHair 85
+# define rtfBorderWidth 86
+# define rtfBorderColor 87
+# define rtfBorderSpace 88
+# define rtfShading 89
+# define rtfBgPatH 90
+# define rtfBgPatV 91
+# define rtfFwdDiagBgPat 92
+# define rtfBwdDiagBgPat 93
+# define rtfHatchBgPat 94
+# define rtfDiagHatchBgPat 95
+# define rtfDarkBgPatH 96
+# define rtfDarkBgPatV 97
+# define rtfFwdDarkBgPat 98
+# define rtfBwdDarkBgPat 99
+# define rtfDarkHatchBgPat 100
+# define rtfDarkDiagHatchBgPat 101
+# define rtfBgPatLineColor 102
+# define rtfBgPatColor 103
+
+# define rtfCharAttr 12
+# define rtfPlain 0
+# define rtfBold 1
+# define rtfAllCaps 2
+# define rtfDeleted 3
+# define rtfSubScript 4
+# define rtfSubScrShrink 5 /* new in 1.10 */
+# define rtfNoSuperSub 6 /* new in 1.10 */
+# define rtfExpand 7
+# define rtfExpandTwips 8 /* new in 1.10 */
+# define rtfKerning 9 /* new in 1.10 */
+# define rtfFontNum 10
+# define rtfFontSize 11
+# define rtfItalic 12
+# define rtfOutline 13
+# define rtfRevised 14
+# define rtfRevAuthor 15 /* new in 1.10 */
+# define rtfRevDTTM 16 /* new in 1.10 */
+# define rtfSmallCaps 17
+# define rtfShadow 18
+# define rtfStrikeThru 19
+# define rtfUnderline 20
+# define rtfDotUnderline 21 /* renamed in 1.10 */
+# define rtfDbUnderline 22
+# define rtfNoUnderline 23
+# define rtfWordUnderline 24 /* renamed in 1.10 */
+# define rtfSuperScript 25
+# define rtfSuperScrShrink 26 /* new in 1.10 */
+# define rtfInvisible 27
+# define rtfForeColor 28
+# define rtfBackColor 29
+# define rtfRTLChar 30 /* new in 1.10 */
+# define rtfLTRChar 31 /* new in 1.10 */
+# define rtfCharStyleNum 32 /* new in 1.10 */
+# define rtfCharCharSet 33 /* new in 1.10 */
+# define rtfLanguage 34
+# define rtfGray 35
+
+# define rtfPictAttr 13
+# define rtfMacQD 0
+# define rtfPMMetafile 1
+# define rtfWinMetafile 2
+# define rtfDevIndBitmap 3
+# define rtfWinBitmap 4
+# define rtfPixelBits 5
+# define rtfBitmapPlanes 6
+# define rtfBitmapWid 7
+# define rtfPicWid 8
+# define rtfPicHt 9
+# define rtfPicGoalWid 10
+# define rtfPicGoalHt 11
+# define rtfPicScaleX 12
+# define rtfPicScaleY 13
+# define rtfPicScaled 14
+# define rtfPicCropTop 15
+# define rtfPicCropBottom 16
+# define rtfPicCropLeft 17
+# define rtfPicCropRight 18
+# define rtfPicMFHasBitmap 19 /* new in 1.10 */
+# define rtfPicMFBitsPerPixel 20 /* new in 1.10 */
+# define rtfPicBinary 21
+
+# define rtfBookmarkAttr 14
+# define rtfBookmarkFirstCol 0
+# define rtfBookmarkLastCol 1
+
+# define rtfNeXTGrAttr 15
+# define rtfNeXTGWidth 0
+# define rtfNeXTGHeight 1
+
+# define rtfFieldAttr 16
+# define rtfFieldDirty 0
+# define rtfFieldEdited 1
+# define rtfFieldLocked 2
+# define rtfFieldPrivate 3
+# define rtfFieldAlt 4 /* new in 1.10 */
+
+# define rtfTOCAttr 17
+# define rtfTOCType 0
+# define rtfTOCLevel 1
+
+# define rtfPosAttr 18
+# define rtfAbsWid 0
+# define rtfAbsHt 1
+# define rtfRPosMargH 2
+# define rtfRPosPageH 3
+# define rtfRPosColH 4
+# define rtfPosX 5
+# define rtfPosNegX 6 /* new in 1.10 */
+# define rtfPosXCenter 7
+# define rtfPosXInside 8
+# define rtfPosXOutSide 9
+# define rtfPosXRight 10
+# define rtfPosXLeft 11
+# define rtfRPosMargV 12
+# define rtfRPosPageV 13
+# define rtfRPosParaV 14
+# define rtfPosY 15
+# define rtfPosNegY 16 /* new in 1.10 */
+# define rtfPosYInline 17
+# define rtfPosYTop 18
+# define rtfPosYCenter 19
+# define rtfPosYBottom 20
+# define rtfNoWrap 21
+# define rtfDistFromTextAll 22 /* renamed in 1.10 */
+# define rtfDistFromTextX 23 /* new in 1.10 */
+# define rtfDistFromTextY 24 /* new in 1.10 */
+# define rtfTextDistY 25
+# define rtfDropCapLines 26 /* new in 1.10 */
+# define rtfDropCapType 27 /* new in 1.10 */
+
+# define rtfObjAttr 19
+# define rtfObjEmb 0
+# define rtfObjLink 1
+# define rtfObjAutoLink 2
+# define rtfObjSubscriber 3
+# define rtfObjPublisher 4 /* new in 1.10 */
+# define rtfObjICEmb 5
+# define rtfObjLinkSelf 6
+# define rtfObjLock 7
+# define rtfObjUpdate 8 /* new in 1.10 */
+# define rtfObjHt 9
+# define rtfObjWid 10
+# define rtfObjSetSize 11
+# define rtfObjAlign 12 /* new in 1.10 */
+# define rtfObjTransposeY 13
+# define rtfObjCropTop 14
+# define rtfObjCropBottom 15
+# define rtfObjCropLeft 16
+# define rtfObjCropRight 17
+# define rtfObjScaleX 18
+# define rtfObjScaleY 19
+# define rtfObjResRTF 20
+# define rtfObjResPict 21
+# define rtfObjResBitmap 22
+# define rtfObjResText 23
+# define rtfObjResMerge 24
+# define rtfObjBookmarkPubObj 25
+# define rtfObjPubAutoUpdate 26
+
+# define rtfFNoteAttr 20 /* new in 1.10 */
+# define rtfFNAlt 0 /* new in 1.10 */
+
+# define rtfKeyCodeAttr 21 /* new in 1.10 */
+# define rtfAltKey 0 /* new in 1.10 */
+# define rtfShiftKey 1 /* new in 1.10 */
+# define rtfControlKey 2 /* new in 1.10 */
+# define rtfFunctionKey 3 /* new in 1.10 */
+
+# define rtfACharAttr 22 /* new in 1.10 */
+# define rtfACBold 0 /* new in 1.10 */
+# define rtfACAllCaps 1 /* new in 1.10 */
+# define rtfACForeColor 2 /* new in 1.10 */
+# define rtfACSubScript 3 /* new in 1.10 */
+# define rtfACExpand 4 /* new in 1.10 */
+# define rtfACFontNum 5 /* new in 1.10 */
+# define rtfACFontSize 6 /* new in 1.10 */
+# define rtfACItalic 7 /* new in 1.10 */
+# define rtfACLanguage 8 /* new in 1.10 */
+# define rtfACOutline 9 /* new in 1.10 */
+# define rtfACSmallCaps 10 /* new in 1.10 */
+# define rtfACShadow 11 /* new in 1.10 */
+# define rtfACStrikeThru 12 /* new in 1.10 */
+# define rtfACUnderline 13 /* new in 1.10 */
+# define rtfACDotUnderline 14 /* new in 1.10 */
+# define rtfACDbUnderline 15 /* new in 1.10 */
+# define rtfACNoUnderline 16 /* new in 1.10 */
+# define rtfACWordUnderline 17 /* new in 1.10 */
+# define rtfACSuperScript 18 /* new in 1.10 */
+
+# define rtfFontAttr 23 /* new in 1.10 */
+# define rtfFontCharSet 0 /* new in 1.10 */
+# define rtfFontPitch 1 /* new in 1.10 */
+# define rtfFontCodePage 2 /* new in 1.10 */
+# define rtfFTypeNil 3 /* new in 1.10 */
+# define rtfFTypeTrueType 4 /* new in 1.10 */
+
+# define rtfFileAttr 24 /* new in 1.10 */
+# define rtfFileNum 0 /* new in 1.10 */
+# define rtfFileRelPath 1 /* new in 1.10 */
+# define rtfFileOSNum 2 /* new in 1.10 */
+
+# define rtfFileSource 25 /* new in 1.10 */
+# define rtfSrcMacintosh 0 /* new in 1.10 */
+# define rtfSrcDOS 1 /* new in 1.10 */
+# define rtfSrcNTFS 2 /* new in 1.10 */
+# define rtfSrcHPFS 3 /* new in 1.10 */
+# define rtfSrcNetwork 4 /* new in 1.10 */
+
+/*
+ * Drawing attributes
+ */
+
+# define rtfDrawAttr 26 /* new in 1.10 */
+# define rtfDrawLock 0 /* new in 1.10 */
+# define rtfDrawPageRelX 1 /* new in 1.10 */
+# define rtfDrawColumnRelX 2 /* new in 1.10 */
+# define rtfDrawMarginRelX 3 /* new in 1.10 */
+# define rtfDrawPageRelY 4 /* new in 1.10 */
+# define rtfDrawColumnRelY 5 /* new in 1.10 */
+# define rtfDrawMarginRelY 6 /* new in 1.10 */
+# define rtfDrawHeight 7 /* new in 1.10 */
+
+# define rtfDrawBeginGroup 8 /* new in 1.10 */
+# define rtfDrawGroupCount 9 /* new in 1.10 */
+# define rtfDrawEndGroup 10 /* new in 1.10 */
+# define rtfDrawArc 11 /* new in 1.10 */
+# define rtfDrawCallout 12 /* new in 1.10 */
+# define rtfDrawEllipse 13 /* new in 1.10 */
+# define rtfDrawLine 14 /* new in 1.10 */
+# define rtfDrawPolygon 15 /* new in 1.10 */
+# define rtfDrawPolyLine 16 /* new in 1.10 */
+# define rtfDrawRect 17 /* new in 1.10 */
+# define rtfDrawTextBox 18 /* new in 1.10 */
+
+# define rtfDrawOffsetX 19 /* new in 1.10 */
+# define rtfDrawSizeX 20 /* new in 1.10 */
+# define rtfDrawOffsetY 21 /* new in 1.10 */
+# define rtfDrawSizeY 22 /* new in 1.10 */
+
+# define rtfCOAngle 23 /* new in 1.10 */
+# define rtfCOAccentBar 24 /* new in 1.10 */
+# define rtfCOBestFit 25 /* new in 1.10 */
+# define rtfCOBorder 26 /* new in 1.10 */
+# define rtfCOAttachAbsDist 27 /* new in 1.10 */
+# define rtfCOAttachBottom 28 /* new in 1.10 */
+# define rtfCOAttachCenter 29 /* new in 1.10 */
+# define rtfCOAttachTop 30 /* new in 1.10 */
+# define rtfCOLength 31 /* new in 1.10 */
+# define rtfCONegXQuadrant 32 /* new in 1.10 */
+# define rtfCONegYQuadrant 33 /* new in 1.10 */
+# define rtfCOOffset 34 /* new in 1.10 */
+# define rtfCOAttachSmart 35 /* new in 1.10 */
+# define rtfCODoubleLine 36 /* new in 1.10 */
+# define rtfCORightAngle 37 /* new in 1.10 */
+# define rtfCOSingleLine 38 /* new in 1.10 */
+# define rtfCOTripleLine 39 /* new in 1.10 */
+
+# define rtfDrawTextBoxMargin 40 /* new in 1.10 */
+# define rtfDrawTextBoxText 41 /* new in 1.10 */
+# define rtfDrawRoundRect 42 /* new in 1.10 */
+
+# define rtfDrawPointX 43 /* new in 1.10 */
+# define rtfDrawPointY 44 /* new in 1.10 */
+# define rtfDrawPolyCount 45 /* new in 1.10 */
+
+# define rtfDrawArcFlipX 46 /* new in 1.10 */
+# define rtfDrawArcFlipY 47 /* new in 1.10 */
+
+# define rtfDrawLineBlue 48 /* new in 1.10 */
+# define rtfDrawLineGreen 49 /* new in 1.10 */
+# define rtfDrawLineRed 50 /* new in 1.10 */
+# define rtfDrawLinePalette 51 /* new in 1.10 */
+# define rtfDrawLineDashDot 52 /* new in 1.10 */
+# define rtfDrawLineDashDotDot 53 /* new in 1.10 */
+# define rtfDrawLineDash 54 /* new in 1.10 */
+# define rtfDrawLineDot 55 /* new in 1.10 */
+# define rtfDrawLineGray 56 /* new in 1.10 */
+# define rtfDrawLineHollow 57 /* new in 1.10 */
+# define rtfDrawLineSolid 58 /* new in 1.10 */
+# define rtfDrawLineWidth 59 /* new in 1.10 */
+
+# define rtfDrawHollowEndArrow 60 /* new in 1.10 */
+# define rtfDrawEndArrowLength 61 /* new in 1.10 */
+# define rtfDrawSolidEndArrow 62 /* new in 1.10 */
+# define rtfDrawEndArrowWidth 63 /* new in 1.10 */
+# define rtfDrawHollowStartArrow 64 /* new in 1.10 */
+# define rtfDrawStartArrowLength 65 /* new in 1.10 */
+# define rtfDrawSolidStartArrow 66 /* new in 1.10 */
+# define rtfDrawStartArrowWidth 67 /* new in 1.10 */
+
+# define rtfDrawBgFillBlue 68 /* new in 1.10 */
+# define rtfDrawBgFillGreen 69 /* new in 1.10 */
+# define rtfDrawBgFillRed 70 /* new in 1.10 */
+# define rtfDrawBgFillPalette 71 /* new in 1.10 */
+# define rtfDrawBgFillGray 72 /* new in 1.10 */
+# define rtfDrawFgFillBlue 73 /* new in 1.10 */
+# define rtfDrawFgFillGreen 74 /* new in 1.10 */
+# define rtfDrawFgFillRed 75 /* new in 1.10 */
+# define rtfDrawFgFillPalette 76 /* new in 1.10 */
+# define rtfDrawFgFillGray 77 /* new in 1.10 */
+# define rtfDrawFillPatIndex 78 /* new in 1.10 */
+
+# define rtfDrawShadow 79 /* new in 1.10 */
+# define rtfDrawShadowXOffset 80 /* new in 1.10 */
+# define rtfDrawShadowYOffset 81 /* new in 1.10 */
+
+/*
+ * index entry attributes
+ */
+
+# define rtfIndexAttr 27 /* new in 1.10 */
+# define rtfIndexNumber 0 /* new in 1.10 */
+# define rtfIndexBold 1 /* reclassified in 1.10 */
+# define rtfIndexItalic 2 /* reclassified in 1.10 */
+
+
+/*
+ * \wmetafile argument values
+ */
+
+# define rtfWmMmText 1
+# define rtfWmMmLometric 2
+# define rtfWmMmHimetric 3
+# define rtfWmMmLoenglish 4
+# define rtfWmMmHienglish 5
+# define rtfWmMmTwips 6
+# define rtfWmMmIsotropic 7
+# define rtfWmMmAnisotropic 8
+
+/*
+ * \pmmetafile argument values
+ */
+
+# define rtfPmPuArbitrary 4
+# define rtfPmPuPels 8
+# define rtfPmPuLometric 12
+# define rtfPmPuHimetric 16
+# define rtfPmPuLoenglish 20
+# define rtfPmPuHienglish 24
+# define rtfPmPuTwips 28
+
+/*
+ * \lang argument values
+ */
+
+# define rtfLangNoLang 0x0400
+# define rtfLangAlbanian 0x041c
+# define rtfLangArabic 0x0401
+# define rtfLangBahasa 0x0421
+# define rtfLangBelgianDutch 0x0813
+# define rtfLangBelgianFrench 0x080c
+# define rtfLangBrazilianPortuguese 0x0416
+# define rtfLangBulgarian 0x0402
+# define rtfLangCatalan 0x0403
+# define rtfLangLatinCroatoSerbian 0x041a
+# define rtfLangCzech 0x0405
+# define rtfLangDanish 0x0406
+# define rtfLangDutch 0x0413
+# define rtfLangAustralianEnglish 0x0c09
+# define rtfLangUKEnglish 0x0809
+# define rtfLangUSEnglish 0x0409
+# define rtfLangFinnish 0x040b
+# define rtfLangFrench 0x040c
+# define rtfLangCanadianFrench 0x0c0c
+# define rtfLangGerman 0x0407
+# define rtfLangGreek 0x0408
+# define rtfLangHebrew 0x040d
+# define rtfLangHungarian 0x040e
+# define rtfLangIcelandic 0x040f
+# define rtfLangItalian 0x0410
+# define rtfLangJapanese 0x0411
+# define rtfLangKorean 0x0412
+# define rtfLangBokmalNorwegian 0x0414
+# define rtfLangNynorskNorwegian 0x0814
+# define rtfLangPolish 0x0415
+# define rtfLangPortuguese 0x0816
+# define rtfLangRhaetoRomanic 0x0417
+# define rtfLangRomanian 0x0418
+# define rtfLangRussian 0x0419
+# define rtfLangCyrillicSerboCroatian 0x081a
+# define rtfLangSimplifiedChinese 0x0804
+# define rtfLangSlovak 0x041b
+# define rtfLangCastilianSpanish 0x040a
+# define rtfLangMexicanSpanish 0x080a
+# define rtfLangSwedish 0x041d
+# define rtfLangSwissFrench 0x100c
+# define rtfLangSwissGerman 0x0807
+# define rtfLangSwissItalian 0x0810
+# define rtfLangThai 0x041e
+# define rtfLangTraditionalChinese 0x0404
+# define rtfLangTurkish 0x041f
+# define rtfLangUrdu 0x0420
+
+/*
+ * CharSet indices
+ */
+
+# define rtfCSGeneral 0 /* general (default) charset */
+# define rtfCSSymbol 1 /* symbol charset */
+
+/*
+ * Flags for auto-charset-processing. Both are on by default.
+ */
+
+# define rtfReadCharSet 0x01 /* auto-read charset files */
+# define rtfSwitchCharSet 0x02 /* auto-switch charset maps */
+
+/*
+ * Style types
+ */
+
+# define rtfParStyle 0 /* the default */
+# define rtfCharStyle 1
+# define rtfSectStyle 2
+
+/*
+ * RTF font, color and style structures. Used for font table,
+ * color table, and stylesheet processing.
+ */
+
+typedef struct RTFFont RTFFont;
+typedef struct RTFColor RTFColor;
+typedef struct RTFStyle RTFStyle;
+typedef struct RTFStyleElt RTFStyleElt;
+
+
+struct RTFFont
+{
+ char *rtfFName; /* font name */
+ char *rtfFAltName; /* font alternate name */
+ int rtfFNum; /* font number */
+ int rtfFFamily; /* font family */
+ int rtfFCharSet; /* font charset */
+ int rtfFPitch; /* font pitch */
+ int rtfFType; /* font type */
+ int rtfFCodePage; /* font code page */
+ RTFFont *rtfNextFont; /* next font in list */
+};
+
+
+/*
+ * Color values are -1 if the default color for the the color
+ * number should be used. The default color is writer-dependent.
+ */
+
+struct RTFColor
+{
+ int rtfCNum; /* color number */
+ int rtfCRed; /* red value */
+ int rtfCGreen; /* green value */
+ int rtfCBlue; /* blue value */
+ RTFColor *rtfNextColor; /* next color in list */
+};
+
+
+struct RTFStyle
+{
+ char *rtfSName; /* style name */
+ int rtfSType; /* style type */
+ int rtfSAdditive; /* whether or not style is additive */
+ int rtfSNum; /* style number */
+ int rtfSBasedOn; /* style this one's based on */
+ int rtfSNextPar; /* style next paragraph style */
+ RTFStyleElt *rtfSSEList; /* list of style words */
+ int rtfExpanding; /* non-zero = being expanded */
+ RTFStyle *rtfNextStyle; /* next style in style list */
+};
+
+
+struct RTFStyleElt
+{
+ int rtfSEClass; /* token class */
+ int rtfSEMajor; /* token major number */
+ int rtfSEMinor; /* token minor number */
+ int rtfSEParam; /* control symbol parameter */
+ char *rtfSEText; /* text of symbol */
+ RTFStyleElt *rtfNextSE; /* next element in style */
+};
+
+
+/*
+ * Return pointer to new element of type t, or NULL
+ * if no memory available.
+ */
+
+# define New(t) ((t *) RTFAlloc ((int) sizeof (t)))
+
+/* maximum number of character values representable in a byte */
+
+# define charSetSize 256
+
+/* charset stack size */
+
+# define maxCSStack 10
+
+
+struct _RTF_Info;
+typedef struct _RTF_Info RTF_Info;
+
+typedef void (*RTFFuncPtr) (RTF_Info *); /* generic function pointer */
+
+struct _RTF_Info {
+ /*
+ * Public variables (listed in rtf.h)
+ */
+
+ /*
+ * Information pertaining to last token read by RTFToken. The
+ * text is exactly as it occurs in the input file, e.g., "\{"
+ * will be found in rtfTextBuf as "\{", even though it means "{".
+ * These variables are also set when styles are reprocessed.
+ */
+
+ int rtfClass;
+ int rtfMajor;
+ int rtfMinor;
+ int rtfParam;
+ int rtfFormat;
+ char *rtfTextBuf;
+ int rtfTextLen;
+
+ long rtfLineNum;
+ int rtfLinePos;
+
+
+ /*
+ * Private stuff
+ */
+
+ int pushedChar; /* pushback char if read too far */
+
+ int pushedClass; /* pushed token info for RTFUngetToken() */
+ int pushedMajor;
+ int pushedMinor;
+ int pushedParam;
+ char *pushedTextBuf;
+
+ int prevChar;
+ int bumpLine;
+
+ RTFFont *fontList; /* these lists MUST be */
+ RTFColor *colorList; /* initialized to NULL */
+ RTFStyle *styleList;
+
+ char *inputName;
+ char *outputName;
+
+ EDITSTREAM editstream;
+ char InputBuffer[0x1000];
+ DWORD dwInputSize;
+ DWORD dwInputUsed;
+
+ /* edit window to output to */
+ HWND hwndEdit;
+
+ /*
+ * These arrays are used to map RTF input character values onto the standard
+ * character names represented by the values. Input character values are
+ * used as indices into the arrays to produce standard character codes.
+ */
+
+
+ char *genCharSetFile ;
+ int genCharCode[charSetSize]; /* general */
+ int haveGenCharSet;
+
+ char *symCharSetFile;
+ int symCharCode[charSetSize]; /* symbol */
+ int haveSymCharSet;
+
+ int curCharSet;
+ int *curCharCode;
+
+ /*
+ * By default, the reader is configured to handle charset mapping invisibly,
+ * including reading the charset files and switching charset maps as necessary
+ * for Symbol font.
+ */
+
+ int autoCharSetFlags;
+
+ /*
+ * Stack for keeping track of charset map on group begin/end. This is
+ * necessary because group termination reverts the font to the previous
+ * value, which may implicitly change it.
+ */
+
+ int csStack[maxCSStack];
+ int csTop;
+
+ RTFFuncPtr ccb[rtfMaxClass]; /* class callbacks */
+
+ RTFFuncPtr dcb[rtfMaxDestination]; /* destination callbacks */
+
+ RTFFuncPtr readHook;
+
+ RTFFuncPtr panicProc;
+
+ FILE *(*libFileOpen) ();
+
+ char *outMap[rtfSC_MaxChar];
+
+ DWORD dwOutputCount;
+ char OutputBuffer[0x1000];
+};
+
+
+/*
+ * Public RTF reader routines
+ */
+
+void RTFInit (RTF_Info *);
+void RTFSetInputName (RTF_Info *, char *);
+char *RTFGetInputName (RTF_Info *);
+void RTFSetOutputName (RTF_Info *, char *);
+char *RTFGetOutputName (RTF_Info *);
+void RTFSetClassCallback (RTF_Info *, int, RTFFuncPtr);
+RTFFuncPtr RTFGetClassCallback (RTF_Info *, int);
+void RTFSetDestinationCallback (RTF_Info *, int, RTFFuncPtr);
+RTFFuncPtr RTFGetDestinationCallback (RTF_Info *, int);
+void RTFRead (RTF_Info *);
+int RTFGetToken (RTF_Info *); /* writer should rarely need this */
+void RTFUngetToken (RTF_Info *);
+int RTFPeekToken (RTF_Info *);
+void RTFSetToken (RTF_Info *, int, int, int, int, const char *);
+void RTFSetReadHook (RTF_Info *, RTFFuncPtr);
+RTFFuncPtr RTFGetReadHook (RTF_Info *);
+void RTFRouteToken (RTF_Info *);
+void RTFSkipGroup (RTF_Info *);
+void RTFExpandStyle (RTF_Info *, int);
+int RTFCheckCM (RTF_Info *, int, int);
+int RTFCheckCMM (RTF_Info *, int, int, int);
+int RTFCheckMM (RTF_Info *, int, int);
+RTFFont *RTFGetFont (RTF_Info *, int);
+RTFColor *RTFGetColor (RTF_Info *, int);
+RTFStyle *RTFGetStyle (RTF_Info *, int);
+# define RTFAlloc(size) _RTFAlloc ((int) size)
+char *_RTFAlloc (int);
+char *RTFStrSave (char *);
+void RTFFree (char *);
+int RTFCharToHex ( char);
+int RTFHexToChar ( int );
+void RTFSetMsgProc ( RTFFuncPtr );
+void RTFSetPanicProc ( RTF_Info *, RTFFuncPtr);
+
+/*
+ * The following messing around is used to allow RTFMsg() and RTFPanic()
+ * to be variable-argument functions that are declared publicly but
+ * without generating prototype-mismatch errors on systems that have
+ * stdarg.h.
+ */
+
+void RTFMsg (RTF_Info *, const char *fmt, ...);
+void RTFPanic (RTF_Info *, const char *fmt, ...);
+
+int RTFReadOutputMap ( RTF_Info *, char *[], int);
+int RTFReadCharSetMap ( RTF_Info *, int);
+void RTFSetCharSetMap ( RTF_Info *, char *, int);
+int RTFStdCharCode ( RTF_Info *, const char *);
+const char *RTFStdCharName ( RTF_Info *, int);
+int RTFMapChar ( RTF_Info *, int);
+int RTFGetCharSet( RTF_Info * );
+void RTFSetCharSet( RTF_Info *, int);
+
+void RTFSetOpenLibFileProc ( RTF_Info *, FILE *(*)());
+FILE *RTFOpenLibFile ( RTF_Info *, char *, char *);
+
+void RTFFlushOutputBuffer( RTF_Info *info );
+void RTFSetEditStream(RTF_Info *, EDITSTREAM *es);
+
+void WriterInit (RTF_Info *);
+int BeginFile (RTF_Info *);
+
+#endif
--- /dev/null
+/*
+ * RichEdit - operations on runs (diRun, rectangular pieces of paragraphs).
+ * Splitting/joining runs. Adjusting offsets after deleting/adding content.
+ * Character/pixel conversions.
+ *
+ * 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);
+
+int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2)
+{
+ if ((run1->nFlags | run2->nFlags) & (MERF_ENDPARA | MERF_GRAPHICS))
+ return 0;
+ if (run1->style != run2->style)
+ return 0;
+ if ((run1->nFlags & MERF_STYLEFLAGS) != (run2->nFlags & MERF_STYLEFLAGS))
+ return 0;
+ return 1;
+}
+
+void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift)
+{
+ p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
+ assert(p);
+ ME_PropagateCharOffset(p, shift);
+}
+
+void ME_PropagateCharOffset(ME_DisplayItem *p, int shift)
+{
+ if (p->type == diRun) /* propagate in all runs in this para */
+ {
+ TRACE("PropagateCharOffset(%s, %d)\n", debugstr_w(p->member.run.strText->szData), shift);
+ do {
+ p->member.run.nCharOfs += shift;
+ assert(p->member.run.nCharOfs >= 0);
+ p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
+ } while(p->type == diRun);
+ }
+ if (p->type == diParagraph) /* propagate in all next paras */
+ {
+ do {
+ p->member.para.nCharOfs += shift;
+ assert(p->member.para.nCharOfs >= 0);
+ p = p->member.para.next_para;
+ } while(p->type == diParagraph);
+ }
+ if (p->type == diTextEnd)
+ {
+ p->member.para.nCharOfs += shift;
+ assert(p->member.para.nCharOfs >= 0);
+ }
+}
+
+void ME_CheckCharOffsets(ME_TextEditor *editor)
+{
+ ME_DisplayItem *p = editor->pBuffer->pFirst;
+ int ofs = 0, ofsp = 0;
+ if(TRACE_ON(richedit))
+ {
+ TRACE("---\n");
+ ME_DumpDocument(editor->pBuffer);
+ }
+ do {
+ p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
+ switch(p->type) {
+ case diTextEnd:
+ TRACE("tend, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
+ assert(ofsp+ofs == p->member.para.nCharOfs);
+ return;
+ case diParagraph:
+ TRACE("para, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
+ assert(ofsp+ofs == p->member.para.nCharOfs);
+ ofsp = p->member.para.nCharOfs;
+ ofs = 0;
+ break;
+ case diRun:
+ TRACE("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08lx\n",
+ p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs,
+ p->member.run.strText->nLen, debugstr_w(p->member.run.strText->szData),
+ p->member.run.nFlags,
+ p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects);
+ assert(ofs == p->member.run.nCharOfs);
+ ofs += ME_StrLen(p->member.run.strText);
+ break;
+ default:
+ assert(0);
+ }
+ } while(1);
+}
+
+int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs)
+{
+ ME_DisplayItem *pPara;
+
+ assert(pRun->type == diRun);
+ assert(pRun->member.run.nCharOfs != -1);
+
+ pPara = ME_FindItemBack(pRun, diParagraph);
+ assert(pPara);
+ assert(pPara->type==diParagraph);
+ return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs
+ + ME_VPosToPos(pRun->member.run.strText, nOfs);
+}
+
+void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor)
+{
+ ME_RunOfsFromCharOfs(editor, nCharOfs, &pCursor->pRun, &pCursor->nOffset);
+}
+
+void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs)
+{
+ ME_DisplayItem *pPara;
+ int nParaOfs;
+
+ pPara = editor->pBuffer->pFirst->member.para.next_para;
+ assert(pPara);
+ assert(ppRun);
+ assert(pOfs);
+ while (pPara->type == diParagraph)
+ {
+ nParaOfs = pPara->member.para.nCharOfs;
+ assert(nCharOfs >= nParaOfs);
+
+ if (nCharOfs < pPara->member.para.next_para->member.para.nCharOfs)
+ {
+ *ppRun = ME_FindItemFwd(pPara, diRun);
+ assert(*ppRun);
+ while (!((*ppRun)->member.run.nFlags & MERF_ENDPARA))
+ {
+ ME_DisplayItem *pNext = ME_FindItemFwd(*ppRun, diRun);
+ assert(pNext);
+ assert(pNext->type == diRun);
+ if (nCharOfs < nParaOfs + pNext->member.run.nCharOfs) {
+ *pOfs = ME_PosToVPos((*ppRun)->member.run.strText,
+ nCharOfs - nParaOfs - (*ppRun)->member.run.nCharOfs);
+ return;
+ }
+ *ppRun = pNext;
+ }
+ if (nCharOfs == nParaOfs + (*ppRun)->member.run.nCharOfs) {
+ *pOfs = 0;
+ return;
+ }
+ }
+ pPara = pPara->member.para.next_para;
+ }
+ *ppRun = ME_FindItemBack(editor->pBuffer->pLast, diRun);
+ *pOfs = 0;
+ assert((*ppRun)->member.run.nFlags & MERF_ENDPARA);
+}
+
+void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p)
+{
+ ME_DisplayItem *pNext = p->next;
+ int i;
+ assert(p->type == diRun && pNext->type == diRun);
+ assert(p->member.run.nCharOfs != -1);
+ ME_GetParagraph(p)->member.para.nFlags |= MEPF_REWRAP;
+
+ for (i=0; i<editor->nCursors; i++) {
+ if (editor->pCursors[i].pRun == pNext) {
+ editor->pCursors[i].pRun = p;
+ editor->pCursors[i].nOffset += ME_StrVLen(p->member.run.strText);
+ }
+ }
+
+ ME_AppendString(p->member.run.strText, pNext->member.run.strText);
+ ME_Remove(pNext);
+ ME_DestroyDisplayItem(pNext);
+ ME_UpdateRunFlags(editor, &p->member.run);
+ if(TRACE_ON(richedit))
+ {
+ TRACE("Before check after join\n");
+ ME_CheckCharOffsets(editor);
+ TRACE("After check after join\n");
+ }
+}
+
+ME_DisplayItem *ME_SplitRun(ME_Context *c, ME_DisplayItem *item, int nVChar)
+{
+ ME_TextEditor *editor = c->editor;
+ ME_DisplayItem *item2 = NULL;
+ ME_Run *run, *run2;
+
+ assert(item->member.run.nCharOfs != -1);
+ if(TRACE_ON(richedit))
+ {
+ TRACE("Before check before split\n");
+ ME_CheckCharOffsets(editor);
+ TRACE("After check before split\n");
+ }
+
+ run = &item->member.run;
+
+ TRACE("Before split: %s(%ld, %ld)\n", debugstr_w(run->strText->szData),
+ run->pt.x, run->pt.y);
+
+ item2 = ME_SplitRunSimple(editor, item, nVChar);
+
+ run2 = &item2->member.run;
+
+ ME_CalcRunExtent(c, run);
+ ME_CalcRunExtent(c, run2);
+
+ run2->pt.x = run->pt.x+run->nWidth;
+ run2->pt.y = run->pt.y;
+
+ if(TRACE_ON(richedit))
+ {
+ TRACE("Before check after split\n");
+ ME_CheckCharOffsets(editor);
+ TRACE("After check after split\n");
+ TRACE("After split: %s(%ld, %ld), %s(%ld, %ld)\n",
+ debugstr_w(run->strText->szData), run->pt.x, run->pt.y,
+ debugstr_w(run2->strText->szData), run2->pt.x, run2->pt.y);
+ }
+
+ return item2;
+}
+
+/* split a run starting from voffset */
+ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nVChar)
+{
+ ME_Run *run = &item->member.run;
+ ME_DisplayItem *item2;
+ ME_Run *run2;
+ int i;
+ assert(nVChar > 0 && nVChar < ME_StrVLen(run->strText));
+ assert(item->type == diRun);
+ assert(!(item->member.run.nFlags & MERF_GRAPHICS));
+ assert(item->member.run.nCharOfs != -1);
+
+ item2 = ME_MakeRun(run->style,
+ ME_VSplitString(run->strText, nVChar), run->nFlags&MERF_SPLITMASK);
+
+ item2->member.run.nCharOfs = item->member.run.nCharOfs+
+ ME_VPosToPos(item->member.run.strText, nVChar);
+
+ run2 = &item2->member.run;
+ ME_InsertBefore(item->next, item2);
+
+ ME_UpdateRunFlags(editor, run);
+ ME_UpdateRunFlags(editor, run2);
+ for (i=0; i<editor->nCursors; i++) {
+ if (editor->pCursors[i].pRun == item &&
+ editor->pCursors[i].nOffset >= nVChar) {
+ assert(item2->type == diRun);
+ editor->pCursors[i].pRun = item2;
+ editor->pCursors[i].nOffset -= nVChar;
+ }
+ }
+ ME_GetParagraph(item)->member.para.nFlags |= MEPF_REWRAP;
+ return item2;
+}
+
+/* split the start and final whitespace into separate runs */
+/* returns the last run added */
+/*
+ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *item)
+{
+ int i, nVLen, nChanged;
+ assert(item->type == diRun);
+ assert(!(item->member.run.nFlags & MERF_GRAPHICS));
+ return item;
+}
+*/
+
+ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags)
+{
+ ME_DisplayItem *item = ME_MakeDI(diRun);
+ item->member.run.style = s;
+ item->member.run.strText = strData;
+ item->member.run.nFlags = nFlags;
+ item->member.run.nCharOfs = -1;
+ ME_AddRefStyle(s);
+ return item;
+}
+
+ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem *pItem)
+{
+ ME_Cursor tmp;
+ ME_DisplayItem *pDI;
+ ME_UndoItem *pUI;
+
+ assert(pItem->type == diRun || pItem->type == diUndoInsertRun);
+
+ pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
+ pUI->nStart = nCharOfs;
+ pUI->nLen = pItem->member.run.strText->nLen;
+ ME_CursorFromCharOfs(editor, nCharOfs, &tmp);
+ if (tmp.nOffset) {
+ tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
+ tmp.nOffset = 0;
+ }
+ pDI = ME_MakeRun(pItem->member.run.style, ME_StrDup(pItem->member.run.strText), pItem->member.run.nFlags);
+ pDI->member.run.nCharOfs = tmp.pRun->member.run.nCharOfs;
+ ME_InsertBefore(tmp.pRun, pDI);
+ TRACE("Shift length:%d\n", pDI->member.run.strText->nLen);
+ ME_PropagateCharOffset(tmp.pRun, pDI->member.run.strText->nLen);
+ ME_GetParagraph(tmp.pRun)->member.para.nFlags |= MEPF_REWRAP;
+
+ return pDI;
+}
+
+static inline int ME_IsWSpace(WCHAR ch)
+{
+ return ch <= ' ';
+}
+
+void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run)
+{
+ assert(run->nCharOfs != -1);
+ if (ME_IsSplitable(run->strText))
+ run->nFlags |= MERF_SPLITTABLE;
+ else
+ run->nFlags &= ~MERF_SPLITTABLE;
+
+ if (!(run->nFlags & MERF_GRAPHICS)) {
+ if (ME_IsWhitespaces(run->strText))
+ run->nFlags |= MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE;
+ else
+ {
+ run->nFlags &= ~MERF_WHITESPACE;
+
+ if (ME_IsWSpace(ME_GetCharFwd(run->strText,0)))
+ run->nFlags |= MERF_STARTWHITE;
+ else
+ run->nFlags &= ~MERF_STARTWHITE;
+
+ if (ME_IsWSpace(ME_GetCharBack(run->strText,0)))
+ run->nFlags |= MERF_ENDWHITE;
+ else
+ run->nFlags &= ~MERF_ENDWHITE;
+ }
+ }
+ else
+ run->nFlags &= ~(MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE);
+}
+
+void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize)
+{
+ assert(run->nFlags & MERF_GRAPHICS);
+ pSize->cx = 64;
+ pSize->cy = 64;
+}
+
+int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run)
+{
+ int fit = 0;
+ HGDIOBJ hOldFont;
+ HDC hDC;
+ SIZE sz;
+ if (!run->strText->nLen)
+ return 0;
+
+ if (run->nFlags & MERF_GRAPHICS)
+ {
+ SIZE sz;
+ ME_GetGraphicsSize(editor, run, &sz);
+ if (cx < sz.cx)
+ return 0;
+ return 1;
+ }
+ hDC = GetDC(editor->hWnd);
+ hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
+ GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
+ cx, &fit, NULL, &sz);
+ ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
+ ReleaseDC(editor->hWnd, hDC);
+ return fit;
+}
+
+int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run)
+{
+ int fit = 0, fit1 = 0;
+ HGDIOBJ hOldFont;
+ HDC hDC;
+ SIZE sz, sz2, sz3;
+ if (!run->strText->nLen)
+ return 0;
+
+ if (run->nFlags & MERF_GRAPHICS)
+ {
+ SIZE sz;
+ ME_GetGraphicsSize(editor, run, &sz);
+ if (cx < sz.cx/2)
+ return 0;
+ return 1;
+ }
+
+ hDC = GetDC(editor->hWnd);
+ hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
+ GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
+ cx, &fit, NULL, &sz);
+ if (fit != run->strText->nLen)
+ {
+ int chars = 1;
+
+ GetTextExtentPoint32W(hDC, run->strText->szData, fit, &sz2);
+ fit1 = ME_StrRelPos(run->strText, fit, &chars);
+ GetTextExtentPoint32W(hDC, run->strText->szData, fit1, &sz3);
+ if (cx >= (sz2.cx+sz3.cx)/2)
+ fit = fit1;
+ }
+ ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
+ ReleaseDC(editor->hWnd, hDC);
+ return fit;
+}
+
+int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset)
+{
+ SIZE size;
+ HDC hDC = GetDC(editor->hWnd);
+ HGDIOBJ hOldFont;
+
+ if (pRun->nFlags & MERF_GRAPHICS)
+ {
+ if (!nOffset) return 0;
+ ME_GetGraphicsSize(editor, pRun, &size);
+ return 1;
+ }
+ hOldFont = ME_SelectStyleFont(editor, hDC, pRun->style);
+ GetTextExtentPoint32W(hDC, pRun->strText->szData, nOffset, &size);
+ ME_UnselectStyleFont(editor, hDC, pRun->style, hOldFont);
+ ReleaseDC(editor->hWnd, hDC);
+ return size.cx;
+}
+
+void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s,
+ SIZE *size)
+{
+ HDC hDC = c->hDC;
+ HGDIOBJ hOldFont;
+ hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
+ GetTextExtentPoint32W(hDC, szText, nChars, size);
+ ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
+}
+
+SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen)
+{
+ SIZE size;
+ int nMaxLen = ME_StrVLen(run->strText);
+
+ if (nLen>nMaxLen)
+ nLen = nMaxLen;
+
+ if (run->nFlags & MERF_GRAPHICS)
+ {
+ ME_GetGraphicsSize(c->editor, run, &size);
+ return size;
+ }
+
+ ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size);
+
+ return size;
+}
+
+void ME_CalcRunExtent(ME_Context *c, ME_Run *run)
+{
+ SIZE size;
+ int nEnd = ME_StrVLen(run->strText);
+
+ if (run->nFlags & MERF_GRAPHICS) {
+ ME_GetGraphicsSize(c->editor, run, &size);
+ run->nWidth = size.cx;
+ run->nAscent = size.cy;
+ run->nDescent = 0;
+ return;
+ }
+ ME_GetTextExtent(c, run->strText->szData, nEnd, run->style, &size);
+ run->nWidth = size.cx;
+ run->nAscent = run->style->tm.tmAscent;
+ run->nDescent = run->style->tm.tmDescent;
+}
+
+void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para)
+{
+ assert(para->type == diParagraph);
+ /* FIXME */
+}
+
+void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
+{
+ int nFrom, nTo;
+ ME_GetSelection(editor, &nFrom, &nTo);
+ if (nFrom == nTo)
+ {
+ ME_Style *s;
+ if (!editor->pBuffer->pCharStyle)
+ editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
+ s = ME_ApplyStyle(editor->pBuffer->pCharStyle, pFmt);
+ ME_ReleaseStyle(editor->pBuffer->pCharStyle);
+ editor->pBuffer->pCharStyle = s;
+ }
+ else
+ ME_SetCharFormat(editor, nFrom, nTo-nFrom, pFmt);
+}
+
+void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W *pFmt)
+{
+ ME_Cursor tmp, tmp2;
+ ME_DisplayItem *para;
+
+ ME_CursorFromCharOfs(editor, nOfs, &tmp);
+ if (tmp.nOffset)
+ tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
+
+ ME_CursorFromCharOfs(editor, nOfs+nChars, &tmp2);
+ if (tmp2.nOffset)
+ tmp2.pRun = ME_SplitRunSimple(editor, tmp2.pRun, tmp2.nOffset);
+
+ para = ME_GetParagraph(tmp.pRun);
+ para->member.para.nFlags |= MEPF_REWRAP;
+
+ while(tmp.pRun != tmp2.pRun)
+ {
+ ME_UndoItem *undo = NULL;
+ ME_Style *new_style = ME_ApplyStyle(tmp.pRun->member.run.style, pFmt);
+ /* ME_DumpStyle(new_style); */
+ undo = ME_AddUndoItem(editor, diUndoSetCharFormat, NULL);
+ if (undo) {
+ undo->nStart = tmp.pRun->member.run.nCharOfs+para->member.para.nCharOfs;
+ undo->nLen = tmp.pRun->member.run.strText->nLen;
+ undo->di.member.ustyle = tmp.pRun->member.run.style;
+ /* we'd have to addref undo..ustyle and release tmp...style
+ but they'd cancel each other out so we can do nothing instead */
+ }
+ else
+ ME_ReleaseStyle(tmp.pRun->member.run.style);
+ tmp.pRun->member.run.style = new_style;
+ tmp.pRun = ME_FindItemFwd(tmp.pRun, diRunOrParagraph);
+ if (tmp.pRun->type == diParagraph)
+ {
+ para = tmp.pRun;
+ tmp.pRun = ME_FindItemFwd(tmp.pRun, diRun);
+ if (tmp.pRun != tmp2.pRun)
+ para->member.para.nFlags |= MEPF_REWRAP;
+ }
+ assert(tmp.pRun);
+ }
+}
+
+void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod)
+{
+ ME_Style *style;
+ ME_UndoItem *undo;
+
+ assert(mod->cbSize == sizeof(CHARFORMAT2W));
+ undo = ME_AddUndoItem(editor, diUndoSetDefaultCharFormat, NULL);
+ if (undo) {
+ undo->nStart = -1;
+ undo->nLen = -1;
+ undo->di.member.ustyle = editor->pBuffer->pDefaultStyle;
+ ME_AddRefStyle(undo->di.member.ustyle);
+ }
+ style = ME_ApplyStyle(editor->pBuffer->pDefaultStyle, mod);
+ editor->pBuffer->pDefaultStyle->fmt = style->fmt;
+ editor->pBuffer->pDefaultStyle->tm = style->tm;
+ ME_ReleaseStyle(style);
+ ME_MarkAllForWrapping(editor);
+ /* pcf = editor->pBuffer->pDefaultStyle->fmt; */
+}
+
+void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt)
+{
+ ME_CopyCharFormat(pFmt, &run->member.run.style->fmt);
+}
+
+void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
+{
+ int nFrom, nTo;
+ ME_GetSelection(editor, &nFrom, &nTo);
+ ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt);
+}
+
+void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
+{
+ int nFrom, nTo;
+ ME_GetSelection(editor, &nFrom, &nTo);
+ if (nFrom == nTo && editor->pBuffer->pCharStyle)
+ {
+ ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt);
+ return;
+ }
+ ME_GetCharFormat(editor, nFrom, nTo, pFmt);
+}
+
+void ME_GetCharFormat(ME_TextEditor *editor, int nFrom, int nTo, CHARFORMAT2W *pFmt)
+{
+ ME_DisplayItem *run, *run_end;
+ int nOffset, nOffset2;
+ CHARFORMAT2W tmp;
+
+ if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */
+ nTo--;
+
+ ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset);
+ if (nFrom == nTo) /* special case - if selection is empty, take previous char's formatting */
+ {
+ if (!nOffset)
+ {
+ ME_DisplayItem *tmp_run = ME_FindItemBack(run, diRunOrParagraph);
+ if (tmp_run->type == diRun) {
+ ME_GetRunCharFormat(editor, tmp_run, pFmt);
+ return;
+ }
+ }
+ ME_GetRunCharFormat(editor, run, pFmt);
+ return;
+ }
+ ME_RunOfsFromCharOfs(editor, nTo, &run_end, &nOffset2);
+
+ ME_GetRunCharFormat(editor, run, pFmt);
+
+ if (run == run_end) return;
+
+ do {
+ /* FIXME add more style feature comparisons */
+ int nAttribs = CFM_SIZE | CFM_FACE | CFM_COLOR;
+ int nEffects = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
+
+ run = ME_FindItemFwd(run, diRun);
+
+ ZeroMemory(&tmp, sizeof(tmp));
+ tmp.cbSize = sizeof(tmp);
+ ME_GetRunCharFormat(editor, run, &tmp);
+
+ assert((tmp.dwMask & nAttribs) == nAttribs);
+ assert((tmp.dwMask & nEffects) == nEffects);
+ /* reset flags that differ */
+
+ if (pFmt->yHeight != tmp.yHeight)
+ pFmt->dwMask &= ~CFM_SIZE;
+ if (pFmt->dwMask & CFM_FACE)
+ {
+ if (!(tmp.dwMask & CFM_FACE))
+ pFmt->dwMask &= ~CFM_FACE;
+ else if (lstrcmpW(pFmt->szFaceName, tmp.szFaceName))
+ pFmt->dwMask &= ~CFM_FACE;
+ }
+ if (pFmt->yHeight != tmp.yHeight)
+ pFmt->dwMask &= ~CFM_SIZE;
+ if (pFmt->dwMask & CFM_COLOR)
+ {
+ if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR)))
+ {
+ if (pFmt->crTextColor != tmp.crTextColor)
+ pFmt->dwMask &= ~CFM_COLOR;
+ }
+ }
+
+ pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & nEffects);
+
+ } while(run != run_end);
+}
--- /dev/null
+/*
+ * RichEdit - string operations
+ *
+ * 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);
+
+int ME_GetOptimalBuffer(int nLen)
+{
+ return ((2*nLen+1)+128)&~63;
+}
+
+ME_String *ME_MakeString(LPCWSTR szText)
+{
+ ME_String *s = ALLOC_OBJ(ME_String);
+ s->nLen = lstrlenW(szText);
+ s->nBuffer = ME_GetOptimalBuffer(s->nLen+1);
+ s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer);
+ lstrcpyW(s->szData, szText);
+ return s;
+}
+
+ME_String *ME_MakeStringN(LPCWSTR szText, int nMaxChars)
+{
+ ME_String *s = ALLOC_OBJ(ME_String);
+ int i;
+ for (i=0; i<nMaxChars && szText[i]; i++)
+ ;
+ s->nLen = i;
+ s->nBuffer = ME_GetOptimalBuffer(s->nLen+1);
+ s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer);
+ lstrcpynW(s->szData, szText, s->nLen+1);
+ return s;
+}
+
+ME_String *ME_StrDup(ME_String *s)
+{
+ return ME_MakeStringN(s->szData, s->nLen);
+}
+
+void ME_DestroyString(ME_String *s)
+{
+ FREE_OBJ(s->szData);
+ FREE_OBJ(s);
+}
+
+void ME_AppendString(ME_String *s1, ME_String *s2)
+{
+ if (s1->nLen+s2->nLen+1 <= s1->nBuffer) {
+ lstrcpyW(s1->szData+s1->nLen, s2->szData);
+ s1->nLen += s2->nLen;
+ }
+ else
+ {
+ WCHAR *buf;
+ s1->nBuffer = ME_GetOptimalBuffer(s1->nLen+s2->nLen+1);
+
+ buf = ALLOC_N_OBJ(WCHAR, s1->nBuffer);
+ lstrcpyW(buf, s1->szData);
+ lstrcpyW(buf+s1->nLen, s2->szData);
+ FREE_OBJ(s1->szData);
+ s1->szData = buf;
+ s1->nLen += s2->nLen;
+ }
+}
+
+ME_String *ME_ConcatString(ME_String *s1, ME_String *s2)
+{
+ ME_String *s = ALLOC_OBJ(ME_String);
+ s->nLen = s1->nLen+s2->nLen;
+ s->nBuffer = ME_GetOptimalBuffer(s1->nLen+s2->nLen+1);
+ s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer);
+ lstrcpyW(s->szData, s1->szData);
+ lstrcpyW(s->szData+s1->nLen, s2->szData);
+ return s;
+}
+
+ME_String *ME_VSplitString(ME_String *orig, int charidx)
+{
+ ME_String *s;
+
+ /*if (charidx<0) charidx = 0;
+ if (charidx>orig->nLen) charidx = orig->nLen;
+ */
+ assert(charidx>=0);
+ assert(charidx<=orig->nLen);
+
+ s = ME_MakeString(orig->szData+charidx);
+ orig->nLen = charidx;
+ orig->szData[charidx] = L'\0';
+ return s;
+}
+
+int ME_IsWhitespaces(ME_String *s)
+{
+ /* FIXME multibyte */
+ WCHAR *pos = s->szData;
+ while(*pos++ == ' ')
+ ;
+ pos--;
+ if (*pos)
+ return 0;
+ else
+ return 1;
+}
+
+int ME_IsSplitable(ME_String *s)
+{
+ /* FIXME multibyte */
+ WCHAR *pos = s->szData;
+ WCHAR ch;
+ while(*pos++ == L' ')
+ ;
+ pos--;
+ while((ch = *pos++) != 0)
+ {
+ if (ch == L' ')
+ return 1;
+ }
+ return 0;
+}
+
+/* FIXME multibyte */
+/*
+int ME_CalcSkipChars(ME_String *s)
+{
+ int cnt = 0;
+ while(cnt < s->nLen && s->szData[s->nLen-1-cnt]==' ')
+ cnt++;
+ return cnt;
+}
+*/
+
+int ME_StrLen(ME_String *s) {
+ return s->nLen;
+}
+
+int ME_StrVLen(ME_String *s) {
+ return s->nLen;
+}
+
+/* FIXME we use widechars, not multibytes, inside, no need for complex logic anymore */
+int ME_StrRelPos(ME_String *s, int nVChar, int *pRelChars)
+{
+ TRACE("%s,%d,&%d\n", debugstr_w(s->szData), nVChar, *pRelChars);
+
+ assert(*pRelChars);
+ if (!*pRelChars) return nVChar;
+
+ if (*pRelChars>0)
+ {
+ while(nVChar<s->nLen && *pRelChars>0)
+ {
+ nVChar++;
+ (*pRelChars)--;
+ }
+ return nVChar;
+ }
+
+ while(nVChar>0 && *pRelChars<0)
+ {
+ nVChar--;
+ (*pRelChars)++;
+ }
+ return nVChar;
+}
+
+int ME_StrRelPos2(ME_String *s, int nVChar, int nRelChars)
+{
+ return ME_StrRelPos(s, nVChar, &nRelChars);
+}
+
+int ME_VPosToPos(ME_String *s, int nVPos)
+{
+ return nVPos;
+ /*
+ int i = 0, len = 0;
+ if (!nVPos)
+ return 0;
+ while (i < s->nLen)
+ {
+ if (i == nVPos)
+ return len;
+ if (s->szData[i]=='\\') i++;
+ i++;
+ len++;
+ }
+ return len;
+ */
+}
+
+int ME_PosToVPos(ME_String *s, int nPos)
+{
+ if (!nPos)
+ return 0;
+ return ME_StrRelPos2(s, 0, nPos);
+}
+
+void ME_StrDeleteV(ME_String *s, int nVChar, int nChars)
+{
+ int end_ofs;
+
+ assert(nVChar >=0 && nVChar <= s->nLen);
+ assert(nChars >= 0);
+ assert(nVChar+nChars <= s->nLen);
+
+ end_ofs = ME_StrRelPos2(s, nVChar, nChars);
+ assert(end_ofs <= s->nLen);
+ memmove(s->szData+nVChar, s->szData+end_ofs, 2*(s->nLen+1-end_ofs));
+ s->nLen -= (end_ofs - nVChar);
+}
+
+int ME_GetCharFwd(ME_String *s, int nPos)
+{
+ int nVPos = 0;
+
+ assert(nPos < ME_StrLen(s));
+ if (nPos)
+ nVPos = ME_StrRelPos2(s, nVPos, nPos);
+
+ if (nVPos < s->nLen)
+ return s->szData[nVPos];
+ return -1;
+}
+
+int ME_GetCharBack(ME_String *s, int nPos)
+{
+ int nVPos = ME_StrVLen(s);
+
+ assert(nPos < ME_StrLen(s));
+ if (nPos)
+ nVPos = ME_StrRelPos2(s, nVPos, -nPos);
+
+ if (nVPos < s->nLen)
+ return s->szData[nVPos];
+ return -1;
+}
+
+int ME_FindNonWhitespaceV(ME_String *s, int nVChar) {
+ int i;
+ for (i = nVChar; isspace(s->szData[i]) && i<s->nLen; i++)
+ ;
+
+ return i;
+}
+
+/* note: returns offset of the first trailing whitespace */
+int ME_ReverseFindNonWhitespaceV(ME_String *s, int nVChar) {
+ int i;
+ for (i = nVChar; i>0 && isspace(s->szData[i-1]); i--)
+ ;
+
+ return i;
+}
+
+/* note: returns offset of the first trailing nonwhitespace */
+int ME_ReverseFindWhitespaceV(ME_String *s, int nVChar) {
+ int i;
+ for (i = nVChar; i>0 && !isspace(s->szData[i-1]); i--)
+ ;
+
+ return i;
+}
+
+LPWSTR ME_ToUnicode(HWND hWnd, LPVOID psz)
+{
+ if (IsWindowUnicode(hWnd))
+ return (LPWSTR)psz;
+ else {
+ WCHAR *tmp;
+ int nChars = MultiByteToWideChar(CP_ACP, 0, (char *)psz, -1, NULL, 0);
+ if((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
+ MultiByteToWideChar(CP_ACP, 0, (char *)psz, -1, tmp, nChars);
+ return tmp;
+ }
+}
+
+void ME_EndToUnicode(HWND hWnd, LPVOID psz)
+{
+ if (IsWindowUnicode(hWnd))
+ FREE_OBJ(psz);
+}
+
+LPSTR ME_ToAnsi(HWND hWnd, LPVOID psz)
+{
+ if (!IsWindowUnicode(hWnd))
+ return (LPSTR)psz;
+ else {
+ char *tmp;
+ int nChars = WideCharToMultiByte(CP_ACP, 0, (WCHAR *)psz, -1, NULL, 0, NULL, NULL);
+ if((tmp = ALLOC_N_OBJ(char, nChars)) != NULL)
+ WideCharToMultiByte(CP_ACP, 0, (WCHAR *)psz, -1, tmp, nChars, NULL, NULL);
+ return tmp;
+ }
+}
+
+void ME_EndToAnsi(HWND hWnd, LPVOID psz)
+{
+ if (!IsWindowUnicode(hWnd))
+ FREE_OBJ(psz);
+}
--- /dev/null
+/*
+ * RichEdit style management functions
+ *
+ * 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 int all_refs = 0;
+
+CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from)
+{
+ if (from->cbSize == sizeof(CHARFORMATA))
+ {
+ CHARFORMATA *f = (CHARFORMATA *)from;
+ CopyMemory(to, f, sizeof(*f)-sizeof(f->szFaceName));
+ to->cbSize = sizeof(CHARFORMAT2W);
+ if (f->dwMask & CFM_FACE) {
+ MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName)/sizeof(WCHAR));
+ }
+ return to;
+ }
+ if (from->cbSize == sizeof(CHARFORMATW))
+ {
+ CHARFORMATW *f = (CHARFORMATW *)from;
+ CopyMemory(to, f, sizeof(*f));
+ /* theoretically, we don't need to zero the remaining memory */
+ ZeroMemory(((CHARFORMATW *)to)+1, sizeof(CHARFORMAT2W)-sizeof(CHARFORMATW));
+ to->cbSize = sizeof(CHARFORMAT2W);
+ return to;
+ }
+ if (from->cbSize == sizeof(CHARFORMAT2A))
+ {
+ CHARFORMATA *f = (CHARFORMATA *)from;
+ /* copy the A structure without face name */
+ CopyMemory(to, f, sizeof(CHARFORMATA)-sizeof(f->szFaceName));
+ /* convert face name */
+ if (f->dwMask & CFM_FACE)
+ MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName));
+ /* copy the rest of the 2A structure to 2W */
+ CopyMemory(1+((CHARFORMATW *)from), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
+ to->cbSize = sizeof(CHARFORMAT2W);
+ return to;
+ }
+
+ assert(from->cbSize >= sizeof(CHARFORMAT2W));
+ return from;
+}
+
+void ME_CopyToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from)
+{
+ if (ME_ToCF2W(to, from) == from)
+ CopyMemory(to, from, sizeof(*from));
+}
+
+CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from)
+{
+ assert(from->cbSize == sizeof(CHARFORMAT2W));
+ if (to->cbSize == sizeof(CHARFORMATA))
+ {
+ CHARFORMATA *t = (CHARFORMATA *)to;
+ CopyMemory(t, from, sizeof(*t)-sizeof(t->szFaceName));
+ WideCharToMultiByte(0, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), 0, 0);
+ t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */
+ return to;
+ }
+ if (to->cbSize == sizeof(CHARFORMATW))
+ {
+ CHARFORMATW *t = (CHARFORMATW *)to;
+ CopyMemory(t, from, sizeof(*t));
+ t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */
+ return to;
+ }
+ if (to->cbSize == sizeof(CHARFORMAT2A))
+ {
+ CHARFORMAT2A *t = (CHARFORMAT2A *)to;
+ /* copy the A structure without face name */
+ CopyMemory(t, from, sizeof(CHARFORMATA)-sizeof(t->szFaceName));
+ /* convert face name */
+ WideCharToMultiByte(0, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), 0, 0);
+ /* copy the rest of the 2A structure to 2W */
+ CopyMemory(&t->wWeight, &from->wWeight, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
+ t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */
+ return to;
+ }
+ assert(to->cbSize >= sizeof(CHARFORMAT2W));
+ return from;
+}
+
+void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from)
+{
+ if (ME_ToCFAny(to, from) == from)
+ CopyMemory(to, from, to->cbSize);
+}
+
+ME_Style *ME_MakeStyle(CHARFORMAT2W *style) {
+ CHARFORMAT2W styledata;
+ ME_Style *s = ALLOC_OBJ(ME_Style);
+
+ style = ME_ToCF2W(&styledata, style);
+ memset(s, 0, sizeof(ME_Style));
+ if (style->cbSize <= sizeof(CHARFORMAT2W))
+ CopyMemory(&s->fmt, style, style->cbSize);
+ else
+ CopyMemory(&s->fmt, style, sizeof(CHARFORMAT2W));
+ s->fmt.cbSize = sizeof(CHARFORMAT2W);
+
+ s->nSequence = -2;
+ s->nRefs = 1;
+ s->hFont = NULL;
+ all_refs++;
+ return s;
+}
+
+#define COPY_STYLE_ITEM(mask, member) \
+ if (style->dwMask & mask) { \
+ s->fmt.dwMask |= mask;\
+ s->fmt.member = style->member;\
+ }
+
+#define COPY_STYLE_ITEM_MEMCPY(mask, member) \
+ if (style->dwMask & mask) { \
+ s->fmt.dwMask |= mask;\
+ CopyMemory(s->fmt.member, style->member, sizeof(style->member));\
+ }
+
+void ME_InitCharFormat2W(CHARFORMAT2W *pFmt)
+{
+ ZeroMemory(pFmt, sizeof(CHARFORMAT2W));
+ pFmt->cbSize = sizeof(CHARFORMAT2W);
+}
+
+ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style)
+{
+ CHARFORMAT2W styledata;
+ ME_Style *s = ME_MakeStyle(&sSrc->fmt);
+ style = ME_ToCF2W(&styledata, style);
+ COPY_STYLE_ITEM(CFM_ANIMATION, bAnimation);
+ COPY_STYLE_ITEM(CFM_BACKCOLOR, crBackColor);
+ COPY_STYLE_ITEM(CFM_CHARSET, bCharSet);
+ COPY_STYLE_ITEM(CFM_COLOR, crTextColor);
+ COPY_STYLE_ITEM_MEMCPY(CFM_FACE, szFaceName);
+ COPY_STYLE_ITEM(CFM_KERNING, wKerning);
+ COPY_STYLE_ITEM(CFM_LCID, lcid);
+ COPY_STYLE_ITEM(CFM_OFFSET, yOffset);
+ COPY_STYLE_ITEM(CFM_REVAUTHOR, bRevAuthor);
+ COPY_STYLE_ITEM(CFM_SIZE, yHeight);
+ COPY_STYLE_ITEM(CFM_SPACING, sSpacing);
+ COPY_STYLE_ITEM(CFM_STYLE, sStyle);
+ COPY_STYLE_ITEM(CFM_UNDERLINETYPE, bUnderlineType);
+ COPY_STYLE_ITEM(CFM_WEIGHT, wWeight);
+
+ s->fmt.dwEffects &= ~(style->dwMask);
+ s->fmt.dwEffects |= style->dwEffects & style->dwMask;
+ s->fmt.dwMask |= style->dwMask;
+ if (style->dwMask & CFM_COLOR)
+ {
+ if (style->dwEffects & CFE_AUTOCOLOR)
+ s->fmt.dwEffects |= CFE_AUTOCOLOR;
+ else
+ s->fmt.dwEffects &= ~CFE_AUTOCOLOR;
+ }
+ return s;
+}
+
+void ME_CopyCharFormat(CHARFORMAT2W *pDest, CHARFORMAT2W *pSrc)
+{
+ /* using this with non-2W structs is forbidden */
+ assert(pSrc->cbSize == sizeof(CHARFORMAT2W));
+ assert(pDest->cbSize == sizeof(CHARFORMAT2W));
+ CopyMemory(pDest, pSrc, sizeof(CHARFORMAT2W));
+}
+
+static void ME_DumpStyleEffect(char **p, const char *name, CHARFORMAT2W *fmt, int mask)
+{
+ *p += sprintf(*p, "%-22s%s\n", name, (fmt->dwMask & mask) ? ((fmt->dwEffects & mask) ? "YES" : "no") : "N/A");
+}
+
+void ME_DumpStyle(ME_Style *s)
+{
+ char buf[2048];
+ ME_DumpStyleToBuf(&s->fmt, buf);
+ TRACE("%s\n", buf);
+}
+
+void ME_DumpStyleToBuf(CHARFORMAT2W *pFmt, char buf[2048])
+{
+ /* FIXME only CHARFORMAT styles implemented */
+ /* this function sucks, doesn't check for buffer overruns but it's "good enough" as for debug code */
+ char *p;
+ p = buf;
+ p += sprintf(p, "Font face: ");
+ if (pFmt->dwMask & CFM_FACE) {
+ WCHAR *q = pFmt->szFaceName;
+ while(*q) {
+ *p++ = (*q > 255) ? '?' : *q;
+ q++;
+ }
+ } else
+ p += sprintf(p, "N/A");
+
+ if (pFmt->dwMask & CFM_SIZE)
+ p += sprintf(p, "\nFont size: %d\n", (int)pFmt->yHeight);
+ else
+ p += sprintf(p, "\nFont size: N/A\n");
+
+ if (pFmt->dwMask & CFM_OFFSET)
+ p += sprintf(p, "Char offset: %d\n", (int)pFmt->yOffset);
+ else
+ p += sprintf(p, "Char offset: N/A\n");
+
+ if (pFmt->dwMask & CFM_CHARSET)
+ p += sprintf(p, "Font charset: %d\n", (int)pFmt->bCharSet);
+ else
+ p += sprintf(p, "Font charset: N/A\n");
+
+ /* I'm assuming CFM_xxx and CFE_xxx are the same values, fortunately it IS true wrt used flags*/
+ ME_DumpStyleEffect(&p, "Font bold:", pFmt, CFM_BOLD);
+ ME_DumpStyleEffect(&p, "Font italic:", pFmt, CFM_ITALIC);
+ ME_DumpStyleEffect(&p, "Font underline:", pFmt, CFM_UNDERLINE);
+ ME_DumpStyleEffect(&p, "Font strikeout:", pFmt, CFM_STRIKEOUT);
+ p += sprintf(p, "Text color: ");
+ if (pFmt->dwMask & CFM_COLOR)
+ {
+ if (pFmt->dwEffects & CFE_AUTOCOLOR)
+ p += sprintf(p, "auto\n");
+ else
+ p += sprintf(p, "%06x\n", (int)pFmt->crTextColor);
+ }
+ else
+ p += sprintf(p, "N/A\n");
+ ME_DumpStyleEffect(&p, "Text protected:", pFmt, CFM_PROTECTED);
+}
+
+void ME_LogFontFromStyle(HDC hDC, LOGFONTW *lf, ME_Style *s)
+{
+ int rx, ry;
+ rx = GetDeviceCaps(hDC, LOGPIXELSX);
+ ry = GetDeviceCaps(hDC, LOGPIXELSY);
+ ZeroMemory(lf, sizeof(LOGFONTW));
+ lstrcpyW(lf->lfFaceName, s->fmt.szFaceName);
+ lf->lfHeight = -s->fmt.yHeight*ry/1440;
+ lf->lfWeight = 400;
+ if (s->fmt.dwEffects & s->fmt.dwMask & CFM_BOLD)
+ lf->lfWeight = 700;
+ if (s->fmt.dwEffects & s->fmt.dwMask & CFM_WEIGHT)
+ lf->lfWeight = s->fmt.wWeight;
+ if (s->fmt.dwEffects & s->fmt.dwMask & CFM_ITALIC)
+ lf->lfItalic = 1;
+ if (s->fmt.dwEffects & s->fmt.dwMask & CFM_UNDERLINE)
+ lf->lfUnderline = 1;
+ if (s->fmt.dwEffects & s->fmt.dwMask & CFM_STRIKEOUT)
+ lf->lfStrikeOut = 1;
+/*lf.lfQuality = PROOF_QUALITY; */
+ lf->lfPitchAndFamily = s->fmt.bPitchAndFamily;
+ lf->lfCharSet = s->fmt.bCharSet;
+}
+
+
+BOOL ME_IsFontEqual(LOGFONTW *p1, LOGFONTW *p2)
+{
+ if (memcmp(p1, p2, sizeof(LOGFONTW)-sizeof(p1->lfFaceName)))
+ return FALSE;
+ if (lstrcmpW(p1->lfFaceName, p2->lfFaceName))
+ return FALSE;
+ return TRUE;
+}
+
+HFONT ME_SelectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s)
+{
+ HFONT hOldFont;
+ LOGFONTW lf;
+ int i, nEmpty, nAge = 0x7FFFFFFF;
+ ME_FontCacheItem *item;
+ assert(hDC);
+ assert(s);
+
+ ME_LogFontFromStyle(hDC, &lf, s);
+
+ for (i=0; i<HFONT_CACHE_SIZE; i++)
+ editor->pFontCache[i].nAge++;
+ for (i=0, nEmpty=-1, nAge=0; i<HFONT_CACHE_SIZE; i++)
+ {
+ item = &editor->pFontCache[i];
+ if (!item->nRefs)
+ {
+ if (item->nAge > nAge)
+ nEmpty = i, nAge = item->nAge;
+ }
+ if (ME_IsFontEqual(&item->lfSpecs, &lf))
+ break;
+ }
+ if (i < HFONT_CACHE_SIZE) /* found */
+ {
+ item = &editor->pFontCache[i];
+ TRACE("font reused %d\n", i);
+
+ s->hFont = item->hFont;
+ item->nRefs++;
+ }
+ else
+ {
+ item = &editor->pFontCache[nEmpty]; /* this legal even when nEmpty == -1, as we don't dereference it */
+
+ assert(nEmpty != -1); /* otherwise we leak cache entries or get too many fonts at once*/
+ if (item->hFont) {
+ TRACE("font deleted %d\n", nEmpty);
+ DeleteObject(item->hFont);
+ item->hFont = NULL;
+ }
+ s->hFont = CreateFontIndirectW(&lf);
+ assert(s->hFont);
+ TRACE("font created %d\n", nEmpty);
+ item->hFont = s->hFont;
+ item->nRefs = 1;
+ memcpy(&item->lfSpecs, &lf, sizeof(LOGFONTW));
+ }
+ hOldFont = SelectObject(hDC, s->hFont);
+ /* should be cached too, maybe ? */
+ GetTextMetricsW(hDC, &s->tm);
+ return hOldFont;
+}
+
+void ME_UnselectStyleFont(ME_TextEditor *editor, HDC hDC, ME_Style *s, HFONT hOldFont)
+{
+ int i;
+
+ assert(hDC);
+ assert(s);
+ SelectObject(hDC, hOldFont);
+ for (i=0; i<HFONT_CACHE_SIZE; i++)
+ {
+ ME_FontCacheItem *pItem = &editor->pFontCache[i];
+ if (pItem->hFont == s->hFont && pItem->nRefs > 0)
+ {
+ pItem->nRefs--;
+ pItem->nAge = 0;
+ s->hFont = NULL;
+ return;
+ }
+ }
+ assert(0 == "UnselectStyleFont without SelectStyleFont");
+}
+
+void ME_DestroyStyle(ME_Style *s) {
+ if (s->hFont)
+ {
+ DeleteObject(s->hFont);
+ s->hFont = NULL;
+ }
+ FREE_OBJ(s);
+}
+
+void ME_AddRefStyle(ME_Style *s)
+{
+ assert(s->nRefs>0); /* style with 0 references isn't supposed to exist */
+ s->nRefs++;
+ all_refs++;
+}
+
+void ME_ReleaseStyle(ME_Style *s)
+{
+ s->nRefs--;
+ all_refs--;
+ if (s->nRefs==0)
+ TRACE("destroy style %p, total refs=%d\n", s, all_refs);
+ else
+ TRACE("release style %p, new refs=%d, total refs=%d\n", s, s->nRefs, all_refs);
+ if (!all_refs) FIXME("all style references freed (good!)\n");
+ assert(s->nRefs>=0);
+ if (!s->nRefs)
+ ME_DestroyStyle(s);
+}
+
+ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor) {
+ if (ME_IsSelection(editor))
+ {
+ ME_Cursor c;
+ int from, to;
+
+ ME_GetSelection(editor, &from, &to);
+ ME_CursorFromCharOfs(editor, from, &c);
+ ME_AddRefStyle(c.pRun->member.run.style);
+ return c.pRun->member.run.style;
+ }
+ if (editor->pBuffer->pCharStyle) {
+ ME_AddRefStyle(editor->pBuffer->pCharStyle);
+ return editor->pBuffer->pCharStyle;
+ }
+ else
+ {
+ ME_Cursor *pCursor = &editor->pCursors[nCursor];
+ ME_DisplayItem *pRunItem = pCursor->pRun;
+ ME_DisplayItem *pPrevItem = NULL;
+ if (pCursor->nOffset) {
+ ME_Run *pRun = &pRunItem->member.run;
+ ME_AddRefStyle(pRun->style);
+ return pRun->style;
+ }
+ pPrevItem = ME_FindItemBack(pRunItem, diRunOrParagraph);
+ if (pPrevItem->type == diRun)
+ {
+ ME_AddRefStyle(pPrevItem->member.run.style);
+ return pPrevItem->member.run.style;
+ }
+ else
+ {
+ ME_AddRefStyle(pRunItem->member.run.style);
+ return pRunItem->member.run.style;
+ }
+ }
+}
+
+void ME_SaveTempStyle(ME_TextEditor *editor)
+{
+ ME_Style *old_style = editor->pBuffer->pCharStyle;
+ editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
+ if (old_style)
+ ME_ReleaseStyle(old_style);
+}
+
+void ME_ClearTempStyle(ME_TextEditor *editor)
+{
+ if (!editor->pBuffer->pCharStyle) return;
+ ME_ReleaseStyle(editor->pBuffer->pCharStyle);
+ editor->pBuffer->pCharStyle = NULL;
+}
--- /dev/null
+/*
+ * RichEdit - functions dealing with editor object
+ *
+ * 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);
+
+void ME_EmptyUndoStack(ME_TextEditor *editor)
+{
+ ME_DisplayItem *p, *pNext;
+
+ TRACE("Emptying undo stack\n");
+
+ p = editor->pUndoStack;
+ editor->pUndoStack = NULL;
+ while(p) {
+ pNext = p->next;
+ ME_DestroyDisplayItem(p);
+ p = pNext;
+ }
+ p = editor->pRedoStack;
+ editor->pRedoStack = NULL;
+ while(p) {
+ pNext = p->next;
+ ME_DestroyDisplayItem(p);
+ p = pNext;
+ }
+}
+
+ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *pdi) {
+ if (editor->nUndoMode == umIgnore)
+ return NULL;
+ else
+ {
+ ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem);
+ switch(type)
+ {
+ case diUndoEndTransaction:
+ break;
+ case diUndoSetParagraphFormat:
+ assert(pdi);
+ CopyMemory(&pItem->member.para, &pdi->member.para, sizeof(ME_Paragraph));
+ pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
+ CopyMemory(pItem->member.para.pFmt, pdi->member.para.pFmt, sizeof(PARAFORMAT2));
+ break;
+ case diUndoInsertRun:
+ assert(pdi);
+ CopyMemory(&pItem->member.run, &pdi->member.run, sizeof(ME_Run));
+ pItem->member.run.strText = ME_StrDup(pItem->member.run.strText);
+ ME_AddRefStyle(pItem->member.run.style);
+ break;
+ case diUndoSetCharFormat:
+ case diUndoSetDefaultCharFormat:
+ break;
+ case diUndoDeleteRun:
+ case diUndoJoinParagraphs:
+ break;
+ case diUndoSplitParagraph:
+ pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2);
+ pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2);
+ pItem->member.para.pFmt->dwMask = 0;
+
+ break;
+ default:
+ assert(0 == "AddUndoItem, unsupported item type");
+ return NULL;
+ }
+ pItem->type = type;
+ pItem->prev = NULL;
+ if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo)
+ {
+ if (editor->nUndoMode == umAddToUndo)
+ TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type));
+ else
+ TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type));
+ pItem->next = editor->pUndoStack;
+ if (editor->pUndoStack)
+ editor->pUndoStack->prev = pItem;
+ editor->pUndoStack = pItem;
+ /* any new operation (not redo) clears the redo stack */
+ if (editor->nUndoMode == umAddToUndo) {
+ ME_DisplayItem *p = editor->pRedoStack;
+ while(p)
+ {
+ ME_DisplayItem *pp = p->next;
+ ME_DestroyDisplayItem(p);
+ p = pp;
+ }
+ editor->pRedoStack = NULL;
+ }
+ }
+ else if (editor->nUndoMode == umAddToRedo)
+ {
+ TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type));
+ pItem->next = editor->pRedoStack;
+ if (editor->pRedoStack)
+ editor->pRedoStack->prev = pItem;
+ editor->pRedoStack = pItem;
+ }
+ else
+ assert(0);
+ return (ME_UndoItem *)pItem;
+ }
+}
+
+void ME_CommitUndo(ME_TextEditor *editor) {
+ assert(editor->nUndoMode == umAddToUndo);
+
+ /* no transactions, no need to commit */
+ if (!editor->pUndoStack)
+ return;
+
+ /* no need to commit empty transactions */
+ if (editor->pUndoStack->type == diUndoEndTransaction)
+ return;
+
+ ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
+ ME_SendSelChange(editor);
+ editor->nModifyStep++;
+}
+
+void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem)
+{
+ ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
+
+ TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
+
+ switch(pItem->type)
+ {
+ case diUndoEndTransaction:
+ assert(0);
+ case diUndoSetParagraphFormat:
+ {
+ ME_Cursor tmp;
+ ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp);
+ ME_SetParaFormat(editor, ME_FindItemBack(tmp.pRun, diParagraph), pItem->member.para.pFmt);
+ break;
+ }
+ case diUndoSetCharFormat:
+ {
+ ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt);
+ break;
+ }
+ case diUndoSetDefaultCharFormat:
+ {
+ ME_SetDefaultCharFormat(editor, &pItem->member.ustyle->fmt);
+ break;
+ }
+ case diUndoInsertRun:
+ {
+ ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem);
+ break;
+ }
+ case diUndoDeleteRun:
+ {
+ ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen);
+ break;
+ }
+ case diUndoJoinParagraphs:
+ {
+ ME_Cursor tmp;
+ ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
+ /* the only thing that's needed is paragraph offset, so no need to split runs */
+ ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun));
+ break;
+ }
+ case diUndoSplitParagraph:
+ {
+ ME_Cursor tmp;
+ ME_DisplayItem *new_para;
+ ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp);
+ if (tmp.nOffset)
+ tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
+ new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style);
+ assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2));
+ CopyMemory(new_para->member.para.pFmt, pItem->member.para.pFmt, sizeof(PARAFORMAT2));
+ break;
+ }
+ default:
+ assert(0 == "PlayUndoItem, unexpected type");
+ }
+}
+
+void ME_Undo(ME_TextEditor *editor) {
+ ME_DisplayItem *p;
+ ME_UndoMode nMode = editor->nUndoMode;
+
+ assert(nMode == umAddToUndo || nMode == umIgnore);
+
+ /* no undo items ? */
+ if (!editor->pUndoStack)
+ return;
+
+ /* watch out for uncommited transactions ! */
+ assert(editor->pUndoStack->type == diUndoEndTransaction);
+
+ editor->nUndoMode = umAddToRedo;
+ p = editor->pUndoStack->next;
+ ME_DestroyDisplayItem(editor->pUndoStack);
+ do {
+ ME_DisplayItem *pp = p;
+ ME_PlayUndoItem(editor, p);
+ p = p->next;
+ ME_DestroyDisplayItem(pp);
+ } while(p && p->type != diUndoEndTransaction);
+ ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
+ editor->pUndoStack = p;
+ if (p)
+ p->prev = NULL;
+ editor->nUndoMode = nMode;
+ editor->nModifyStep--;
+ ME_UpdateRepaint(editor);
+}
+
+void ME_Redo(ME_TextEditor *editor) {
+ ME_DisplayItem *p;
+ ME_UndoMode nMode = editor->nUndoMode;
+
+ assert(nMode == umAddToUndo || nMode == umIgnore);
+
+ /* no redo items ? */
+ if (!editor->pRedoStack)
+ return;
+
+ /* watch out for uncommited transactions ! */
+ assert(editor->pRedoStack->type == diUndoEndTransaction);
+
+ editor->nUndoMode = umAddBackToUndo;
+ p = editor->pRedoStack->next;
+ ME_DestroyDisplayItem(editor->pRedoStack);
+ do {
+ ME_DisplayItem *pp = p;
+ ME_PlayUndoItem(editor, p);
+ p = p->next;
+ ME_DestroyDisplayItem(pp);
+ } while(p && p->type != diUndoEndTransaction);
+ ME_AddUndoItem(editor, diUndoEndTransaction, NULL);
+ editor->pRedoStack = p;
+ if (p)
+ p->prev = NULL;
+ editor->nUndoMode = nMode;
+ editor->nModifyStep++;
+ ME_UpdateRepaint(editor);
+}
--- /dev/null
+/*
+ * RichEdit - Paragraph wrapping. Don't try to understand it. You've been
+ * warned !
+ *
+ * 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);
+
+/*
+ * Unsolved problems:
+ *
+ * - center and right align in WordPad omits all spaces at the start, we don't
+ * - objects/images are not handled yet
+ * - no tabs
+ */
+
+ME_DisplayItem *ME_MakeRow(int height, int baseline, int width)
+{
+ ME_DisplayItem *item = ME_MakeDI(diStartRow);
+
+ item->member.row.nHeight = height;
+ item->member.row.nBaseline = baseline;
+ item->member.row.nWidth = width;
+ return item;
+}
+
+void ME_BeginRow(ME_WrapContext *wc)
+{
+ wc->pRowStart = NULL;
+ wc->bOverflown = FALSE;
+ wc->pLastSplittableRun = NULL;
+ wc->nAvailWidth = wc->nTotalWidth - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin;
+ wc->pt.x = 0;
+}
+
+void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd)
+{
+ ME_DisplayItem *p, *row, *para;
+ int ascent = 0, descent = 0, width=0, shift = 0, align = 0;
+ /* wrap text */
+ para = ME_GetParagraph(wc->pRowStart);
+ for (p = wc->pRowStart; p!=pEnd; p = p->next)
+ {
+ /* ENDPARA run shouldn't affect row height, except if it's the only run in the paragraph */
+ if (p->type==diRun && ((p==wc->pRowStart) || !(p->member.run.nFlags & MERF_ENDPARA))) { /* FIXME add more run types */
+ if (p->member.run.nAscent>ascent)
+ ascent = p->member.run.nAscent;
+ if (p->member.run.nDescent>descent)
+ descent = p->member.run.nDescent;
+ if (!(p->member.run.nFlags & (MERF_ENDPARA|MERF_SKIPPED)))
+ width += p->member.run.nWidth;
+ }
+ }
+ row = ME_MakeRow(ascent+descent, ascent, width);
+ row->member.row.nYPos = wc->pt.y;
+ row->member.row.nLMargin = (!wc->nRow ? wc->nFirstMargin : wc->nLeftMargin);
+ row->member.row.nRMargin = wc->nRightMargin;
+ assert(para->member.para.pFmt->dwMask & PFM_ALIGNMENT);
+ align = para->member.para.pFmt->wAlignment;
+ if (align == PFA_CENTER)
+ shift = (wc->nAvailWidth-width)/2;
+ if (align == PFA_RIGHT)
+ shift = wc->nAvailWidth-width;
+ for (p = wc->pRowStart; p!=pEnd; p = p->next)
+ {
+ if (p->type==diRun) { /* FIXME add more run types */
+ p->member.run.pt.x += row->member.row.nLMargin+shift;
+ }
+ }
+ ME_InsertBefore(wc->pRowStart, row);
+ wc->nRow++;
+ wc->pt.y += ascent+descent;
+ ME_BeginRow(wc);
+}
+
+void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p)
+{
+ if (wc->pRowStart)
+ ME_InsertRowStart(wc, p->next);
+
+ /*
+ p = p->member.para.prev_para->next;
+ while(p) {
+ if (p->type == diParagraph || p->type == diTextEnd)
+ return;
+ if (p->type == diRun)
+ {
+ ME_Run *run = &p->member.run;
+ TRACE("%s - (%d, %d)\n", debugstr_w(run->strText->szData), run->pt.x, run->pt.y);
+ }
+ p = p->next;
+ }
+ */
+}
+
+void ME_WrapSizeRun(ME_WrapContext *wc, ME_DisplayItem *p)
+{
+ /* FIXME compose style (out of character and paragraph styles) here */
+
+ ME_UpdateRunFlags(wc->context->editor, &p->member.run);
+
+ ME_CalcRunExtent(wc->context, &p->member.run);
+}
+
+ME_DisplayItem *ME_MaximizeSplit(ME_WrapContext *wc, ME_DisplayItem *p, int i)
+{
+ ME_DisplayItem *pp, *piter = p;
+ int j;
+ if (!i)
+ return NULL;
+ j = ME_ReverseFindNonWhitespaceV(p->member.run.strText, i);
+ if (j>0) {
+ pp = ME_SplitRun(wc->context, piter, j);
+ wc->pt.x += piter->member.run.nWidth;
+ return pp;
+ }
+ else
+ {
+ pp = piter;
+ /* omit all spaces before split point */
+ while(piter != wc->pRowStart)
+ {
+ piter = ME_FindItemBack(piter, diRun);
+ if (piter->member.run.nFlags & MERF_WHITESPACE)
+ {
+ pp = piter;
+ continue;
+ }
+ if (piter->member.run.nFlags & MERF_ENDWHITE)
+ {
+ j = ME_ReverseFindNonWhitespaceV(piter->member.run.strText, i);
+ pp = ME_SplitRun(wc->context, piter, i);
+ wc->pt = pp->member.run.pt;
+ return pp;
+ }
+ /* this run is the end of spaces, so the run edge is a good point to split */
+ wc->pt = pp->member.run.pt;
+ wc->bOverflown = TRUE;
+ TRACE("Split point is: %s|%s\n", debugstr_w(piter->member.run.strText->szData), debugstr_w(pp->member.run.strText->szData));
+ return pp;
+ }
+ wc->pt = piter->member.run.pt;
+ return piter;
+ }
+}
+
+ME_DisplayItem *ME_SplitByBacktracking(ME_WrapContext *wc, ME_DisplayItem *p, int loc)
+{
+ ME_DisplayItem *piter = p, *pp;
+ int i, idesp, len;
+ ME_Run *run = &p->member.run;
+
+ idesp = i = ME_CharFromPoint(wc->context->editor, loc, run);
+ len = ME_StrVLen(run->strText);
+ assert(len>0);
+ assert(i<len);
+ if (i) {
+ /* don't split words */
+ i = ME_ReverseFindWhitespaceV(run->strText, i);
+ pp = ME_MaximizeSplit(wc, p, i);
+ if (pp)
+ return pp;
+ }
+ TRACE("Must backtrack to split at: %s\n", debugstr_w(p->member.run.strText->szData));
+ if (wc->pLastSplittableRun)
+ {
+ if (wc->pLastSplittableRun->member.run.nFlags & MERF_GRAPHICS)
+ {
+ wc->pt = wc->ptLastSplittableRun;
+ return wc->pLastSplittableRun;
+ }
+ else if (wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE)
+ {
+ /* the following two lines are just to check if we forgot to call UpdateRunFlags earlier,
+ they serve no other purpose */
+ ME_UpdateRunFlags(wc->context->editor, run);
+ assert((wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE));
+
+ piter = wc->pLastSplittableRun;
+ run = &piter->member.run;
+ len = ME_StrVLen(run->strText);
+ /* don't split words */
+ i = ME_ReverseFindWhitespaceV(run->strText, len);
+ if (i == len)
+ i = ME_ReverseFindNonWhitespaceV(run->strText, len);
+ if (i) {
+ ME_DisplayItem *piter2 = ME_SplitRun(wc->context, piter, i);
+ wc->pt = piter2->member.run.pt;
+ return piter2;
+ }
+ /* splittable = must have whitespaces */
+ assert(0 == "Splittable, but no whitespaces");
+ }
+ else
+ {
+ /* restart from the first run beginning with spaces */
+ wc->pt = wc->ptLastSplittableRun;
+ return wc->pLastSplittableRun;
+ }
+ }
+ TRACE("Backtracking failed, trying desperate: %s\n", debugstr_w(p->member.run.strText->szData));
+ /* OK, no better idea, so assume we MAY split words if we can split at all*/
+ if (idesp)
+ return ME_SplitRun(wc->context, piter, idesp);
+ else
+ if (wc->pRowStart && piter != wc->pRowStart)
+ {
+ /* don't need to break current run, because it's possible to split
+ before this run */
+ wc->bOverflown = TRUE;
+ return piter;
+ }
+ else
+ {
+ /* split point inside first character - no choice but split after that char */
+ int chars = 1;
+ int pos2 = ME_StrRelPos(run->strText, 0, &chars);
+ if (pos2 != len) {
+ /* the run is more than 1 char, so we may split */
+ return ME_SplitRun(wc->context, piter, pos2);
+ }
+ /* the run is one char, can't split it */
+ return piter;
+ }
+}
+
+ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
+{
+ ME_DisplayItem *pp;
+ ME_Run *run;
+ int len;
+
+ assert(p->type == diRun);
+ if (!wc->pRowStart)
+ wc->pRowStart = p;
+ ME_WrapSizeRun(wc, p);
+ run = &p->member.run;
+ run->pt.x = wc->pt.x;
+ run->pt.y = wc->pt.y;
+ len = ME_StrVLen(run->strText);
+
+ if (wc->bOverflown) /* just skipping final whitespaces */
+ {
+ if (run->nFlags & MERF_WHITESPACE) {
+ p->member.run.nFlags |= MERF_SKIPPED;
+ /* wc->pt.x += run->nWidth; */
+ /* skip runs consisting of only whitespaces */
+ return p->next;
+ }
+
+ if (run->nFlags & MERF_STARTWHITE) {
+ /* try to split the run at the first non-white char */
+ int black;
+ black = ME_FindNonWhitespaceV(run->strText, 0);
+ if (black) {
+ wc->bOverflown = FALSE;
+ pp = ME_SplitRun(wc->context, p, black);
+ p->member.run.nFlags |= MERF_SKIPPED;
+/*
+ run->pt = wc->pt;
+ wc->pt.x += run->nWidth;
+ */
+ ME_InsertRowStart(wc, pp);
+ return pp;
+ }
+ }
+ /* black run: the row goes from pRowStart to the previous run */
+ ME_InsertRowStart(wc, p);
+ return p;
+ }
+ /* we're not at the end of the row */
+ /* will current run fit? */
+ if (wc->pt.x + run->nWidth > wc->nAvailWidth)
+ {
+ int loc = wc->nAvailWidth - wc->pt.x;
+ /* total white run ? */
+ if (run->nFlags & MERF_WHITESPACE) {
+ /* let the overflow logic handle it */
+ wc->bOverflown = TRUE;
+ return p;
+ }
+ /* graphics - we can split before */
+ if (run->nFlags & MERF_GRAPHICS) {
+ wc->bOverflown = TRUE;
+ return p;
+ }
+ /* can we separate out the last spaces ? (to use overflow logic later) */
+ if (run->nFlags & MERF_ENDWHITE)
+ {
+ /* we aren't sure if it's *really* necessary, it's a good start however */
+ int black = ME_ReverseFindNonWhitespaceV(run->strText, len);
+ ME_SplitRun(wc->context, p, black);
+ /* handle both parts again */
+ return p;
+ }
+ /* determine the split point by backtracking */
+ pp = ME_SplitByBacktracking(wc, p, loc);
+ if (pp == wc->pRowStart)
+ {
+ /* we had only spaces so far, entire content can be omitted */
+ wc->pt.x = 0;
+ return p->next;
+ }
+ if (p != pp) /* found a suitable split point */
+ {
+ wc->bOverflown = TRUE;
+ return pp;
+ }
+ /* we detected that it's best to split on start of this run */
+ if (wc->bOverflown)
+ return pp;
+ ERR("failure!\n");
+ /* not found anything - writing over margins is the only option left */
+ }
+ if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE))
+ || ((run->nFlags & MERF_GRAPHICS) && (p != wc->pRowStart)))
+ {
+ wc->pLastSplittableRun = p;
+ wc->ptLastSplittableRun = wc->pt;
+ }
+ wc->pt.x += run->nWidth;
+ return p->next;
+}
+
+void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) {
+ ME_DisplayItem *p;
+ ME_WrapContext wc;
+
+ assert(tp->type == diParagraph);
+ if (!(tp->member.para.nFlags & MEPF_REWRAP)) {
+ return;
+ }
+ ME_PrepareParagraphForWrapping(c, tp);
+
+ wc.context = c;
+/* wc.para_style = tp->member.para.style; */
+ wc.style = NULL;
+ wc.nFirstMargin = tp->member.para.nFirstMargin;
+ wc.nLeftMargin = tp->member.para.nLeftMargin;
+ wc.nRightMargin = tp->member.para.nRightMargin;
+ wc.nRow = 0;
+ wc.pt.x = 0;
+ wc.pt.y = 0;
+ wc.nTotalWidth = c->rcView.right - c->rcView.left;
+ wc.nAvailWidth = wc.nTotalWidth - wc.nFirstMargin - wc.nRightMargin;
+ wc.pRowStart = NULL;
+
+ ME_BeginRow(&wc);
+ for (p = tp->next; p!=tp->member.para.next_para; ) {
+ assert(p->type != diStartRow);
+ if (p->type == diRun) {
+ p = ME_WrapHandleRun(&wc, p);
+ continue;
+ }
+ p = p->next;
+ }
+ ME_WrapEndParagraph(&wc, p);
+ tp->member.para.nFlags &= ~MEPF_REWRAP;
+ tp->member.para.nHeight = wc.pt.y;
+}
+
+
+void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp) {
+ ME_DisplayItem *p;
+ /* remove all items that will be reinserted by paragraph wrapper anyway */
+ for (p = tp->next; p!=tp->member.para.next_para; p = p->next) {
+ switch(p->type) {
+ case diStartRow:
+ p = p->prev;
+ ME_Remove(p->next);
+ break;
+ default:
+ break;
+ }
+ }
+ /* join runs that can be joined, set up flags */
+ for (p = tp->next; p!=tp->member.para.next_para; p = p->next) {
+ int changed = 0;
+ switch(p->type) {
+ case diStartRow: assert(0); break; /* should have deleted it */
+ case diRun:
+ while (p->next->type == diRun) { /* FIXME */
+ if (ME_CanJoinRuns(&p->member.run, &p->next->member.run)) {
+ ME_JoinRuns(c->editor, p);
+ changed = 1;
+ }
+ else
+ break;
+ }
+ p->member.run.nFlags &= ~MERF_CALCBYWRAP;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
+ HWND hWnd = editor->hWnd;
+ HDC hDC = GetDC(hWnd);
+ ME_DisplayItem *item;
+ ME_Context c;
+
+ ME_InitContext(&c, editor, hDC);
+ c.pt.x = 0;
+ c.pt.y = 0;
+ item = editor->pBuffer->pFirst->next;
+ while(item != editor->pBuffer->pLast) {
+ BOOL bRedraw = FALSE;
+
+ assert(item->type == diParagraph);
+ if ((item->member.para.nFlags & MEPF_REWRAP)
+ || (item->member.para.nYPos != c.pt.y))
+ bRedraw = TRUE;
+ item->member.para.nYPos = c.pt.y;
+
+ ME_WrapTextParagraph(&c, item);
+
+ if (bRedraw)
+ item->member.para.nFlags |= MEPF_REPAINT;
+
+ c.pt.y += item->member.para.nHeight;
+ item = item->member.para.next_para;
+ }
+ editor->sizeWindow.cx = c.rcView.right-c.rcView.left;
+ editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top;
+ editor->nTotalLength = c.pt.y;
+
+ ME_DestroyContext(&c);
+ ReleaseDC(hWnd, hDC);
+}
DEFINE_GUID(FMTID_DocSummaryInformation,0xD5CDD502,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE);
DEFINE_GUID(FMTID_UserDefinedProperties,0xD5CDD505,0x2E9C,0x101B,0x93,0x97,0x08,0x00,0x2B,0x2C,0xF9,0xAE);
+DEFINE_GUID(IID_IRichEditOle,0x00020D00,0,0,0xC0,0,0,0,0,0,0,0x46);
+DEFINE_GUID(IID_IRichEditOleCallback,0x00020D03,0,0,0xC0,0,0,0,0,0,0,0x46);
+
DEFINE_OLEGUID(IID_StdOle,0x00020430,0,0);
DEFINE_GUID(CLSID_StdFont,0x0be35203,0x8f91,0x11ce,0x9d,0xe3,0x00,0xaa,0x00,0x4b,0xb8,0x51);
#define CFM_FACE 0x20000000
#define CFM_OFFSET 0x10000000
#define CFM_CHARSET 0x08000000
+#define CFM_BACKCOLOR 0x04000000
+#define CFM_LCID 0x02000000
+#define CFM_UNDERLINETYPE 0x00800000
+#define CFM_WEIGHT 0x00400000
+#define CFM_SPACING 0x00200000
+#define CFM_KERNING 0x00100000
+#define CFM_STYLE 0x00080000
+#define CFM_ANIMATION 0x00040000
#define CFM_SUBSCRIPT 0x00030000
#define CFM_SUPERSCRIPT 0x00030000
+#define CFM_REVAUTHOR 0x00008000
+#define CFM_REVISED 0x00004000
+#define CFM_DISABLED 0x00002000
+#define CFM_IMPRINT 0x00001000
+#define CFM_EMBOSS 0x00000800
+#define CFM_SHADOW 0x00000400
+#define CFM_OUTLINE 0x00000200
+#define CFM_HIDDEN 0x00000100
+#define CFM_ALLCAPS 0x00000080
+#define CFM_SMALLCAPS 0x00000040
#define CFM_EFFECTS (CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_COLOR | CFM_STRIKEOUT | CFE_PROTECTED | CFM_LINK)
#define CFE_BOLD 1
#define CFE_ITALIC 2
#define CFE_STRIKEOUT 8
#define CFE_PROTECTED 16
#define CFE_AUTOCOLOR 0x40000000
+#define CFE_AUTOBACKCOLOR 0x04000000
#define CFE_SUBSCRIPT 0x00010000
#define CFE_SUPERSCRIPT 0x00020000
#define IMF_FORCENONE 1