RICHEDIT: better RTF import+bugfiles

Krzysztof Foltman kfoltman at portal.onet.pl
Thu Mar 10 14:27:54 CST 2005


ChangeLog:

  * EM_STREAMIN can now deal with undo in a reasonable manner (no 
multiple undo actions in one EM_STREAMIN)
  * Related changes to undo code (umIgnore mode is now handled correctly)
  * Numerous improvements in the RTF reader: it reads some character 
attributes now (you will have proper small print in license agreements now)
  * Fixed a memory overwrite bug in conversion from CHARFORMAT2A to 
CHARFORMAT2W

Is there any important reason to keep the reader.c use hard tabs instead 
of spaces ? It's completely different than the rest of the editor this 
way, and it isn't really comfortable to have different TAB settings for 
different files of the same component.

Krzysztof
-------------- next part --------------
Index: editor.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/editor.c,v
retrieving revision 1.9
diff -u -r1.9 editor.c
--- editor.c	9 Mar 2005 18:43:18 -0000	1.9
+++ editor.c	10 Mar 2005 20:23:15 -0000
@@ -274,25 +274,151 @@
   return 0;
 }
 
+void ME_RTFCharAttrHook(RTF_Info *info)
+{
+  CHARFORMAT2A fmt;
+  fmt.cbSize = sizeof(fmt);
+  fmt.dwMask = 0;
+  
+  switch(info->rtfMinor)
+  {
+    case rtfBold:
+      fmt.dwMask = CFM_BOLD;
+      fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+      break;
+    case rtfItalic:
+      fmt.dwMask = CFM_ITALIC;
+      fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+      break;
+    case rtfUnderline:
+      fmt.dwMask = CFM_UNDERLINE;
+      fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+      break;
+    case rtfStrikeThru:
+      fmt.dwMask = CFM_STRIKEOUT;
+      fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+      break;
+    case rtfBackColor:
+      fmt.dwMask = CFM_BACKCOLOR;
+      fmt.dwEffects = 0;
+      if (info->rtfParam == 0)
+        fmt.dwEffects = CFE_AUTOBACKCOLOR;
+      else if (info->rtfParam != rtfNoParam)
+      {
+        RTFColor *c = RTFGetColor(info, info->rtfParam);
+        fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
+      }
+      break;
+    case rtfForeColor:
+      fmt.dwMask = CFM_COLOR;
+      fmt.dwEffects = 0;
+      if (info->rtfParam == 0)
+        fmt.dwEffects = CFE_AUTOCOLOR;
+      else if (info->rtfParam != rtfNoParam)
+      {
+        RTFColor *c = RTFGetColor(info, info->rtfParam);
+        fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
+      }
+      break;
+    case rtfFontNum:
+      if (info->rtfParam != rtfNoParam)
+      {
+        RTFFont *f = RTFGetFont(info, info->rtfParam);
+        if (f)
+        {
+          strncpy(fmt.szFaceName, f->rtfFName, sizeof(fmt.szFaceName)-1);
+          fmt.szFaceName[sizeof(fmt.szFaceName)-1] = '\0';
+          fmt.dwMask = CFM_FACE;
+        }
+      }
+      break;
+    case rtfFontSize:
+      fmt.dwMask = CFM_SIZE;
+      if (info->rtfParam != rtfNoParam)
+        fmt.yHeight = info->rtfParam*10;
+      break;
+  }
+  if (fmt.dwMask) {
+    RTFFlushOutputBuffer(info);
+    SendMessageW(info->hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
+  }
+}
+
+void ME_RTFParAttrHook(RTF_Info *info)
+{
+  PARAFORMAT2 fmt;
+  fmt.cbSize = sizeof(fmt);
+  fmt.dwMask = 0;
+  
+  switch(info->rtfMinor)
+  {
+  case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
+    fmt.dwMask = PFM_ALIGNMENT;
+    fmt.wAlignment = PFA_LEFT;
+    break;
+  case rtfQuadLeft:
+  case rtfQuadJust:
+    fmt.dwMask = PFM_ALIGNMENT;
+    fmt.wAlignment = PFA_LEFT;
+    break;
+  case rtfQuadRight:
+    fmt.dwMask = PFM_ALIGNMENT;
+    fmt.wAlignment = PFA_RIGHT;
+    break;
+  case rtfQuadCenter:
+    fmt.dwMask = PFM_ALIGNMENT;
+    fmt.wAlignment = PFA_CENTER;
+    break;
+  }  
+  if (fmt.dwMask) {
+    RTFFlushOutputBuffer(info);
+    SendMessageW(info->hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
+  }
+}
+
+void ME_RTFReadHook(RTF_Info *info) {
+  switch(info->rtfClass)
+  {
+    case rtfControl:
+      switch(info->rtfMajor)
+      {
+        case rtfCharAttr:
+          ME_RTFCharAttrHook(info);
+          break;
+        case rtfParAttr:
+          ME_RTFParAttrHook(info);
+          break;
+      }
+      break;
+  }
+}
+
 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
 {
   RTF_Info parser;
   ME_Style *style;
+  int from, to, to2, nUndoMode;
+  ME_UndoItem *pUI;
 
   TRACE("%p %p\n", stream, editor->hWnd);
   
+  ME_GetSelection(editor, &from, &to);
   if (format & SFF_SELECTION) {
     style = ME_GetSelectionInsertStyle(editor);
-    SendMessageW(editor->hWnd, WM_CLEAR, 0, 0);
+
+    ME_InternalDeleteText(editor, from, to-from);
   }
   else {
     style = editor->pBuffer->pDefaultStyle;
     ME_AddRefStyle(style);
     SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);    
-    SetWindowTextA(editor->hWnd, "");
+    ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
+    from = to = 0;
     ME_ClearTempStyle(editor);
   }
-
+  
+  nUndoMode = editor->nUndoMode;
+  editor->nUndoMode = umIgnore;
   if (format & SF_RTF) {
     /* setup the RTF parser */
     memset(&parser, 0, sizeof parser);
@@ -301,6 +427,7 @@
     parser.hwndEdit = editor->hWnd;
     WriterInit(&parser);
     RTFInit(&parser);
+    RTFSetReadHook(&parser, ME_RTFReadHook);
     BeginFile(&parser);
   
     /* do the parsing */
@@ -311,6 +438,7 @@
     ME_StreamInText(editor, format, stream, style);
   else
     ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
+  ME_GetSelection(editor, &to, &to2);
   /* put the cursor at the top */
   if (!(format & SFF_SELECTION))
     SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
@@ -318,6 +446,16 @@
   {
     /* FIXME where to put cursor now ? */
   }
+  
+  editor->nUndoMode = nUndoMode;
+  pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
+  FIXME("from %d to %d\n", from, to);
+  if (pUI && from < to)
+  {
+    pUI->nStart = from;
+    pUI->nLen = to-from;
+  }
+  ME_CommitUndo(editor);
   ME_ReleaseStyle(style);
 
   return 0;
Index: reader.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/reader.c,v
retrieving revision 1.2
diff -u -r1.2 reader.c
--- reader.c	8 Mar 2005 19:01:25 -0000	1.2
+++ reader.c	10 Mar 2005 20:23:15 -0000
@@ -40,6 +40,7 @@
 #include <string.h>
 #include <stdarg.h>
 #include <stdlib.h>
+#include <assert.h>
 
 #include "rtf.h"
 
@@ -1297,16 +1298,16 @@
 
 	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;
+  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() */
@@ -1989,8 +1990,8 @@
 			else if (info->rtfClass == rtfText)	/* font name */
 			{
 				bp = buf;
-                                while (info->rtfClass == rtfText
-                                        && !RTFCheckCM (info, rtfText, ';'))
+        while (info->rtfClass == rtfText
+          && !RTFCheckCM (info, rtfText, ';'))
 				{
 					*bp++ = info->rtfMajor;
 					(void) RTFGetToken (info);
@@ -3590,6 +3591,16 @@
 	return (1);
 }
 
+/*
+ * Write out a character. Seems to work for the default ANSI codepage,
+ * contrary to TextClass_orig. 
+ */
+
+static void
+TextClass (RTF_Info *info)
+{
+	PutLitChar (info, info->rtfMajor);
+}
 
 /*
  * Write out a character.  rtfMajor contains the input character, rtfMinor
@@ -3597,10 +3608,13 @@
  *
  * If the input character isn't in the charset map, try to print some
  * representation of it.
+ * 
+ * I'm not removing it, because it may be helpful if someone else decides
+ * to rewrite the character handler in a i18n-friendly way 
  */
 
 static void
-TextClass (RTF_Info *info)
+TextClass_orig (RTF_Info *info)
 {
 	char	buf[rtfBufSiz];
 
@@ -3612,11 +3626,12 @@
 		PutStdChar (info, info->rtfMinor);
 	else
 	{
-		if (info->rtfMajor < 128)	/* in ASCII range */
-			sprintf (buf, "[[%c]]", info->rtfMajor);
-		else
+		if (info->rtfMajor < 256)	/* in ASCII range */
+			PutLitChar(info, info->rtfMajor);
+		else {
 			sprintf (buf, "[[\\'%02x]]", info->rtfMajor);
-		PutLitStr (info, buf);
+		  PutLitStr (info, buf);
+		}
 	}
 }
 
@@ -3763,10 +3778,16 @@
 	info->OutputBuffer[info->dwOutputCount++] = c;
 }
 
+void RTFOutputANSIString( RTF_Info *info, char *str, int len )
+{
+  assert(str[len] == '\0');
+	if (len) SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) str);
+}
+
 void RTFFlushOutputBuffer( RTF_Info *info )
 {
 	info->OutputBuffer[info->dwOutputCount] = 0;
-	SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) info->OutputBuffer );
+	RTFOutputANSIString(info, info->OutputBuffer, info->dwOutputCount);
 	info->dwOutputCount = 0;
 }
 
@@ -3776,9 +3797,9 @@
 
 	if( ( len + info->dwOutputCount + 1 ) > sizeof info->OutputBuffer )
 		RTFFlushOutputBuffer( info );
-	if( ( len + 1 ) >= sizeof info->OutputBuffer )
+  if( ( len + 1 ) >= sizeof info->OutputBuffer )
 	{
-		SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) str );
+  	RTFOutputANSIString(info, str, len);
 		return;
 	}
 	strcpy( &info->OutputBuffer[info->dwOutputCount], str );
Index: run.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/run.c,v
retrieving revision 1.4
diff -u -r1.4 run.c
--- run.c	9 Mar 2005 18:43:18 -0000	1.4
+++ run.c	10 Mar 2005 20:23:15 -0000
@@ -302,8 +302,10 @@
   assert(pItem->type == diRun || pItem->type == diUndoInsertRun);
   
   pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
-  pUI->nStart = nCharOfs;
-  pUI->nLen = pItem->member.run.strText->nLen;
+  if (pUI) {
+    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);
Index: style.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/style.c,v
retrieving revision 1.2
diff -u -r1.2 style.c
--- style.c	8 Mar 2005 16:26:23 -0000	1.2
+++ style.c	10 Mar 2005 20:23:15 -0000
@@ -54,7 +54,7 @@
     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));
+    CopyMemory(1+((CHARFORMATW *)to), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
     to->cbSize = sizeof(CHARFORMAT2W);
     return to;
   }
Index: undo.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/undo.c,v
retrieving revision 1.1
diff -u -r1.1 undo.c
--- undo.c	5 Mar 2005 11:19:14 -0000	1.1
+++ undo.c	10 Mar 2005 20:23:15 -0000
@@ -26,6 +26,9 @@
 {
   ME_DisplayItem *p, *pNext;
   
+  if (editor->nUndoMode == umIgnore)
+    return;
+  
   TRACE("Emptying undo stack\n");
 
   p = editor->pUndoStack;
@@ -121,6 +124,10 @@
 }
 
 void ME_CommitUndo(ME_TextEditor *editor) {
+  
+  if (editor->nUndoMode == umIgnore)
+    return;
+  
   assert(editor->nUndoMode == umAddToUndo);
   
   /* no transactions, no need to commit */
@@ -140,6 +147,8 @@
 {
   ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
 
+  if (editor->nUndoMode == umIgnore)
+    return;
   TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
 
   switch(pItem->type)
@@ -202,6 +211,8 @@
   ME_DisplayItem *p;
   ME_UndoMode nMode = editor->nUndoMode;
   
+  if (editor->nUndoMode == umIgnore)
+    return;
   assert(nMode == umAddToUndo || nMode == umIgnore);
   
   /* no undo items ? */
@@ -235,6 +246,8 @@
   
   assert(nMode == umAddToUndo || nMode == umIgnore);
   
+  if (editor->nUndoMode == umIgnore)
+    return;
   /* no redo items ? */
   if (!editor->pRedoStack)
     return;


More information about the wine-patches mailing list