RICHEDIT: WM_COPY/WM_PASTE, RTF export fixes

Krzysztof Foltman kfoltman at portal.onet.pl
Mon Mar 14 17:03:02 CST 2005


ChangeLog:
  * WM_COPY (and WM_CUT) can now put both Unicode and RTF format (thanks 
to Phil Krylov's RTF generator code)
  * new message implemented - WM_PASTE
  * RTF reader: rtfPlain implemented (kind of)
  * RTF writer: rewritten main loop (the old one crashed in some 
circumstances when SFF_SELECTION was used)

There are at least two known bugs in the RTF reader code (apart from 
lack of RTF-8 support):

  - inability to ignore unknown optional destinations (which are 
inserted as garbage)
  - inability to restore the pre-group formatting at the end of the RTF 
group (example: A{\b B}C is supposed to turn bold on just for B, not for 
B and C)

Because of those bugs, RTF reader and writer aren't exactly compatible 
with each other (the writer relies on RTF groups working correctly, 
which isn't the case). In some situations, the pasted text has different 
formatting from the copied text.

I hope to fix those bugs tomorrow.

Krzysztof

-------------- next part --------------
Index: editor.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/editor.c,v
retrieving revision 1.11
diff -u -r1.11 editor.c
--- editor.c	14 Mar 2005 21:41:16 -0000	1.11
+++ editor.c	14 Mar 2005 22:39:53 -0000
@@ -107,12 +107,12 @@
   - EM_UNDO
   + WM_CHAR
   + WM_CLEAR
-  - WM_COPY (lame implementation, no RTF support)
-  - WM_CUT (lame implementation, no RTF support)
+  + WM_COPY
+  + WM_CUT
   + WM_GETDLGCODE (the current implementation is incomplete)
   + WM_GETTEXT (ANSI&Unicode)
   + WM_GETTEXTLENGTH (ANSI version sucks)
-  - WM_PASTE
+  + WM_PASTE
   - WM_SETFONT
   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
   - WM_STYLECHANGING
@@ -270,7 +270,8 @@
     if (nDataSize<STREAMIN_BUFFER_SIZE)
       break;
   } while(1);
-  ME_CommitUndo(editor);    
+  ME_CommitUndo(editor);
+  ME_Repaint(editor);
   return 0;
 }
 
@@ -282,6 +283,11 @@
   
   switch(info->rtfMinor)
   {
+    case rtfPlain:
+      FIXME("rtfPlain: how plain should it be ?\n");
+      fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR;
+      fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
+      break;
     case rtfBold:
       fmt.dwMask = CFM_BOLD;
       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
@@ -449,7 +455,7 @@
   
   editor->nUndoMode = nUndoMode;
   pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
-  FIXME("from %d to %d\n", from, to);
+  TRACE("from %d to %d\n", from, to);
   if (pUI && from < to)
   {
     pUI->nStart = from;
@@ -530,6 +536,70 @@
   return ed;
 }
 
+typedef struct tagME_GlobalDestStruct
+{
+  HGLOBAL hData;
+  int nLength;
+} ME_GlobalDestStruct;
+
+static DWORD CALLBACK ME_AppendToHGLOBAL(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
+{
+  ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
+  int nMaxSize;
+  BYTE *pDest;
+  
+  nMaxSize = GlobalSize(pData->hData);
+  if (pData->nLength+cb+1 >= cb)
+  {
+    /* round up to 2^17 */
+    int nNewSize = (((nMaxSize+cb+1)|0x1FFFF)+1) & 0xFFFE0000;
+    pData->hData = GlobalReAlloc(pData->hData, nNewSize, 0);
+  }
+  pDest = (BYTE *)GlobalLock(pData->hData);
+  memcpy(pDest + pData->nLength, lpBuff, cb);
+  pData->nLength += cb;
+  pDest[pData->nLength] = '\0';
+  GlobalUnlock(pData->hData);
+  *pcb = cb;
+  
+  return 0;
+}
+
+static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
+{
+  ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
+  int i;
+  WORD *pSrc, *pDest;
+  
+  cb = cb >> 1;
+  pDest = (WORD *)lpBuff;
+  pSrc = (WORD *)GlobalLock(pData->hData);
+  for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
+    pDest[i] = pSrc[pData->nLength+i];
+  }    
+  pData->nLength += i;
+  *pcb = 2*i;
+  GlobalUnlock(pData->hData);
+  return 0;
+}
+
+static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
+{
+  ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
+  int i;
+  BYTE *pSrc, *pDest;
+  
+  pDest = lpBuff;
+  pSrc = (BYTE *)GlobalLock(pData->hData);
+  for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
+    pDest[i] = pSrc[pData->nLength+i];
+  }    
+  pData->nLength += i;
+  *pcb = i;
+  GlobalUnlock(pData->hData);
+  return 0;
+}
+
 void ME_DestroyEditor(ME_TextEditor *editor)
 {
   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
@@ -618,7 +688,6 @@
   UNSUPPORTED_MSG(EM_SETWORDBREAKPROC)
   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
   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 */
@@ -805,12 +874,40 @@
     ME_UpdateRepaint(editor);
     return 0;
   }
+  case WM_PASTE:
+  {    
+    DWORD dwFormat = 0;
+    EDITSTREAM es;
+    ME_GlobalDestStruct gds;
+    UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
+    UINT cf = 0;
+
+    if (!OpenClipboard(hWnd))
+      return 0;
+    if (IsClipboardFormatAvailable(nRTFFormat))
+      cf = nRTFFormat, dwFormat = SF_RTF;
+    else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
+      cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
+    else
+      return 0;
+
+    gds.hData = GetClipboardData(cf);
+    gds.nLength = 0;
+    es.dwCookie = (DWORD)&gds;
+    es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
+    SendMessageW(hWnd, EM_STREAMIN, dwFormat|SFF_SELECTION, (LPARAM)&es);
+    
+    CloseClipboard();
+    return 0;
+  }
   case WM_CUT:
   case WM_COPY:
   {
     int from, to, pars;
     WCHAR *data;
     HANDLE hData;
+    EDITSTREAM es;
+    ME_GlobalDestStruct gds;
     
     if (!OpenClipboard(hWnd))
       return 0;
@@ -822,7 +919,17 @@
     data = (WCHAR *)GlobalLock(hData);
     ME_GetTextW(editor, data, from, to-from, TRUE);
     GlobalUnlock(hData);
-    SetClipboardData(CF_UNICODETEXT, hData);
+
+    gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
+    gds.nLength = 0;
+    es.dwCookie = (DWORD)&gds;
+    es.pfnCallback = ME_AppendToHGLOBAL;
+    SendMessageW(hWnd, EM_STREAMOUT, SFF_SELECTION|SF_RTF, (LPARAM)&es);
+    GlobalReAlloc(gds.hData, gds.nLength+1, 0);
+    
+    SetClipboardData(CF_UNICODETEXT, hData);    
+    SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
+    
     CloseClipboard();
     if (msg == WM_CUT)
     {
Index: writer.c
===================================================================
RCS file: /home/wine/wine/dlls/riched20/writer.c,v
retrieving revision 1.1
diff -u -r1.1 writer.c
--- writer.c	14 Mar 2005 21:41:16 -0000	1.1
+++ writer.c	14 Mar 2005 22:39:53 -0000
@@ -1,6 +1,6 @@
 /*
  * RichEdit - RTF writer module
- *
+ * 
  * Copyright 2005 by Phil Krylov
  *
  * This library is free software; you can redistribute it and/or
@@ -19,7 +19,7 @@
  */
 
 #include "editor.h"
-
+ 
 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
 
 
@@ -171,10 +171,9 @@
 
 
 static BOOL
-ME_StreamOutRTFFontAndColorTbl(ME_TextEditor *editor, ME_DisplayItem *pFirstRun, LONG to)
+ME_StreamOutRTFFontAndColorTbl(ME_TextEditor *editor, ME_DisplayItem *pFirstRun, ME_DisplayItem *pLastRun)
 {
   ME_DisplayItem *item = pFirstRun;
-  ME_DisplayItem *pLastRun = ME_FindItemAtOffset(editor, diRun, to, NULL);
   ME_FontTableItem *table = editor->pStream->fonttbl;
   int i;
   
@@ -485,15 +484,15 @@
 static BOOL
 ME_StreamOutRTF(ME_TextEditor *editor, int nStart, int nChars, int dwFormat)
 {
-  int nTo;
-  ME_DisplayItem *para = ME_FindItemAtOffset(editor, diParagraph, nStart, NULL);
-  ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
-  ME_DisplayItem *last_item = ME_FindItemAtOffset(editor, diRun, nStart + nChars, &nTo);
-
+  ME_DisplayItem *p, *pEnd;
+  int nOffset, nEndLen;
+  ME_RunOfsFromCharOfs(editor, nStart, &p, &nOffset);
+  ME_RunOfsFromCharOfs(editor, nStart+nChars, &pEnd, &nEndLen);
+  
   if (!ME_StreamOutRTFHeader(editor, dwFormat))
     return FALSE;
 
-  if (!ME_StreamOutRTFFontAndColorTbl(editor, item, nStart + nChars))
+  if (!ME_StreamOutRTFFontAndColorTbl(editor, p, pEnd))
     return FALSE;
   
   /* TODO: stylesheet table */
@@ -510,48 +509,51 @@
 
   /* TODO: section formatting properties */
 
-  while (para) {
-    ME_DisplayItem *p;
-    int nLen;
-    
-    if (!ME_StreamOutRTFParaProps(editor, para))
-      return FALSE;
-    
-    if (!item) {
-       item = ME_FindItemFwd(para, diRun);
-       nStart = 0;
-    }
-    for (p = item; p && p != para->member.para.next_para; p = p->next) {
-      TRACE("type %d\n", p->type);
-      if (p->type == diRun) {
+  if (!ME_StreamOutRTFParaProps(editor, ME_GetParagraph(p)))
+    return FALSE;
+
+  while(1)
+  {
+    switch(p->type)
+    {
+      case diParagraph:
+        if (!ME_StreamOutRTFParaProps(editor, p))
+          return FALSE;
+        break;
+      case diRun:
+        if (p == pEnd && !nEndLen)
+          break;
         TRACE("flags %xh\n", p->member.run.nFlags);
+        /* TODO: emit embedded objects */
+        if (p->member.run.nFlags & MERF_GRAPHICS)
+          FIXME("embedded objects are not handled\n");
         if (p->member.run.nFlags & MERF_ENDPARA) {
           if (!ME_StreamOutPrint(editor, "\r\n\\par"))
             return FALSE;
           nChars--;
         } else {
+          int nEnd;
+          
           if (!ME_StreamOutPrint(editor, "{"))
             return FALSE;
           TRACE("style %p\n", p->member.run.style);
           if (!ME_StreamOutRTFCharProps(editor, &p->member.run.style->fmt))
             return FALSE;
         
-          /* TODO: emit embedded objects as well as text */
-          nLen = ME_StrLen(p->member.run.strText) - nStart;
-          if (!ME_StreamOutRTFText(editor, p->member.run.strText->szData + nStart,
-                                   (p == last_item ? nTo - nStart : nLen)))
+          nEnd = (p == pEnd) ? nEndLen : ME_StrLen(p->member.run.strText);
+          if (!ME_StreamOutRTFText(editor, p->member.run.strText->szData + nOffset, nEnd - nOffset))
             return FALSE;
+          nOffset = 0;
           if (!ME_StreamOutPrint(editor, "}"))
             return FALSE;
         }
-      }
-      if (p == last_item)
         break;
+      default: /* we missed the last item */
+        assert(0);
     }
-    para = para->member.para.next_para;
-    item = NULL;
-    if (p == last_item)
+    if (p == pEnd)
       break;
+    p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
   }
   if (!ME_StreamOutPrint(editor, "}"))
     return FALSE;
@@ -562,6 +564,7 @@
 static BOOL
 ME_StreamOutText(ME_TextEditor *editor, int nStart, int nChars, DWORD dwFormat)
 {
+  /* FIXME: use ME_RunOfsFromCharOfs */
   ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
   int nLen;
   UINT nCodePage = CP_ACP;


More information about the wine-patches mailing list