Sync to Wine-20050419:
Krzysztof Foltman <wdev@foltman.com>
- removed trailing spaces from some files
- tab support
- indent support
- PageUp key support.
- Support for "normal" scroll bar functionality (line and page
  scrolling).
- made string operations consistent wrt whitespace handling (which
  should greatly improve stability of the wrap code and eliminate
  regressions of the most recent versions)
- completely new scrollbar handling (much more reliable) and related
  redraw fixes
- Page Down handler (no Page Up yet, fixing wrap/redraw/scrollbar bugs
  was of higher priority)
- RTF reader doesn't use RichEdit messages anymore (which saves on
  unnecessary repaints)
- added unicode character support to RTF import (like: \u12345 ?)
- small fixes
- fixed whitespace identification bug
- removed drawing of paragraph marks
- improved stub implementations for IRichEditOle
- Unknown destinations are now correctly skipped (so loading an RTF
  file generated by, for example, OpenOffice doesn't produce lots of
  garbage anymore).
- Format stack for RTF groups (so that RTF reader can correctly read
  what RTF writer wrote :) )
- 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.
- Optimized repaint of the area below the text.
- ME_JoinRuns didn't mark the paragraph for rewrapping, fixed.
- Removed PostQuitMessage(0) from WM_DESTROY handler (duh!).
- Use of EM_GETOLEINTERFACE is reported with FIXME instead of TRACE
  (any app using this message is likely to encounter major problems).
- 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).
- The meaning of the rewrap flag got inverted (MEPF_REWRAP instead of
  MEPF_WRAPPED) for consistency.
- Major code cleanups in rewrap/repaint code, leading to "smarter"
  behaviour wrt repainting selections.
- Old font management replaced by the cache-based one, which keeps
  maximum of 10 HFONTs at once, instead of one per a couple of runs.
- EM_CANPASTE implemented
- updated TODO list (including list of (un)implemented messages)
- fixed WM_PASTE (the previous version might not close the clipboard
  if it didn't contain a usable format)
- scrollbar operations (like clicking on arrows) should update
  scrollbar's current position
Gerald Pfeifer <gerald@pfeifer.com>
- Make ME_ArrowLeft() return a value in every case.
Phil Krylov <phil@newstar.rinet.ru>
- Make RTF reader fall back to simple text if a correct RTF header is
  not detected. This should fix some installers.
- Made RTF reader and writer handle codepages mostly similar to the
  original riched20.dll.
- Fixed support for RTF documents using ANSI charset and added support
  for multibyte charsets, so that BIG5 and UTF-8 RTF documents are
  working now.
- Replaced slow and outdated character set handling in RTF reader by
  Unicode/codepages support. Added charset->codepage conversion.
- Implemented hash table lookup for RTF keywords in RTF reader.
- Added "generator" RTF destination handling.
- Initial implementation of EM_STREAMOUT and RTF writer.
- Fixed \u keyword to output signed 16-bit values. Also fixed CP_SYMBOL
  conversion and the detection of the default font's codepage.
- Improved RTF export.
Mike McCormack <mike@codeweavers.com>
- Remove casts and unused code.
Hannu Valtonen <Hannu.Valtonen@hut.fi>
- Added mousewheel support.
Vincent Beron <vberon@mecano.gme.usherb.ca>
- Use "" for Windows includes in dlls, instead of <>.
Jakob Eriksson <jakov@vmlinux.org>
- Get rid of HeapAlloc casts.
Jason Edmeades <us@the-edmeades.demon.co.uk>
- Correct memory allocation macro.
Modified: trunk/reactos/lib/riched20/Makefile.in
Modified: trunk/reactos/lib/riched20/caret.c
Modified: trunk/reactos/lib/riched20/editor.c
Modified: trunk/reactos/lib/riched20/editor.h
Modified: trunk/reactos/lib/riched20/editstr.h
Modified: trunk/reactos/lib/riched20/paint.c
Modified: trunk/reactos/lib/riched20/para.c
Modified: trunk/reactos/lib/riched20/reader.c
Modified: trunk/reactos/lib/riched20/richole.c
Modified: trunk/reactos/lib/riched20/rtf.h
Modified: trunk/reactos/lib/riched20/run.c
Modified: trunk/reactos/lib/riched20/string.c
Modified: trunk/reactos/lib/riched20/style.c
Modified: trunk/reactos/lib/riched20/undo.c
Modified: trunk/reactos/lib/riched20/wrap.c
Added: trunk/reactos/lib/riched20/writer.c
Modified: trunk/reactos/w32api/include/richedit.h

Modified: trunk/reactos/lib/riched20/Makefile.in
--- trunk/reactos/lib/riched20/Makefile.in	2005-05-05 18:59:14 UTC (rev 15013)
+++ trunk/reactos/lib/riched20/Makefile.in	2005-05-05 19:00:49 UTC (rev 15014)
@@ -20,7 +20,8 @@
 	string.c \
 	style.c \
 	undo.c \
-	wrap.c
+	wrap.c \
+	writer.c
 
 @MAKE_DLL_RULES@
 

Modified: trunk/reactos/lib/riched20/caret.c
--- trunk/reactos/lib/riched20/caret.c	2005-05-05 18:59:14 UTC (rev 15013)
+++ trunk/reactos/lib/riched20/caret.c	2005-05-05 19:00:49 UTC (rev 15014)
@@ -104,15 +104,15 @@
         {
           row = ME_FindItemBack(tmp, diStartRow);
           pSizeRun = run = tmp;
-          sz = ME_GetRunSize(&c, &run->member.run, ME_StrLen(run->member.run.strText));
+          sz = ME_GetRunSize(&c, &para->member.para, &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);
+        sz = ME_GetRunSize(&c, &para->member.para, &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));
+        para->member.para.nYPos+row->member.row.nBaseline+pSizeRun->member.run.pt.y-pSizeRun->member.run.nAscent-ME_GetYScrollPos(editor));
     } else {
       assert(0 == "Wrapped paragraph run without a row?");
       CreateCaret(editor->hWnd, NULL, 0, 10);
@@ -300,12 +300,6 @@
   assert(style);
   editor->bCaretAtEnd = FALSE;
 
-  /*
-  if (!style)
-    style = ME_GetInsertStyle(editor, nCursor);
-  else
-    ME_AddRefStyle(style);
-    */
   ME_AddRefStyle(style);
   
   /* FIXME really HERE ? */
@@ -317,10 +311,30 @@
     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')
+  while(pos-str < len && *pos != '\r' && *pos != '\n' && *pos != '\t')
     pos++;
-  /* handle EOLs */
-  if (pos-str < len) {   
+  if (pos-str < len && *pos == '\t') { /* handle tabs */
+    ME_DisplayItem *pNewRun = NULL;
+    WCHAR tab = '\t';
+
+    if (pos!=str)
+      ME_InsertTextFromCursor(editor, nCursor, str, pos-str, style);
+    
+    p = &editor->pCursors[nCursor];
+    assert(style);
+    assert(p->pRun->type == diRun);
+    pNewRun = ME_MakeRun(style, ME_MakeStringN(&tab, 1), MERF_TAB); /* addrefs style */
+    ME_InsertRun(editor, ME_CharOfsFromRunOfs(editor, p->pRun, p->nOffset), pNewRun);
+    ME_DestroyDisplayItem(pNewRun);
+    ME_ReleaseStyle(style);
+
+    pos++;
+    if(pos-str < len) {
+      ME_InsertTextFromCursor(editor, nCursor, pos, len-(pos-str), style);
+    }
+    return;
+  }
+  if (pos-str < len) {   /* handle EOLs */
     ME_DisplayItem *tp, *end_run;
     ME_Paragraph *para;
     ME_Style *tmp_style;
@@ -404,6 +418,7 @@
     }
     assert(0);
   }
+  return FALSE;
 }
 
 BOOL ME_ArrowRight(ME_TextEditor *editor, ME_Cursor *p)
@@ -537,7 +552,7 @@
   
   editor->nUDArrowX = -1;
   
-  y += GetScrollPos(editor->hWnd, SB_VERT);  
+  y += ME_GetYScrollPos(editor);
 
   tmp_cursor = editor->pCursors[0];
   is_selection = ME_IsSelection(editor);
@@ -568,7 +583,7 @@
 {
   ME_Cursor tmp_cursor;
   
-  y += GetScrollPos(editor->hWnd, SB_VERT);  
+  y += ME_GetYScrollPos(editor);
 
   tmp_cursor = editor->pCursors[0];
   if (!ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd))
@@ -702,6 +717,109 @@
   assert(pCursor->pRun->type == diRun);
 }
 
+void ME_ArrowPageUp(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+  ME_DisplayItem *pRun = pCursor->pRun;
+  ME_DisplayItem *pLast, *p;
+  int x, y, ys, yd, yp, yprev;
+  ME_Cursor tmp_curs = *pCursor;
+  
+  x = ME_GetXForArrow(editor, pCursor);
+  if (!pCursor->nOffset && editor->bCaretAtEnd)
+    pRun = ME_FindItemBack(pRun, diRun);
+  
+  p = ME_FindItemBack(pRun, diStartRowOrParagraph);
+  assert(p->type == diStartRow);
+  yp = ME_FindItemBack(p, diParagraph)->member.para.nYPos;
+  yprev = ys = y = yp + p->member.row.nYPos;
+  yd = y - editor->sizeWindow.cy;
+  pLast = p;
+  
+  do {
+    p = ME_FindItemBack(p, diStartRowOrParagraph);
+    if (!p)
+      break;
+    if (p->type == diParagraph) { /* crossing paragraphs */
+      if (p->member.para.prev_para == NULL)
+        break;
+      yp = p->member.para.prev_para->member.para.nYPos;
+      continue;
+    }
+    y = yp + p->member.row.nYPos;
+    if (y < yd)
+      break;
+    pLast = p;
+    yprev = y;
+  } while(1);
+  
+  pCursor->pRun = ME_FindRunInRow(editor, pLast, x, &pCursor->nOffset, &editor->bCaretAtEnd);
+  ME_UpdateSelection(editor, &tmp_curs);
+  if (yprev < editor->sizeWindow.cy)
+  {
+    ME_EnsureVisible(editor, ME_FindItemFwd(editor->pBuffer->pFirst, diRun));
+    ME_Repaint(editor);
+  }
+  else {
+    ME_Scroll(editor, 0, ys-yprev);
+    ME_Repaint(editor);
+  }
+  assert(pCursor->pRun);
+  assert(pCursor->pRun->type == diRun);
+}
+
+/* FIXME: in the original RICHEDIT, PageDown always scrolls by the same amount 
+   of pixels, even if it makes the scroll bar position exceed its normal maximum.
+   In such a situation, clicking the scrollbar restores its position back to the
+   normal range (ie. sets it to (doclength-screenheight)). */
+
+void ME_ArrowPageDown(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+  ME_DisplayItem *pRun = pCursor->pRun;
+  ME_DisplayItem *pLast, *p;
+  int x, y, ys, yd, yp, yprev;
+  ME_Cursor tmp_curs = *pCursor;
+  
+  x = ME_GetXForArrow(editor, pCursor);
+  if (!pCursor->nOffset && editor->bCaretAtEnd)
+    pRun = ME_FindItemBack(pRun, diRun);
+  
+  p = ME_FindItemBack(pRun, diStartRowOrParagraph);
+  assert(p->type == diStartRow);
+  yp = ME_FindItemBack(p, diParagraph)->member.para.nYPos;
+  yprev = ys = y = yp + p->member.row.nYPos;
+  yd = y + editor->sizeWindow.cy;
+  pLast = p;
+  
+  do {
+    p = ME_FindItemFwd(p, diStartRowOrParagraph);
+    if (!p)
+      break;
+    if (p->type == diParagraph) {
+      yp = p->member.para.nYPos;
+      continue;
+    }
+    y = yp + p->member.row.nYPos;
+    if (y >= yd)
+      break;
+    pLast = p;
+    yprev = y;
+  } while(1);
+  
+  pCursor->pRun = ME_FindRunInRow(editor, pLast, x, &pCursor->nOffset, &editor->bCaretAtEnd);
+  ME_UpdateSelection(editor, &tmp_curs);
+  if (yprev >= editor->nTotalLength-editor->sizeWindow.cy)
+  {
+    ME_EnsureVisible(editor, ME_FindItemBack(editor->pBuffer->pLast, diRun));
+    ME_Repaint(editor);
+  }
+  else {
+    ME_Scroll(editor, 0, ys-yprev);
+    ME_Repaint(editor);
+  }
+  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);
@@ -799,45 +917,44 @@
     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)
+BOOL ME_UpdateSelection(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 TRUE;
     }
-    return;
+    return FALSE;
   }
   else
   {
     if (!memcmp(pTempCursor, &editor->pCursors[1], sizeof(ME_Cursor))) /* starting selection */
     {
       editor->pCursors[1] = *pTempCursor;
+      return TRUE;
     }
   }
 
   ME_Repaint(editor);
+  return TRUE;
 }
 
+void ME_RepaintSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor)
+{
+  if (ME_UpdateSelection(editor, pTempCursor)) {
+    ME_EnsureVisible(editor, editor->pCursors[0].pRun); 
+    ME_Repaint(editor);
+  }
+}
+
 void ME_DeleteSelection(ME_TextEditor *editor)
 {
   int from, to;
@@ -898,6 +1015,16 @@
       ME_RepaintSelection(editor, &tmp_curs);
       ME_SendSelChange(editor);
       return TRUE;
+    case VK_PRIOR:
+      ME_ArrowPageUp(editor, p);
+      ME_ClearTempStyle(editor);
+      ME_SendSelChange(editor);
+      return TRUE;
+    case VK_NEXT:
+      ME_ArrowPageDown(editor, p);
+      ME_ClearTempStyle(editor);
+      ME_SendSelChange(editor);
+      return TRUE;
   }
   
   editor->nUDArrowX = -1;
@@ -914,6 +1041,7 @@
       }
       if (ME_ArrowLeft(editor, p)) {
         editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */
+        ME_ClearTempStyle(editor);
         ME_MoveCaret(editor);
         ME_DeleteTextAtCursor(editor, nCursor, 1);
         ME_UpdateRepaint(editor);
@@ -927,10 +1055,12 @@
       if (ME_IsSelection(editor))
       {
         ME_DeleteSelection(editor);
+        ME_ClearTempStyle(editor);
         ME_UpdateRepaint(editor);
         return TRUE;
       }
       ME_DeleteTextAtCursor(editor, nCursor, 1);
+      ME_ClearTempStyle(editor);
       ME_UpdateRepaint(editor);
       return TRUE;
     }

Modified: trunk/reactos/lib/riched20/editor.c
--- trunk/reactos/lib/riched20/editor.c	2005-05-05 18:59:14 UTC (rev 15013)
+++ trunk/reactos/lib/riched20/editor.c	2005-05-05 19:00:49 UTC (rev 15014)
@@ -23,7 +23,7 @@
   
   Messages (ANSI versions not done yet)
   - EM_AUTOURLDETECT 2.0
-  - EM_CANPASTE
+  + EM_CANPASTE
   + EM_CANREDO 2.0
   + EM_CANUNDO
   - EM_CHARFROMPOS
@@ -73,7 +73,7 @@
   - EM_LINESCROLL
   - EM_PASTESPECIAL
   - EM_POSFROMCHARS
-  - EM_REDO 2.0
+  + EM_REDO 2.0
   - EM_REQUESTRESIZE
   + EM_REPLACESEL (proper style?) ANSI&Unicode
   - EM_SCROLL
@@ -102,17 +102,17 @@
   - EM_SETWORDBREAKPROCEX
   - EM_SETWORDWRAPMODE 1.0asian
   - EM_STOPGROUPTYPING 2.0
-  - EM_STREAMIN
-  - EM_STREAMOUT
-  - EM_UNDO
+  + EM_STREAMIN (can't fall back to text when the RTF isn't really RTF)
+  + EM_STREAMOUT
+  + 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
@@ -166,30 +166,25 @@
 /*
  * RICHED20 TODO (incomplete):
  *
- * - font caching
+ * - messages/styles/notifications listed above 
+ * - Undo coalescing 
  * - add remaining CHARFORMAT/PARAFORMAT fields
  * - right/center align should strip spaces from the beginning
- * - more advanced navigation (Ctrl-arrows, PageUp/PageDn)
+ * - more advanced navigation (Ctrl-arrows)
  * - tabs
- * - pictures (not just smiling faces that lack API support ;-) )
- * - OLE objects
+ * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
+ * - COM interface (looks like a major pain in the TODO list)
  * - 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
+ * - italic caret 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)
@@ -203,11 +198,11 @@
  */
 
 #include "editor.h"
-#include <ole2.h>
-#include <richole.h>
-#include <winreg.h>
+#include "ole2.h"
+#include "richole.h"
+#include "winreg.h"
 #define NO_SHLWAPI_STREAM 
-#include <shlwapi.h>
+#include "shlwapi.h"
 
 #include "rtf.h"
  
@@ -238,91 +233,388 @@
   return buf;
 }
 
-#define STREAMIN_BUFFER_SIZE 4096 /* M$ compatibility */
 
-static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream, ME_Style *style)
+static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
 {
-  BYTE buffer[STREAMIN_BUFFER_SIZE+1];
   WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
+  WCHAR *pText;
   
   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;
+    long nWideChars = 0;
+
+    if (!stream->dwSize)
+    {
+      ME_StreamInFill(stream);
+      if (stream->editstream->dwError)
+        break;
+      if (!stream->dwSize)
+        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);
+      nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
+      pText = wszText;
     }
     else
-      nWideChars = nDataSize>>1;
-    ME_InsertTextFromCursor(editor, 0, wszText, nWideChars, style);
-    if (nDataSize<STREAMIN_BUFFER_SIZE)
+    {
+      nWideChars = stream->dwSize >> 1;
+      pText = (WCHAR *)stream->buffer;
+    }
+    
+    ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
+    if (stream->dwSize < STREAMIN_BUFFER_SIZE)
       break;
   } while(1);
-  ME_CommitUndo(editor);    
+  ME_CommitUndo(editor);
+  ME_Repaint(editor);
   return 0;
 }
 
+void ME_RTFCharAttrHook(RTF_Info *info)
+{
+  CHARFORMAT2W fmt;
+  fmt.cbSize = sizeof(fmt);
+  fmt.dwMask = 0;
+  
+  switch(info->rtfMinor)
+  {
+    case rtfPlain:
+      /* FIXME add more flags once they're implemented */
+      fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
+      fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
+      fmt.yHeight = 12*20; /* 12pt */
+      fmt.wWeight = 400;
+      break;
+    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)
+        {
+          MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
+          fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
+          fmt.bCharSet = f->rtfFCharSet;
+          fmt.dwMask = CFM_FACE | CFM_CHARSET;
+        }
+      }
+      break;
+    case rtfFontSize:
+      fmt.dwMask = CFM_SIZE;
+      if (info->rtfParam != rtfNoParam)
+        fmt.yHeight = info->rtfParam*10;
+      break;
+  }
+  if (fmt.dwMask) {
+    ME_Style *style2;
+    RTFFlushOutputBuffer(info);
+    /* FIXME too slow ? how come ? */
+    style2 = ME_ApplyStyle(info->style, &fmt);
+    ME_ReleaseStyle(info->style);
+    info->style = style2;
+  }
+}
+
+/* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
+   the same tags mean different things in different contexts */
+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 | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT;
+    fmt.wAlignment = PFA_LEFT;
+    fmt.cTabCount = 0;
+    fmt.dxOffset = fmt.dxStartIndent = 0;
+    break;
+  case rtfFirstIndent:
+    ME_GetSelectionParaFormat(info->editor, &fmt);
+    fmt.dwMask = PFM_STARTINDENT;
+    fmt.dxStartIndent = info->rtfParam + fmt.dxOffset;
+    break;
+  case rtfLeftIndent:
+  {
+    int first, left;
+    ME_GetSelectionParaFormat(info->editor, &fmt);
+    first = fmt.dxStartIndent;
+    left = info->rtfParam;
+    fmt.dwMask = PFM_STARTINDENT|PFM_OFFSET;
+    fmt.dxStartIndent = first + left;
+    fmt.dxOffset = -first;
+    break;
+  }
+  case rtfRightIndent:
+    fmt.dwMask = PFM_RIGHTINDENT;
+    fmt.dxRightIndent = info->rtfParam;
+    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;
+  case rtfTabPos:
+    ME_GetSelectionParaFormat(info->editor, &fmt);
+    if (!(fmt.dwMask & PFM_TABSTOPS))
+    {
+      fmt.dwMask |= PFM_TABSTOPS;
+      fmt.cTabCount = 0;
+    }
+    if (fmt.cTabCount < MAX_TAB_STOPS)
+      fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
+    break;
+  }  
+  if (fmt.dwMask) {
+    RTFFlushOutputBuffer(info);
+    /* FIXME too slow ? how come ?*/
+    ME_SetSelectionParaFormat(info->editor, &fmt);
+  }
+}
+
+void ME_RTFReadHook(RTF_Info *info) {
+  switch(info->rtfClass)
+  {
+    case rtfGroup:
+      switch(info->rtfMajor)
+      {
+        case rtfBeginGroup:
+          if (info->stackTop < maxStack) {
+            memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
+            info->stack[info->stackTop].codePage = info->codePage;
+            info->stack[info->stackTop].unicodeLength = info->unicodeLength;
+          }
+          info->stackTop++;
+          break;
+        case rtfEndGroup:
+        {
+          ME_Style *s;
+          RTFFlushOutputBuffer(info);
+          info->stackTop--;
+          /* FIXME too slow ? how come ? */
+          s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
+          ME_ReleaseStyle(info->style);
+          info->style = s;
+          info->codePage = info->stack[info->stackTop].codePage;
+          info->unicodeLength = info->stack[info->stackTop].unicodeLength;
+          break;
+        }
+      }
+      break;
+    case rtfControl:
+      switch(info->rtfMajor)
+      {
+        case rtfCharAttr:
+          ME_RTFCharAttrHook(info);
+          break;
+        case rtfParAttr:
+          ME_RTFParAttrHook(info);
+          break;
+      }
+      break;
+  }
+}
+
+void
+ME_StreamInFill(ME_InStream *stream)
+{
+  stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
+                                                                stream->buffer,
+                                                                sizeof(stream->buffer),
+                                                                &stream->dwSize);
+  stream->dwUsed = 0;
+}
+
 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;
+  int nEventMask = editor->nEventMask;
+  ME_InStream inStream;
 
   TRACE("%p %p\n", stream, editor->hWnd);
+  editor->nEventMask = 0;
   
+  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);
+    /* FIXME restore default paragraph formatting ! */
   }
+  
+  nUndoMode = editor->nUndoMode;
+  editor->nUndoMode = umIgnore;
 
-  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);
+  inStream.editstream = stream;
+  inStream.editstream->dwError = 0;
+  inStream.dwSize = 0;
+  inStream.dwUsed = 0;
+
+  if (format & SF_RTF)
+  {
+    /* Check if it's really RTF, and if it is not, use plain text */
+    ME_StreamInFill(&inStream);
+    if (!inStream.editstream->dwError)
+    {
+      if (strncmp(inStream.buffer, "{\\rtf1", 6) && strncmp(inStream.buffer, "{\\urtf", 6))
+      {
+        format &= ~SF_RTF;
+        format |= SF_TEXT;
+      }
+    }
+  }
+
+  if (!inStream.editstream->dwError)
+  {
+    if (format & SF_RTF) {
+      /* setup the RTF parser */
+      memset(&parser, 0, sizeof parser);
+      RTFSetEditStream(&parser, &inStream);
+      parser.rtfFormat = format&(SF_TEXT|SF_RTF);
+      parser.hwndEdit = editor->hWnd;
+      parser.editor = editor;
+      parser.style = style;
+      WriterInit(&parser);
+      RTFInit(&parser);
+      RTFSetReadHook(&parser, ME_RTFReadHook);
+      BeginFile(&parser);
   
-    /* do the parsing */
-    RTFRead(&parser);
-    RTFFlushOutputBuffer(&parser);
+      /* do the parsing */
+      RTFRead(&parser);
+      RTFFlushOutputBuffer(&parser);
+      RTFDestroy(&parser);
+
+      style = parser.style;
+    }
+    else if (format & SF_TEXT)
+      ME_StreamInText(editor, format, &inStream, 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);
+    else
+    {
+      /* FIXME where to put cursor now ? */
+    }
   }
-  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
+  
+  editor->nUndoMode = nUndoMode;
+  pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
+  TRACE("from %d to %d\n", from, to);
+  if (pUI && from < to)
   {
-    /* FIXME where to put cursor now ? */
+    pUI->nStart = from;
+    pUI->nLen = to-from;
   }
-  ME_ReleaseStyle(style);
+  ME_CommitUndo(editor);
+  ME_ReleaseStyle(style); 
+  editor->nEventMask = nEventMask;
+  InvalidateRect(editor->hWnd, NULL, TRUE);
+  ME_UpdateRepaint(editor);
+  if (!(format & SFF_SELECTION)) {
+    ME_ClearTempStyle(editor);
+  }
+  ME_MoveCaret(editor);
+  ME_SendSelChange(editor);
 
   return 0;
 }
 
+
+ME_DisplayItem *
+ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset)
+{
+  ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph);
+  
+  while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset)
+    item = ME_FindItemFwd(item, diParagraph);
+
+  if (!item)
+    return item;
+
+  nOffset -= item->member.para.nCharOfs;
+  if (nItemType == diParagraph) {
+    if (nItemOffset)
+      *nItemOffset = nOffset;
+    return item;
+  }
+  
+  do {
+    item = ME_FindItemFwd(item, diRun);
+  } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset));
+  if (item) {
+    nOffset -= item->member.run.nCharOfs;
+    if (nItemOffset)
+      *nItemOffset = nOffset;
+  }
+  return item;
+}
+
+
 ME_TextEditor *ME_MakeEditor(HWND hWnd) {
   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
   HDC hDC;
@@ -340,7 +632,6 @@
   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;
@@ -351,6 +642,7 @@
   ed->nUndoMode = umAddToUndo;
   ed->nParagraphs = 1;
   ed->nLastSelStart = ed->nLastSelEnd = 0;
+  ed->nScrollPosY = 0;
   for (i=0; i<HFONT_CACHE_SIZE; i++)
   {
     ed->pFontCache[i].nRefs = 0;
@@ -361,6 +653,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;
@@ -401,7 +757,6 @@
   switch(msg) {
   
   UNSUPPORTED_MSG(EM_AUTOURLDETECT)
-  UNSUPPORTED_MSG(EM_CANPASTE)
   UNSUPPORTED_MSG(EM_CHARFROMPOS)
   UNSUPPORTED_MSG(EM_DISPLAYBAND)
   UNSUPPORTED_MSG(EM_EXLIMITTEXT)
@@ -448,9 +803,7 @@
   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 */
@@ -459,6 +812,8 @@
   
   case EM_STREAMIN:
    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam);
+  case EM_STREAMOUT:
+   return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
   case WM_GETDLGCODE:
   {
     UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS;
@@ -557,6 +912,7 @@
   case EM_SETCHARFORMAT:
   {
     CHARFORMAT2W buf, *p;
+    BOOL bRepaint = TRUE;
     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
     if (!wParam)
       ME_SetDefaultCharFormat(editor, p);
@@ -564,10 +920,15 @@
       FIXME("word selection not supported\n");
     else if (wParam == SCF_ALL)
       ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
-    else
+    else {
+      int from, to;
+      ME_GetSelection(editor, &from, &to);
+      bRepaint = (from != to);
       ME_SetSelectionCharFormat(editor, p);
+    }
     ME_CommitUndo(editor);
-    ME_UpdateRepaint(editor);
+    if (bRepaint)
+      ME_UpdateRepaint(editor);
     return 0;
   }
   case EM_GETCHARFORMAT:
@@ -583,6 +944,7 @@
   }
   case EM_SETPARAFORMAT:
     ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
+    ME_UpdateRepaint(editor);
     ME_CommitUndo(editor);
     return 0;
   case EM_GETPARAFORMAT:
@@ -635,12 +997,49 @@
     ME_UpdateRepaint(editor);
     return 0;
   }
+  case EM_CANPASTE:
[truncated at 1000 lines; 4802 more skipped]