ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

editor.c
Go to the documentation of this file.
00001 /*
00002  * RichEdit - functions dealing with editor object
00003  *
00004  * Copyright 2004 by Krzysztof Foltman
00005  * Copyright 2005 by Cihan Altinay
00006  * Copyright 2005 by Phil Krylov
00007  * Copyright 2008 Eric Pouech
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00022  */
00023 
00024 /* 
00025   API implementation status:
00026   
00027   Messages (ANSI versions not done yet)
00028   + EM_AUTOURLDETECT 2.0
00029   + EM_CANPASTE
00030   + EM_CANREDO 2.0
00031   + EM_CANUNDO
00032   + EM_CHARFROMPOS
00033   - EM_DISPLAYBAND
00034   + EM_EMPTYUNDOBUFFER
00035   + EM_EXGETSEL
00036   + EM_EXLIMITTEXT
00037   + EM_EXLINEFROMCHAR
00038   + EM_EXSETSEL
00039   + EM_FINDTEXT (only FR_DOWN flag implemented)
00040   + EM_FINDTEXTEX (only FR_DOWN flag implemented)
00041   - EM_FINDWORDBREAK
00042   - EM_FMTLINES
00043   - EM_FORMATRANGE
00044   + EM_GETAUTOURLDETECT 2.0
00045   - EM_GETBIDIOPTIONS 3.0
00046   - EM_GETCHARFORMAT (partly done)
00047   - EM_GETEDITSTYLE
00048   + EM_GETEVENTMASK
00049   + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
00050   - EM_GETIMECOLOR 1.0asian
00051   - EM_GETIMECOMPMODE 2.0
00052   - EM_GETIMEOPTIONS 1.0asian
00053   - EM_GETIMESTATUS
00054   - EM_GETLANGOPTIONS 2.0
00055   + EM_GETLIMITTEXT
00056   + EM_GETLINE
00057   + EM_GETLINECOUNT   returns number of rows, not of paragraphs
00058   + EM_GETMODIFY
00059   + EM_GETOLEINTERFACE
00060   + EM_GETOPTIONS
00061   + EM_GETPARAFORMAT
00062   + EM_GETPASSWORDCHAR 2.0
00063   - EM_GETPUNCTUATION 1.0asian
00064   + EM_GETRECT
00065   - EM_GETREDONAME 2.0
00066   + EM_GETSEL
00067   + EM_GETSELTEXT (ANSI&Unicode)
00068   + EM_GETSCROLLPOS 3.0
00069 ! - EM_GETTHUMB
00070   + EM_GETTEXTEX 2.0
00071   + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
00072   + EM_GETTEXTMODE 2.0
00073 ? + EM_GETTEXTRANGE (ANSI&Unicode)
00074   - EM_GETTYPOGRAPHYOPTIONS 3.0
00075   - EM_GETUNDONAME
00076   + EM_GETWORDBREAKPROC
00077   - EM_GETWORDBREAKPROCEX
00078   - EM_GETWORDWRAPMODE 1.0asian
00079   + EM_GETZOOM 3.0
00080   + EM_HIDESELECTION
00081   + EM_LIMITTEXT (Also called EM_SETLIMITTEXT)
00082   + EM_LINEFROMCHAR
00083   + EM_LINEINDEX
00084   + EM_LINELENGTH
00085   + EM_LINESCROLL
00086   - EM_PASTESPECIAL
00087   + EM_POSFROMCHAR
00088   + EM_REDO 2.0
00089   + EM_REQUESTRESIZE
00090   + EM_REPLACESEL (proper style?) ANSI&Unicode
00091   + EM_SCROLL
00092   + EM_SCROLLCARET
00093   - EM_SELECTIONTYPE
00094   - EM_SETBIDIOPTIONS 3.0
00095   + EM_SETBKGNDCOLOR
00096   + EM_SETCHARFORMAT (partly done, no ANSI)
00097   - EM_SETEDITSTYLE
00098   + EM_SETEVENTMASK (few notifications supported)
00099   - EM_SETFONTSIZE
00100   - EM_SETIMECOLOR 1.0asian
00101   - EM_SETIMEOPTIONS 1.0asian
00102   - EM_SETIMESTATUS
00103   - EM_SETLANGOPTIONS 2.0
00104   - EM_SETLIMITTEXT
00105   - EM_SETMARGINS
00106   + EM_SETMODIFY (not sure if implementation is correct)
00107   - EM_SETOLECALLBACK
00108   + EM_SETOPTIONS (partially implemented)
00109   - EM_SETPALETTE 2.0
00110   + EM_SETPARAFORMAT
00111   + EM_SETPASSWORDCHAR 2.0
00112   - EM_SETPUNCTUATION 1.0asian
00113   + EM_SETREADONLY no beep on modification attempt
00114   + EM_SETRECT
00115   + EM_SETRECTNP (EM_SETRECT without repainting)
00116   + EM_SETSEL
00117   + EM_SETSCROLLPOS 3.0
00118   - EM_SETTABSTOPS 3.0
00119   - EM_SETTARGETDEVICE (partial)
00120   + EM_SETTEXTEX 3.0 (proper style?)
00121   - EM_SETTEXTMODE 2.0
00122   - EM_SETTYPOGRAPHYOPTIONS 3.0
00123   + EM_SETUNDOLIMIT 2.0
00124   + EM_SETWORDBREAKPROC (used only for word movement at the moment)
00125   - EM_SETWORDBREAKPROCEX
00126   - EM_SETWORDWRAPMODE 1.0asian
00127   + EM_SETZOOM 3.0
00128   + EM_SHOWSCROLLBAR 2.0
00129   + EM_STOPGROUPTYPING 2.0
00130   + EM_STREAMIN
00131   + EM_STREAMOUT
00132   + EM_UNDO
00133   + WM_CHAR
00134   + WM_CLEAR
00135   + WM_COPY
00136   + WM_CUT
00137   + WM_GETDLGCODE (the current implementation is incomplete)
00138   + WM_GETTEXT (ANSI&Unicode)
00139   + WM_GETTEXTLENGTH (ANSI version sucks)
00140   + WM_HSCROLL
00141   + WM_PASTE
00142   + WM_SETFONT
00143   + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
00144   + WM_STYLECHANGING (seems to do nothing)
00145   + WM_STYLECHANGED (seems to do nothing)
00146   + WM_UNICHAR
00147   + WM_VSCROLL
00148 
00149   Notifications
00150 
00151   * EN_CHANGE (sent from the wrong place)
00152   - EN_CORRECTTEXT
00153   - EN_DROPFILES
00154   - EN_ERRSPACE
00155   - EN_HSCROLL
00156   - EN_IMECHANGE
00157   + EN_KILLFOCUS
00158   - EN_LINK
00159   - EN_MAXTEXT
00160   - EN_MSGFILTER
00161   - EN_OLEOPFAILED
00162   - EN_PROTECTED
00163   + EN_REQUESTRESIZE
00164   - EN_SAVECLIPBOARD
00165   + EN_SELCHANGE 
00166   + EN_SETFOCUS
00167   - EN_STOPNOUNDO
00168   * EN_UPDATE (sent from the wrong place)
00169   - EN_VSCROLL
00170   
00171   Styles
00172   
00173   - ES_AUTOHSCROLL
00174   - ES_AUTOVSCROLL
00175   - ES_CENTER
00176   + ES_DISABLENOSCROLL (scrollbar is always visible)
00177   - ES_EX_NOCALLOLEINIT
00178   - ES_LEFT
00179   - ES_MULTILINE (currently single line controls aren't supported)
00180   - ES_NOIME
00181   - ES_READONLY (I'm not sure if beeping is the proper behaviour)
00182   - ES_RIGHT
00183   - ES_SAVESEL
00184   - ES_SELFIME
00185   - ES_SUNKEN
00186   - ES_VERTICAL
00187   - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
00188   - WS_SETFONT
00189   + WS_HSCROLL
00190   + WS_VSCROLL
00191 */
00192 
00193 /*
00194  * RICHED20 TODO (incomplete):
00195  *
00196  * - messages/styles/notifications listed above 
00197  * - add remaining CHARFORMAT/PARAFORMAT fields
00198  * - right/center align should strip spaces from the beginning
00199  * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
00200  * - COM interface (looks like a major pain in the TODO list)
00201  * - calculate heights of pictures (half-done)
00202  * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
00203  * - find/replace
00204  * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
00205  * - italic caret with italic fonts
00206  * - IME
00207  * - most notifications aren't sent at all (the most important ones are)
00208  * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
00209  * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
00210  * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
00211  * - full justification
00212  * - hyphenation
00213  * - tables
00214  * - ListBox & ComboBox not implemented
00215  *
00216  * Bugs that are probably fixed, but not so easy to verify:
00217  * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
00218  * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
00219  * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
00220  * - caret shouldn't be displayed when selection isn't empty
00221  * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
00222  * - undo for setting default format (done, might be buggy)
00223  * - styles might be not released properly (looks like they work like charm, but who knows?
00224  *
00225  */
00226 
00227 #include "editor.h"
00228 #include "commdlg.h"
00229 #include "winreg.h"
00230 #define NO_SHLWAPI_STREAM 
00231 #include "shlwapi.h"
00232 #include "rtf.h"
00233 #include "imm.h"
00234 #include "res.h"
00235 
00236 #define STACK_SIZE_DEFAULT  100
00237 #define STACK_SIZE_MAX     1000
00238 
00239 #define TEXT_LIMIT_DEFAULT 32767
00240  
00241 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
00242 
00243 static BOOL ME_RegisterEditorClass(HINSTANCE);
00244 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars);
00245 
00246 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','0','W', 0};
00247 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','0','W', 0};
00248 static HCURSOR hLeft;
00249 
00250 int me_debug = 0;
00251 HANDLE me_heap = NULL;
00252 
00253 static BOOL ME_ListBoxRegistered = FALSE;
00254 static BOOL ME_ComboBoxRegistered = FALSE;
00255 
00256 static inline int is_version_nt(void)
00257 {
00258     return !(GetVersion() & 0x80000000);
00259 }
00260 
00261 static ME_TextBuffer *ME_MakeText(void) {
00262   
00263   ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
00264 
00265   ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
00266   ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
00267   
00268   p1->prev = NULL;
00269   p1->next = p2;
00270   p2->prev = p1;
00271   p2->next = NULL;
00272   p1->member.para.next_para = p2;
00273   p2->member.para.prev_para = p1;
00274   p2->member.para.nCharOfs = 0;  
00275   
00276   buf->pFirst = p1;
00277   buf->pLast = p2;
00278   buf->pCharStyle = NULL;
00279   
00280   return buf;
00281 }
00282 
00283 
00284 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
00285 {
00286   WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
00287   WCHAR *pText;
00288   LRESULT total_bytes_read = 0;
00289 
00290   TRACE("%08x %p\n", dwFormat, stream);
00291 
00292   do {
00293     LONG nWideChars = 0;
00294 
00295     if (!stream->dwSize)
00296     {
00297       ME_StreamInFill(stream);
00298       if (stream->editstream->dwError)
00299         break;
00300       if (!stream->dwSize)
00301         break;
00302       total_bytes_read += stream->dwSize;
00303     }
00304 
00305     if (!(dwFormat & SF_UNICODE))
00306     {
00307       /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
00308       nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
00309       pText = wszText;
00310     }
00311     else
00312     {
00313       nWideChars = stream->dwSize >> 1;
00314       pText = (WCHAR *)stream->buffer;
00315     }
00316 
00317     ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
00318     if (stream->dwSize == 0)
00319       break;
00320     stream->dwSize = 0;
00321   } while(1);
00322   return total_bytes_read;
00323 }
00324 
00325 static void ME_ApplyBorderProperties(RTF_Info *info,
00326                                      ME_BorderRect *borderRect,
00327                                      RTFBorder *borderDef)
00328 {
00329   int i, colorNum;
00330   ME_Border *pBorders[] = {&borderRect->top,
00331                            &borderRect->left,
00332                            &borderRect->bottom,
00333                            &borderRect->right};
00334   for (i = 0; i < 4; i++)
00335   {
00336     RTFColor *colorDef = info->colorList;
00337     pBorders[i]->width = borderDef[i].width;
00338     colorNum = borderDef[i].color;
00339     while (colorDef && colorDef->rtfCNum != colorNum)
00340       colorDef = colorDef->rtfNextColor;
00341     if (colorDef)
00342       pBorders[i]->colorRef = RGB(
00343                            colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0,
00344                            colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0,
00345                            colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0);
00346     else
00347       pBorders[i]->colorRef = RGB(0, 0, 0);
00348   }
00349 }
00350 
00351 void ME_RTFCharAttrHook(RTF_Info *info)
00352 {
00353   CHARFORMAT2W fmt;
00354   fmt.cbSize = sizeof(fmt);
00355   fmt.dwMask = 0;
00356   fmt.dwEffects = 0;
00357 
00358   switch(info->rtfMinor)
00359   {
00360     case rtfPlain:
00361       /* FIXME add more flags once they're implemented */
00362       fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINETYPE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
00363       fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
00364       fmt.yHeight = 12*20; /* 12pt */
00365       fmt.wWeight = FW_NORMAL;
00366       fmt.bUnderlineType = CFU_UNDERLINENONE;
00367       break;
00368     case rtfBold:
00369       fmt.dwMask = CFM_BOLD | CFM_WEIGHT;
00370       fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0;
00371       fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL;
00372       break;
00373     case rtfItalic:
00374       fmt.dwMask = CFM_ITALIC;
00375       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
00376       break;
00377     case rtfUnderline:
00378       fmt.dwMask = CFM_UNDERLINETYPE;
00379       fmt.bUnderlineType = info->rtfParam ? CFU_CF1UNDERLINE : CFU_UNDERLINENONE;
00380       break;
00381     case rtfDotUnderline:
00382       fmt.dwMask = CFM_UNDERLINETYPE;
00383       fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEDOTTED : CFU_UNDERLINENONE;
00384       break;
00385     case rtfDbUnderline:
00386       fmt.dwMask = CFM_UNDERLINETYPE;
00387       fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEDOUBLE : CFU_UNDERLINENONE;
00388       break;
00389     case rtfWordUnderline:
00390       fmt.dwMask = CFM_UNDERLINETYPE;
00391       fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEWORD : CFU_UNDERLINENONE;
00392       break;
00393     case rtfNoUnderline:
00394       fmt.dwMask = CFM_UNDERLINETYPE;
00395       fmt.bUnderlineType = CFU_UNDERLINENONE;
00396       break;
00397     case rtfStrikeThru:
00398       fmt.dwMask = CFM_STRIKEOUT;
00399       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
00400       break;
00401     case rtfSubScript:
00402     case rtfSuperScript:
00403     case rtfSubScrShrink:
00404     case rtfSuperScrShrink:
00405     case rtfNoSuperSub:
00406       fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
00407       if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
00408       if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
00409       if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
00410       break;
00411     case rtfInvisible:
00412       fmt.dwMask = CFM_HIDDEN;
00413       fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
00414       break;
00415     case rtfBackColor:
00416       fmt.dwMask = CFM_BACKCOLOR;
00417       fmt.dwEffects = 0;
00418       if (info->rtfParam == 0)
00419         fmt.dwEffects = CFE_AUTOBACKCOLOR;
00420       else if (info->rtfParam != rtfNoParam)
00421       {
00422         RTFColor *c = RTFGetColor(info, info->rtfParam);
00423         if (c && c->rtfCBlue >= 0)
00424           fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
00425         else
00426           fmt.dwEffects = CFE_AUTOBACKCOLOR;
00427       }
00428       break;
00429     case rtfForeColor:
00430       fmt.dwMask = CFM_COLOR;
00431       fmt.dwEffects = 0;
00432       if (info->rtfParam == 0)
00433         fmt.dwEffects = CFE_AUTOCOLOR;
00434       else if (info->rtfParam != rtfNoParam)
00435       {
00436         RTFColor *c = RTFGetColor(info, info->rtfParam);
00437         if (c && c->rtfCBlue >= 0)
00438           fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
00439         else {
00440           fmt.dwEffects = CFE_AUTOCOLOR;
00441         }
00442       }
00443       break;
00444     case rtfFontNum:
00445       if (info->rtfParam != rtfNoParam)
00446       {
00447         RTFFont *f = RTFGetFont(info, info->rtfParam);
00448         if (f)
00449         {
00450           MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
00451           fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
00452           fmt.bCharSet = f->rtfFCharSet;
00453           fmt.dwMask = CFM_FACE | CFM_CHARSET;
00454           fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4);
00455         }
00456       }
00457       break;
00458     case rtfFontSize:
00459       fmt.dwMask = CFM_SIZE;
00460       if (info->rtfParam != rtfNoParam)
00461         fmt.yHeight = info->rtfParam*10;
00462       break;
00463   }
00464   if (fmt.dwMask) {
00465     ME_Style *style2;
00466     RTFFlushOutputBuffer(info);
00467     /* FIXME too slow ? how come ? */
00468     style2 = ME_ApplyStyle(info->style, &fmt);
00469     ME_ReleaseStyle(info->style);
00470     info->style = style2;
00471     info->styleChanged = TRUE;
00472   }
00473 }
00474 
00475 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
00476    the same tags mean different things in different contexts */
00477 void ME_RTFParAttrHook(RTF_Info *info)
00478 {
00479   PARAFORMAT2 fmt;
00480   fmt.cbSize = sizeof(fmt);
00481   fmt.dwMask = 0;
00482   
00483   switch(info->rtfMinor)
00484   {
00485   case rtfParDef: /* restores default paragraph attributes */
00486     if (!info->editor->bEmulateVersion10) /* v4.1 */
00487       info->borderType = RTFBorderParaLeft;
00488     else /* v1.0 - 3.0 */
00489       info->borderType = RTFBorderParaTop;
00490     fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS |
00491         PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE |
00492         PFM_STARTINDENT;
00493     /* TODO: numbering, shading */
00494     fmt.wAlignment = PFA_LEFT;
00495     fmt.cTabCount = 0;
00496     fmt.dxOffset = fmt.dxStartIndent = fmt.dxRightIndent = 0;
00497     fmt.wBorderWidth = fmt.wBorders = 0;
00498     fmt.wBorderSpace = 0;
00499     fmt.bLineSpacingRule = 0;
00500     fmt.dySpaceBefore = fmt.dySpaceAfter = 0;
00501     fmt.dyLineSpacing = 0;
00502     if (!info->editor->bEmulateVersion10) /* v4.1 */
00503     {
00504       if (info->tableDef && info->tableDef->tableRowStart &&
00505           info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
00506       {
00507         ME_Cursor cursor;
00508         ME_DisplayItem *para;
00509         /* We are just after a table row. */
00510         RTFFlushOutputBuffer(info);
00511         cursor = info->editor->pCursors[0];
00512         para = cursor.pPara;
00513         if (para  == info->tableDef->tableRowStart->member.para.next_para
00514             && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs)
00515         {
00516           /* Since the table row end, no text has been inserted, and the \intbl
00517            * control word has not be used.  We can confirm that we are not in a
00518            * table anymore.
00519            */
00520           info->tableDef->tableRowStart = NULL;
00521           info->canInheritInTbl = FALSE;
00522         }
00523       }
00524     } else { /* v1.0 - v3.0 */
00525       fmt.dwMask |= PFM_TABLE;
00526       fmt.wEffects &= ~PFE_TABLE;
00527     }
00528     break;
00529   case rtfNestLevel:
00530     if (!info->editor->bEmulateVersion10) /* v4.1 */
00531     {
00532       while (info->rtfParam > info->nestingLevel) {
00533         RTFTable *tableDef = ALLOC_OBJ(RTFTable);
00534         ZeroMemory(tableDef, sizeof(RTFTable));
00535         tableDef->parent = info->tableDef;
00536         info->tableDef = tableDef;
00537 
00538         RTFFlushOutputBuffer(info);
00539         if (tableDef->tableRowStart &&
00540             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
00541         {
00542           ME_DisplayItem *para = tableDef->tableRowStart;
00543           para = para->member.para.next_para;
00544           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
00545           tableDef->tableRowStart = para;
00546         } else {
00547           ME_Cursor cursor;
00548           WCHAR endl = '\r';
00549           cursor = info->editor->pCursors[0];
00550           if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
00551             ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
00552           tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
00553         }
00554 
00555         info->nestingLevel++;
00556       }
00557       info->canInheritInTbl = FALSE;
00558     }
00559     break;
00560   case rtfInTable:
00561   {
00562     if (!info->editor->bEmulateVersion10) /* v4.1 */
00563     {
00564       if (info->nestingLevel < 1)
00565       {
00566         RTFTable *tableDef;
00567         if (!info->tableDef)
00568         {
00569             info->tableDef = ALLOC_OBJ(RTFTable);
00570             ZeroMemory(info->tableDef, sizeof(RTFTable));
00571         }
00572         tableDef = info->tableDef;
00573         RTFFlushOutputBuffer(info);
00574         if (tableDef->tableRowStart &&
00575             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
00576         {
00577           ME_DisplayItem *para = tableDef->tableRowStart;
00578           para = para->member.para.next_para;
00579           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
00580           tableDef->tableRowStart = para;
00581         } else {
00582           ME_Cursor cursor;
00583           WCHAR endl = '\r';
00584           cursor = info->editor->pCursors[0];
00585           if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
00586             ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
00587           tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
00588         }
00589         info->nestingLevel = 1;
00590         info->canInheritInTbl = TRUE;
00591       }
00592       return;
00593     } else { /* v1.0 - v3.0 */
00594       fmt.dwMask |= PFM_TABLE;
00595       fmt.wEffects |= PFE_TABLE;
00596     }
00597     break;
00598   }
00599   case rtfFirstIndent:
00600     ME_GetSelectionParaFormat(info->editor, &fmt);
00601     fmt.dwMask = PFM_STARTINDENT | PFM_OFFSET;
00602     fmt.dxStartIndent += fmt.dxOffset + info->rtfParam;
00603     fmt.dxOffset = -info->rtfParam;
00604     break;
00605   case rtfLeftIndent:
00606     ME_GetSelectionParaFormat(info->editor, &fmt);
00607     fmt.dwMask = PFM_STARTINDENT;
00608     fmt.dxStartIndent = info->rtfParam - fmt.dxOffset;
00609     break;
00610   case rtfRightIndent:
00611     fmt.dwMask = PFM_RIGHTINDENT;
00612     fmt.dxRightIndent = info->rtfParam;
00613     break;
00614   case rtfQuadLeft:
00615   case rtfQuadJust:
00616     fmt.dwMask = PFM_ALIGNMENT;
00617     fmt.wAlignment = PFA_LEFT;
00618     break;
00619   case rtfQuadRight:
00620     fmt.dwMask = PFM_ALIGNMENT;
00621     fmt.wAlignment = PFA_RIGHT;
00622     break;
00623   case rtfQuadCenter:
00624     fmt.dwMask = PFM_ALIGNMENT;
00625     fmt.wAlignment = PFA_CENTER;
00626     break;
00627   case rtfTabPos:
00628     ME_GetSelectionParaFormat(info->editor, &fmt);
00629     if (!(fmt.dwMask & PFM_TABSTOPS))
00630     {
00631       fmt.cTabCount = 0;
00632     }
00633     if (fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000)
00634       fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
00635     fmt.dwMask = PFM_TABSTOPS;
00636     break;
00637   case rtfKeep:
00638     fmt.dwMask = PFM_KEEP;
00639     fmt.wEffects = PFE_KEEP;
00640     break;
00641   case rtfNoWidowControl:
00642     fmt.dwMask = PFM_NOWIDOWCONTROL;
00643     fmt.wEffects = PFE_NOWIDOWCONTROL;
00644     break;
00645   case rtfKeepNext:
00646     fmt.dwMask = PFM_KEEPNEXT;
00647     fmt.wEffects = PFE_KEEPNEXT;
00648     break;
00649   case rtfSpaceAfter:
00650     fmt.dwMask = PFM_SPACEAFTER;
00651     fmt.dySpaceAfter = info->rtfParam;
00652     break;
00653   case rtfSpaceBefore:
00654     fmt.dwMask = PFM_SPACEBEFORE;
00655     fmt.dySpaceBefore = info->rtfParam;
00656     break;
00657   case rtfSpaceBetween:
00658     fmt.dwMask = PFM_LINESPACING;
00659     if ((int)info->rtfParam > 0)
00660     {
00661       fmt.dyLineSpacing = info->rtfParam;
00662       fmt.bLineSpacingRule = 3;
00663     }
00664     else
00665     {
00666       fmt.dyLineSpacing = info->rtfParam;
00667       fmt.bLineSpacingRule = 4;
00668     }
00669     break;
00670   case rtfSpaceMultiply:
00671     fmt.dwMask = PFM_LINESPACING;
00672     fmt.dyLineSpacing = info->rtfParam * 20;
00673     fmt.bLineSpacingRule = 5;
00674     break;
00675   case rtfParBullet:
00676     fmt.dwMask = PFM_NUMBERING;
00677     fmt.wNumbering = PFN_BULLET;
00678     break;
00679   case rtfParSimple:
00680     fmt.dwMask = PFM_NUMBERING;
00681     fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
00682     break;
00683   case rtfParNumDecimal:
00684     fmt.dwMask = PFM_NUMBERING;
00685     fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
00686     break;
00687   case rtfParNumIndent:
00688     fmt.dwMask = PFM_NUMBERINGTAB;
00689     fmt.wNumberingTab = info->rtfParam;
00690     break;
00691   case rtfParNumStartAt:
00692     fmt.dwMask = PFM_NUMBERINGSTART;
00693     fmt.wNumberingStart = info->rtfParam;
00694     break;
00695   case rtfBorderLeft:
00696     info->borderType = RTFBorderParaLeft;
00697     ME_GetSelectionParaFormat(info->editor, &fmt);
00698     if (!(fmt.dwMask & PFM_BORDER))
00699     {
00700       fmt.wBorderSpace = 0;
00701       fmt.wBorderWidth = 1;
00702       fmt.wBorders = 0;
00703     }
00704     fmt.wBorders |= 1;
00705     fmt.dwMask = PFM_BORDER;
00706     break;
00707   case rtfBorderRight:
00708     info->borderType = RTFBorderParaRight;
00709     ME_GetSelectionParaFormat(info->editor, &fmt);
00710     if (!(fmt.dwMask & PFM_BORDER))
00711     {
00712       fmt.wBorderSpace = 0;
00713       fmt.wBorderWidth = 1;
00714       fmt.wBorders = 0;
00715     }
00716     fmt.wBorders |= 2;
00717     fmt.dwMask = PFM_BORDER;
00718     break;
00719   case rtfBorderTop:
00720     info->borderType = RTFBorderParaTop;
00721     ME_GetSelectionParaFormat(info->editor, &fmt);
00722     if (!(fmt.dwMask & PFM_BORDER))
00723     {
00724       fmt.wBorderSpace = 0;
00725       fmt.wBorderWidth = 1;
00726       fmt.wBorders = 0;
00727     }
00728     fmt.wBorders |= 4;
00729     fmt.dwMask = PFM_BORDER;
00730     break;
00731   case rtfBorderBottom:
00732     info->borderType = RTFBorderParaBottom;
00733     ME_GetSelectionParaFormat(info->editor, &fmt);
00734     if (!(fmt.dwMask & PFM_BORDER))
00735     {
00736       fmt.wBorderSpace = 0;
00737       fmt.wBorderWidth = 1;
00738       fmt.wBorders = 0;
00739     }
00740     fmt.wBorders |= 8;
00741     fmt.dwMask = PFM_BORDER;
00742     break;
00743   case rtfBorderSingle:
00744     ME_GetSelectionParaFormat(info->editor, &fmt);
00745     /* we assume that borders have been created before (RTF spec) */
00746     fmt.wBorders &= ~0x700;
00747     fmt.wBorders |= 1 << 8;
00748     fmt.dwMask = PFM_BORDER;
00749     break;
00750   case rtfBorderThick:
00751     ME_GetSelectionParaFormat(info->editor, &fmt);
00752     /* we assume that borders have been created before (RTF spec) */
00753     fmt.wBorders &= ~0x700;
00754     fmt.wBorders |= 2 << 8;
00755     fmt.dwMask = PFM_BORDER;
00756     break;
00757   case rtfBorderShadow:
00758     ME_GetSelectionParaFormat(info->editor, &fmt);
00759     /* we assume that borders have been created before (RTF spec) */
00760     fmt.wBorders &= ~0x700;
00761     fmt.wBorders |= 10 << 8;
00762     fmt.dwMask = PFM_BORDER;
00763     break;
00764   case rtfBorderDouble:
00765     ME_GetSelectionParaFormat(info->editor, &fmt);
00766     /* we assume that borders have been created before (RTF spec) */
00767     fmt.wBorders &= ~0x700;
00768     fmt.wBorders |= 7 << 8;
00769     fmt.dwMask = PFM_BORDER;
00770     break;
00771   case rtfBorderDot:
00772     ME_GetSelectionParaFormat(info->editor, &fmt);
00773     /* we assume that borders have been created before (RTF spec) */
00774     fmt.wBorders &= ~0x700;
00775     fmt.wBorders |= 11 << 8;
00776     fmt.dwMask = PFM_BORDER;
00777     break;
00778   case rtfBorderWidth:
00779   {
00780     int borderSide = info->borderType & RTFBorderSideMask;
00781     RTFTable *tableDef = info->tableDef;
00782     ME_GetSelectionParaFormat(info->editor, &fmt);
00783     /* we assume that borders have been created before (RTF spec) */
00784     fmt.wBorderWidth |= ((info->rtfParam / 15) & 7) << 8;
00785     if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell)
00786     {
00787       RTFBorder *border;
00788       if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS)
00789         break;
00790       border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide];
00791       border->width = info->rtfParam;
00792       break;
00793     }
00794     fmt.dwMask = PFM_BORDER;
00795     break;
00796   }
00797   case rtfBorderSpace:
00798     ME_GetSelectionParaFormat(info->editor, &fmt);
00799     /* we assume that borders have been created before (RTF spec) */
00800     fmt.wBorderSpace = info->rtfParam;
00801     fmt.dwMask = PFM_BORDER;
00802     break;
00803   case rtfBorderColor:
00804   {
00805     RTFTable *tableDef = info->tableDef;
00806     int borderSide = info->borderType & RTFBorderSideMask;
00807     int borderType = info->borderType & RTFBorderTypeMask;
00808     switch(borderType)
00809     {
00810     case RTFBorderTypePara:
00811       if (!info->editor->bEmulateVersion10) /* v4.1 */
00812         break;
00813       /* v1.0 - 3.0 treat paragraph and row borders the same. */
00814     case RTFBorderTypeRow:
00815       if (tableDef) {
00816         tableDef->border[borderSide].color = info->rtfParam;
00817       }
00818       break;
00819     case RTFBorderTypeCell:
00820       if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) {
00821         tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam;
00822       }
00823       break;
00824     }
00825     break;
00826   }
00827   }
00828   if (fmt.dwMask) {
00829     RTFFlushOutputBuffer(info);
00830     /* FIXME too slow ? how come ?*/
00831     ME_SetSelectionParaFormat(info->editor, &fmt);
00832   }
00833 }
00834 
00835 void ME_RTFTblAttrHook(RTF_Info *info)
00836 {
00837   switch (info->rtfMinor)
00838   {
00839     case rtfRowDef:
00840     {
00841       if (!info->editor->bEmulateVersion10) /* v4.1 */
00842         info->borderType = 0; /* Not sure */
00843       else /* v1.0 - 3.0 */
00844         info->borderType = RTFBorderRowTop;
00845       if (!info->tableDef) {
00846         info->tableDef = ME_MakeTableDef(info->editor);
00847       } else {
00848         ME_InitTableDef(info->editor, info->tableDef);
00849       }
00850       break;
00851     }
00852     case rtfCellPos:
00853     {
00854       int cellNum;
00855       if (!info->tableDef)
00856       {
00857         info->tableDef = ME_MakeTableDef(info->editor);
00858       }
00859       cellNum = info->tableDef->numCellsDefined;
00860       if (cellNum >= MAX_TABLE_CELLS)
00861         break;
00862       info->tableDef->cells[cellNum].rightBoundary = info->rtfParam;
00863       if (cellNum < MAX_TAB_STOPS) {
00864         /* Tab stops were used to store cell positions before v4.1 but v4.1
00865          * still seems to set the tabstops without using them. */
00866         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
00867         PARAFORMAT2 *pFmt = para->member.para.pFmt;
00868         pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF;
00869         pFmt->rgxTabs[cellNum] = 0x00FFFFFF & info->rtfParam;
00870       }
00871       info->tableDef->numCellsDefined++;
00872       break;
00873     }
00874     case rtfRowBordTop:
00875       info->borderType = RTFBorderRowTop;
00876       break;
00877     case rtfRowBordLeft:
00878       info->borderType = RTFBorderRowLeft;
00879       break;
00880     case rtfRowBordBottom:
00881       info->borderType = RTFBorderRowBottom;
00882       break;
00883     case rtfRowBordRight:
00884       info->borderType = RTFBorderRowRight;
00885       break;
00886     case rtfCellBordTop:
00887       info->borderType = RTFBorderCellTop;
00888       break;
00889     case rtfCellBordLeft:
00890       info->borderType = RTFBorderCellLeft;
00891       break;
00892     case rtfCellBordBottom:
00893       info->borderType = RTFBorderCellBottom;
00894       break;
00895     case rtfCellBordRight:
00896       info->borderType = RTFBorderCellRight;
00897       break;
00898     case rtfRowGapH:
00899       if (info->tableDef)
00900         info->tableDef->gapH = info->rtfParam;
00901       break;
00902     case rtfRowLeftEdge:
00903       if (info->tableDef)
00904         info->tableDef->leftEdge = info->rtfParam;
00905       break;
00906   }
00907 }
00908 
00909 void ME_RTFSpecialCharHook(RTF_Info *info)
00910 {
00911   RTFTable *tableDef = info->tableDef;
00912   switch (info->rtfMinor)
00913   {
00914     case rtfNestCell:
00915       if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
00916         break;
00917       /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */
00918     case rtfCell:
00919       if (!tableDef)
00920         break;
00921       RTFFlushOutputBuffer(info);
00922       if (!info->editor->bEmulateVersion10) { /* v4.1 */
00923         if (tableDef->tableRowStart)
00924         {
00925           if (!info->nestingLevel &&
00926               tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
00927           {
00928             ME_DisplayItem *para = tableDef->tableRowStart;
00929             para = para->member.para.next_para;
00930             para = ME_InsertTableRowStartAtParagraph(info->editor, para);
00931             tableDef->tableRowStart = para;
00932             info->nestingLevel = 1;
00933           }
00934           ME_InsertTableCellFromCursor(info->editor);
00935         }
00936       } else { /* v1.0 - v3.0 */
00937         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
00938         PARAFORMAT2 *pFmt = para->member.para.pFmt;
00939         if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE &&
00940             tableDef->numCellsInserted < tableDef->numCellsDefined)
00941         {
00942           WCHAR tab = '\t';
00943           ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
00944           tableDef->numCellsInserted++;
00945         }
00946       }
00947       break;
00948     case rtfNestRow:
00949       if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
00950         break;
00951       /* else fall through since v4.1 treats rtfNestRow and rtfRow the same */
00952     case rtfRow:
00953     {
00954       ME_DisplayItem *para, *cell, *run;
00955       int i;
00956 
00957       if (!tableDef)
00958         break;
00959       RTFFlushOutputBuffer(info);
00960       if (!info->editor->bEmulateVersion10) { /* v4.1 */
00961         if (!tableDef->tableRowStart)
00962           break;
00963         if (!info->nestingLevel &&
00964             tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
00965         {
00966           para = tableDef->tableRowStart;
00967           para = para->member.para.next_para;
00968           para = ME_InsertTableRowStartAtParagraph(info->editor, para);
00969           tableDef->tableRowStart = para;
00970           info->nestingLevel++;
00971         }
00972         para = tableDef->tableRowStart;
00973         cell = ME_FindItemFwd(para, diCell);
00974         assert(cell && !cell->member.cell.prev_cell);
00975         if (tableDef->numCellsDefined < 1)
00976         {
00977           /* 2000 twips appears to be the cell size that native richedit uses
00978            * when no cell sizes are specified. */
00979           const int defaultCellSize = 2000;
00980           int nRightBoundary = defaultCellSize;
00981           cell->member.cell.nRightBoundary = nRightBoundary;
00982           while (cell->member.cell.next_cell) {
00983             cell = cell->member.cell.next_cell;
00984             nRightBoundary += defaultCellSize;
00985             cell->member.cell.nRightBoundary = nRightBoundary;
00986           }
00987           para = ME_InsertTableCellFromCursor(info->editor);
00988           cell = para->member.para.pCell;
00989           cell->member.cell.nRightBoundary = nRightBoundary;
00990         } else {
00991           for (i = 0; i < tableDef->numCellsDefined; i++)
00992           {
00993             RTFCell *cellDef = &tableDef->cells[i];
00994             cell->member.cell.nRightBoundary = cellDef->rightBoundary;
00995             ME_ApplyBorderProperties(info, &cell->member.cell.border,
00996                                      cellDef->border);
00997             cell = cell->member.cell.next_cell;
00998             if (!cell)
00999             {
01000               para = ME_InsertTableCellFromCursor(info->editor);
01001               cell = para->member.para.pCell;
01002             }
01003           }
01004           /* Cell for table row delimiter is empty */
01005           cell->member.cell.nRightBoundary = tableDef->cells[i-1].rightBoundary;
01006         }
01007 
01008         run = ME_FindItemFwd(cell, diRun);
01009         if (info->editor->pCursors[0].pRun != run ||
01010             info->editor->pCursors[0].nOffset)
01011         {
01012           int nOfs, nChars;
01013           /* Delete inserted cells that aren't defined. */
01014           info->editor->pCursors[1].pRun = run;
01015           info->editor->pCursors[1].pPara = ME_GetParagraph(run);
01016           info->editor->pCursors[1].nOffset = 0;
01017           nOfs = ME_GetCursorOfs(&info->editor->pCursors[1]);
01018           nChars = ME_GetCursorOfs(&info->editor->pCursors[0]) - nOfs;
01019           ME_InternalDeleteText(info->editor, &info->editor->pCursors[1],
01020                                 nChars, TRUE);
01021         }
01022 
01023         para = ME_InsertTableRowEndFromCursor(info->editor);
01024         para->member.para.pFmt->dxOffset = abs(info->tableDef->gapH);
01025         para->member.para.pFmt->dxStartIndent = info->tableDef->leftEdge;
01026         ME_ApplyBorderProperties(info, &para->member.para.border,
01027                                  tableDef->border);
01028         info->nestingLevel--;
01029         if (!info->nestingLevel)
01030         {
01031           if (info->canInheritInTbl) {
01032             tableDef->tableRowStart = para;
01033           } else {
01034             while (info->tableDef) {
01035               tableDef = info->tableDef;
01036               info->tableDef = tableDef->parent;
01037               heap_free(tableDef);
01038             }
01039           }
01040         } else {
01041           info->tableDef = tableDef->parent;
01042           heap_free(tableDef);
01043         }
01044       } else { /* v1.0 - v3.0 */
01045         WCHAR endl = '\r';
01046         ME_DisplayItem *para = info->editor->pCursors[0].pPara;
01047         PARAFORMAT2 *pFmt = para->member.para.pFmt;
01048         pFmt->dxOffset = info->tableDef->gapH;
01049         pFmt->dxStartIndent = info->tableDef->leftEdge;
01050 
01051         ME_ApplyBorderProperties(info, &para->member.para.border,
01052                                  tableDef->border);
01053         while (tableDef->numCellsInserted < tableDef->numCellsDefined)
01054         {
01055           WCHAR tab = '\t';
01056           ME_InsertTextFromCursor(info->editor, 0, &tab, 1, info->style);
01057           tableDef->numCellsInserted++;
01058         }
01059         pFmt->cTabCount = min(tableDef->numCellsDefined, MAX_TAB_STOPS);
01060         if (!tableDef->numCellsDefined)
01061           pFmt->wEffects &= ~PFE_TABLE;
01062         ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
01063         tableDef->numCellsInserted = 0;
01064       }
01065       break;
01066     }
01067     case rtfTab:
01068     case rtfPar:
01069       if (info->editor->bEmulateVersion10) { /* v1.0 - 3.0 */
01070         ME_DisplayItem *para;
01071         PARAFORMAT2 *pFmt;
01072         RTFFlushOutputBuffer(info);
01073         para = info->editor->pCursors[0].pPara;
01074         pFmt = para->member.para.pFmt;
01075         if (pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
01076         {
01077           /* rtfPar is treated like a space within a table. */
01078           info->rtfClass = rtfText;
01079           info->rtfMajor = ' ';
01080         }
01081         else if (info->rtfMinor == rtfPar && tableDef)
01082           tableDef->numCellsInserted = 0;
01083       }
01084       break;
01085   }
01086 }
01087 
01088 static BOOL ME_RTFInsertOleObject(RTF_Info *info, HENHMETAFILE hemf, HBITMAP hbmp,
01089                                   const SIZEL* sz)
01090 {
01091   LPOLEOBJECT         lpObject = NULL;
01092   LPSTORAGE           lpStorage = NULL;
01093   LPOLECLIENTSITE     lpClientSite = NULL;
01094   LPDATAOBJECT        lpDataObject = NULL;
01095   LPOLECACHE          lpOleCache = NULL;
01096   STGMEDIUM           stgm;
01097   FORMATETC           fm;
01098   CLSID               clsid;
01099   BOOL                ret = FALSE;
01100   DWORD               conn;
01101 
01102   if (hemf)
01103   {
01104       stgm.tymed = TYMED_ENHMF;
01105       stgm.u.hEnhMetaFile = hemf;
01106       fm.cfFormat = CF_ENHMETAFILE;
01107   }
01108   else if (hbmp)
01109   {
01110       stgm.tymed = TYMED_GDI;
01111       stgm.u.hBitmap = hbmp;
01112       fm.cfFormat = CF_BITMAP;
01113   }
01114   stgm.pUnkForRelease = NULL;
01115 
01116   fm.ptd = NULL;
01117   fm.dwAspect = DVASPECT_CONTENT;
01118   fm.lindex = -1;
01119   fm.tymed = stgm.tymed;
01120 
01121   if (!info->lpRichEditOle)
01122   {
01123     CreateIRichEditOle(info->editor, (VOID**)&info->lpRichEditOle);
01124   }
01125 
01126   if (OleCreateDefaultHandler(&CLSID_NULL, NULL, &IID_IOleObject, (void**)&lpObject) == S_OK &&
01127 #if 0
01128       /* FIXME: enable it when rich-edit properly implements this method */
01129       IRichEditOle_GetClientSite(info->lpRichEditOle, &lpClientSite) == S_OK &&
01130       IOleObject_SetClientSite(lpObject, lpClientSite) == S_OK &&
01131 #endif
01132       IOleObject_GetUserClassID(lpObject, &clsid) == S_OK &&
01133       IOleObject_QueryInterface(lpObject, &IID_IOleCache, (void**)&lpOleCache) == S_OK &&
01134       IOleCache_Cache(lpOleCache, &fm, 0, &conn) == S_OK &&
01135       IOleObject_QueryInterface(lpObject, &IID_IDataObject, (void**)&lpDataObject) == S_OK &&
01136       IDataObject_SetData(lpDataObject, &fm, &stgm, TRUE) == S_OK)
01137   {
01138     REOBJECT            reobject;
01139 
01140     reobject.cbStruct = sizeof(reobject);
01141     reobject.cp = REO_CP_SELECTION;
01142     reobject.clsid = clsid;
01143     reobject.poleobj = lpObject;
01144     reobject.pstg = lpStorage;
01145     reobject.polesite = lpClientSite;
01146     /* convert from twips to .01 mm */
01147     reobject.sizel.cx = MulDiv(sz->cx, 254, 144);
01148     reobject.sizel.cy = MulDiv(sz->cy, 254, 144);
01149     reobject.dvaspect = DVASPECT_CONTENT;
01150     reobject.dwFlags = 0; /* FIXME */
01151     reobject.dwUser = 0;
01152 
01153     ME_InsertOLEFromCursor(info->editor, &reobject, 0);
01154     ret = TRUE;
01155   }
01156 
01157   if (lpObject)       IOleObject_Release(lpObject);
01158   if (lpClientSite)   IOleClientSite_Release(lpClientSite);
01159   if (lpStorage)      IStorage_Release(lpStorage);
01160   if (lpDataObject)   IDataObject_Release(lpDataObject);
01161   if (lpOleCache)     IOleCache_Release(lpOleCache);
01162 
01163   return ret;
01164 }
01165 
01166 static void ME_RTFReadPictGroup(RTF_Info *info)
01167 {
01168   SIZEL         sz;
01169   BYTE*         buffer = NULL;
01170   unsigned      bufsz, bufidx;
01171   BOOL          flip;
01172   BYTE          val;
01173   METAFILEPICT  mfp;
01174   HENHMETAFILE  hemf;
01175   HBITMAP       hbmp;
01176   enum gfxkind {gfx_unknown = 0, gfx_enhmetafile, gfx_metafile, gfx_dib} gfx = gfx_unknown;
01177 
01178   RTFGetToken (info);
01179   if (info->rtfClass == rtfEOF)
01180     return;
01181   mfp.mm = MM_TEXT;
01182   /* fetch picture type */
01183   if (RTFCheckMM (info, rtfPictAttr, rtfWinMetafile))
01184   {
01185     mfp.mm = info->rtfParam;
01186     gfx = gfx_metafile;
01187   }
01188   else if (RTFCheckMM (info, rtfPictAttr, rtfDevIndBitmap))
01189   {
01190     if (info->rtfParam != 0) FIXME("dibitmap should be 0 (%d)\n", info->rtfParam);
01191     gfx = gfx_dib;
01192   }
01193   else if (RTFCheckMM (info, rtfPictAttr, rtfEmfBlip))
01194   {
01195     gfx = gfx_enhmetafile;
01196   }
01197   else
01198   {
01199     FIXME("%d %d\n", info->rtfMajor, info->rtfMinor);
01200     goto skip_group;
01201   }
01202   sz.cx = sz.cy = 0;
01203   /* fetch picture attributes */
01204   for (;;)
01205   {
01206     RTFGetToken (info);
01207     if (info->rtfClass == rtfEOF)
01208       return;
01209     if (info->rtfClass == rtfText)
01210       break;
01211     if (!RTFCheckCM (info, rtfControl, rtfPictAttr))
01212     {
01213       ERR("Expected picture attribute (%d %d)\n",
01214         info->rtfClass, info->rtfMajor);
01215       goto skip_group;
01216     }
01217     else if (RTFCheckMM (info, rtfPictAttr, rtfPicWid))
01218     {
01219       if (gfx == gfx_metafile) mfp.xExt = info->rtfParam;
01220     }
01221     else if (RTFCheckMM (info, rtfPictAttr, rtfPicHt))
01222     {
01223       if (gfx == gfx_metafile) mfp.yExt = info->rtfParam;
01224     }
01225     else if (RTFCheckMM (info, rtfPictAttr, rtfPicGoalWid))
01226       sz.cx = info->rtfParam;
01227     else if (RTFCheckMM (info, rtfPictAttr, rtfPicGoalHt))
01228       sz.cy = info->rtfParam;
01229     else
01230       FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
01231   }
01232   /* fetch picture data */
01233   bufsz = 1024;
01234   bufidx = 0;
01235   buffer = HeapAlloc(GetProcessHeap(), 0, bufsz);
01236   val = info->rtfMajor;
01237   for (flip = TRUE;; flip = !flip)
01238   {
01239     RTFGetToken (info);
01240     if (info->rtfClass == rtfEOF)
01241     {
01242       HeapFree(GetProcessHeap(), 0, buffer);
01243       return; /* Warn ?? */
01244     }
01245     if (RTFCheckCM(info, rtfGroup, rtfEndGroup))
01246       break;
01247     if (info->rtfClass != rtfText) goto skip_group;
01248     if (flip)
01249     {
01250       if (bufidx >= bufsz &&
01251           !(buffer = HeapReAlloc(GetProcessHeap(), 0, buffer, bufsz += 1024)))
01252         goto skip_group;
01253       buffer[bufidx++] = RTFCharToHex(val) * 16 + RTFCharToHex(info->rtfMajor);
01254     }
01255     else
01256       val = info->rtfMajor;
01257   }
01258   if (flip) FIXME("wrong hex string\n");
01259 
01260   switch (gfx)
01261   {
01262   case gfx_enhmetafile:
01263     if ((hemf = SetEnhMetaFileBits(bufidx, buffer)))
01264       ME_RTFInsertOleObject(info, hemf, NULL, &sz);
01265     break;
01266   case gfx_metafile:
01267     if ((hemf = SetWinMetaFileBits(bufidx, buffer, NULL, &mfp)))
01268         ME_RTFInsertOleObject(info, hemf, NULL, &sz);
01269     break;
01270   case gfx_dib:
01271     {
01272       BITMAPINFO* bi = (BITMAPINFO*)buffer;
01273       HDC         hdc = GetDC(0);
01274       unsigned    nc = bi->bmiHeader.biClrUsed;
01275 
01276       /* not quite right, especially for bitfields type of compression */
01277       if (!nc && bi->bmiHeader.biBitCount <= 8)
01278         nc = 1 << bi->bmiHeader.biBitCount;
01279       if ((hbmp = CreateDIBitmap(hdc, &bi->bmiHeader,
01280                                  CBM_INIT, (char*)(bi + 1) + nc * sizeof(RGBQUAD),
01281                                  bi, DIB_RGB_COLORS)))
01282           ME_RTFInsertOleObject(info, NULL, hbmp, &sz);
01283       ReleaseDC(0, hdc);
01284     }
01285     break;
01286   default:
01287     break;
01288   }
01289   HeapFree(GetProcessHeap(), 0, buffer);
01290   RTFRouteToken (info); /* feed "}" back to router */
01291   return;
01292 skip_group:
01293   HeapFree(GetProcessHeap(), 0, buffer);
01294   RTFSkipGroup(info);
01295   RTFRouteToken(info);  /* feed "}" back to router */
01296 }
01297 
01298 /* for now, lookup the \result part and use it, whatever the object */
01299 static void ME_RTFReadObjectGroup(RTF_Info *info)
01300 {
01301   for (;;)
01302   {
01303     RTFGetToken (info);
01304     if (info->rtfClass == rtfEOF)
01305       return;
01306     if (RTFCheckCM(info, rtfGroup, rtfEndGroup))
01307       break;
01308     if (RTFCheckCM(info, rtfGroup, rtfBeginGroup))
01309     {
01310       RTFGetToken (info);
01311       if (info->rtfClass == rtfEOF)
01312         return;
01313       if (RTFCheckCMM(info, rtfControl, rtfDestination, rtfObjResult))
01314       {
01315     int level = 1;
01316 
01317     while (RTFGetToken (info) != rtfEOF)
01318     {
01319           if (info->rtfClass == rtfGroup)
01320           {
01321             if (info->rtfMajor == rtfBeginGroup) level++;
01322             else if (info->rtfMajor == rtfEndGroup && --level < 0) break;
01323           }
01324           RTFRouteToken(info);
01325     }
01326       }
01327       else RTFSkipGroup(info);
01328       continue;
01329     }
01330     if (!RTFCheckCM (info, rtfControl, rtfObjAttr))
01331     {
01332       FIXME("Non supported attribute: %d %d %d\n", info->rtfClass, info->rtfMajor, info->rtfMinor);
01333       return;
01334     }
01335   }
01336   RTFRouteToken(info);  /* feed "}" back to router */
01337 }
01338 
01339 static void ME_RTFReadHook(RTF_Info *info)
01340 {
01341   switch(info->rtfClass)
01342   {
01343     case rtfGroup:
01344       switch(info->rtfMajor)
01345       {
01346         case rtfBeginGroup:
01347           if (info->stackTop < maxStack) {
01348             info->stack[info->stackTop].style = info->style;
01349             ME_AddRefStyle(info->style);
01350             info->stack[info->stackTop].codePage = info->codePage;
01351             info->stack[info->stackTop].unicodeLength = info->unicodeLength;
01352           }
01353           info->stackTop++;
01354           info->styleChanged = FALSE;
01355           break;
01356         case rtfEndGroup:
01357         {
01358           RTFFlushOutputBuffer(info);
01359           info->stackTop--;
01360           if (info->stackTop <= 0)
01361             info->rtfClass = rtfEOF;
01362           if (info->stackTop < 0)
01363             return;
01364 
01365           ME_ReleaseStyle(info->style);
01366           info->style = info->stack[info->stackTop].style;
01367           info->codePage = info->stack[info->stackTop].codePage;
01368           info->unicodeLength = info->stack[info->stackTop].unicodeLength;
01369           break;
01370         }
01371       }
01372       break;
01373   }
01374 }
01375 
01376 void
01377 ME_StreamInFill(ME_InStream *stream)
01378 {
01379   stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
01380                                                                 (BYTE *)stream->buffer,
01381                                                                 sizeof(stream->buffer),
01382                                                                 (LONG *)&stream->dwSize);
01383   stream->dwUsed = 0;
01384 }
01385 
01386 static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream, BOOL stripLastCR)
01387 {
01388   RTF_Info parser;
01389   ME_Style *style;
01390   int from, to, nUndoMode;
01391   int nEventMask = editor->nEventMask;
01392   ME_InStream inStream;
01393   BOOL invalidRTF = FALSE;
01394   ME_Cursor *selStart, *selEnd;
01395   LRESULT num_read = 0; /* bytes read for SF_TEXT, non-control chars inserted for SF_RTF */
01396 
01397   TRACE("stream==%p editor==%p format==0x%X\n", stream, editor, format);
01398   editor->nEventMask = 0;
01399 
01400   ME_GetSelectionOfs(editor, &from, &to);
01401   if (format & SFF_SELECTION && editor->mode & TM_RICHTEXT)
01402   {
01403     ME_GetSelection(editor, &selStart, &selEnd);
01404     style = ME_GetSelectionInsertStyle(editor);
01405 
01406     ME_InternalDeleteText(editor, selStart, to - from, FALSE);
01407 
01408     /* Don't insert text at the end of the table row */
01409     if (!editor->bEmulateVersion10) { /* v4.1 */
01410       ME_DisplayItem *para = editor->pCursors->pPara;
01411       if (para->member.para.nFlags & MEPF_ROWEND)
01412       {
01413         para = para->member.para.next_para;
01414         editor->pCursors[0].pPara = para;
01415         editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
01416         editor->pCursors[0].nOffset = 0;
01417       }
01418       if (para->member.para.nFlags & MEPF_ROWSTART)
01419       {
01420         para = para->member.para.next_para;
01421         editor->pCursors[0].pPara = para;
01422         editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
01423         editor->pCursors[0].nOffset = 0;
01424       }
01425       editor->pCursors[1] = editor->pCursors[0];
01426     } else { /* v1.0 - 3.0 */
01427       if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA &&
01428           ME_IsInTable(editor->pCursors[0].pRun))
01429         return 0;
01430     }
01431   } else {
01432     style = editor->pBuffer->pDefaultStyle;
01433     ME_AddRefStyle(style);
01434     ME_SetSelection(editor, 0, 0);
01435     ME_InternalDeleteText(editor, &editor->pCursors[1],
01436                           ME_GetTextLength(editor), FALSE);
01437     from = to = 0;
01438     ME_ClearTempStyle(editor);
01439     ME_SetDefaultParaFormat(editor->pCursors[0].pPara->member.para.pFmt);
01440   }
01441 
01442 
01443   /* Back up undo mode to a local variable */
01444   nUndoMode = editor->nUndoMode;
01445 
01446   /* Only create an undo if SFF_SELECTION is set */
01447   if (!(format & SFF_SELECTION))
01448     editor->nUndoMode = umIgnore;
01449 
01450   inStream.editstream = stream;
01451   inStream.editstream->dwError = 0;
01452   inStream.dwSize = 0;
01453   inStream.dwUsed = 0;
01454 
01455   if (format & SF_RTF)
01456   {
01457     /* Check if it's really RTF, and if it is not, use plain text */
01458     ME_StreamInFill(&inStream);
01459     if (!inStream.editstream->dwError)
01460     {
01461       if ((!editor->bEmulateVersion10 && strncmp(inStream.buffer, "{\\rtf", 5) && strncmp(inStream.buffer, "{\\urtf", 6))
01462     || (editor->bEmulateVersion10 && *inStream.buffer != '{'))
01463       {
01464         invalidRTF = TRUE;
01465         inStream.editstream->dwError = -16;
01466       }
01467     }
01468   }
01469 
01470   if (!invalidRTF && !inStream.editstream->dwError)
01471   {
01472     if (format & SF_RTF) {
01473       from = ME_GetCursorOfs(&editor->pCursors[0]);
01474 
01475       /* setup the RTF parser */
01476       memset(&parser, 0, sizeof parser);
01477       RTFSetEditStream(&parser, &inStream);
01478       parser.rtfFormat = format&(SF_TEXT|SF_RTF);
01479       parser.editor = editor;
01480       parser.style = style;
01481       WriterInit(&parser);
01482       RTFInit(&parser);
01483       RTFSetReadHook(&parser, ME_RTFReadHook);
01484       RTFSetDestinationCallback(&parser, rtfPict, ME_RTFReadPictGroup);
01485       RTFSetDestinationCallback(&parser, rtfObject, ME_RTFReadObjectGroup);
01486       if (!parser.editor->bEmulateVersion10) /* v4.1 */
01487       {
01488         RTFSetDestinationCallback(&parser, rtfNoNestTables, RTFSkipGroup);
01489         RTFSetDestinationCallback(&parser, rtfNestTableProps, RTFReadGroup);
01490       }
01491       BeginFile(&parser);
01492 
01493       /* do the parsing */
01494       RTFRead(&parser);
01495       RTFFlushOutputBuffer(&parser);
01496       if (!editor->bEmulateVersion10) { /* v4.1 */
01497         if (parser.tableDef && parser.tableDef->tableRowStart &&
01498             (parser.nestingLevel > 0 || parser.canInheritInTbl))
01499         {
01500           /* Delete any incomplete table row at the end of the rich text. */
01501           int nOfs, nChars;
01502           ME_DisplayItem *para;
01503 
01504           parser.rtfMinor = rtfRow;
01505           /* Complete the table row before deleting it.
01506            * By doing it this way we will have the current paragraph format set
01507            * properly to reflect that is not in the complete table, and undo items
01508            * will be added for this change to the current paragraph format. */
01509           if (parser.nestingLevel > 0)
01510           {
01511             while (parser.nestingLevel > 1)
01512               ME_RTFSpecialCharHook(&parser); /* Decrements nestingLevel */
01513             para = parser.tableDef->tableRowStart;
01514             ME_RTFSpecialCharHook(&parser);
01515           } else {
01516             para = parser.tableDef->tableRowStart;
01517             ME_RTFSpecialCharHook(&parser);
01518             assert(para->member.para.nFlags & MEPF_ROWEND);
01519             para = para->member.para.next_para;
01520           }
01521 
01522           editor->pCursors[1].pPara = para;
01523           editor->pCursors[1].pRun = ME_FindItemFwd(para, diRun);
01524           editor->pCursors[1].nOffset = 0;
01525           nOfs = ME_GetCursorOfs(&editor->pCursors[1]);
01526           nChars = ME_GetCursorOfs(&editor->pCursors[0]) - nOfs;
01527           ME_InternalDeleteText(editor, &editor->pCursors[1], nChars, TRUE);
01528           if (parser.tableDef)
01529             parser.tableDef->tableRowStart = NULL;
01530         }
01531       }
01532       ME_CheckTablesForCorruption(editor);
01533       RTFDestroy(&parser);
01534       if (parser.lpRichEditOle)
01535         IRichEditOle_Release(parser.lpRichEditOle);
01536 
01537       if (parser.stackTop > 0)
01538       {
01539         while (--parser.stackTop >= 0)
01540         {
01541           ME_ReleaseStyle(parser.style);
01542           parser.style = parser.stack[parser.stackTop].style;
01543         }
01544         if (!inStream.editstream->dwError)
01545           inStream.editstream->dwError = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
01546       }
01547 
01548       /* Remove last line break, as mandated by tests. This is not affected by
01549          CR/LF counters, since RTF streaming presents only \para tokens, which
01550          are converted according to the standard rules: \r for 2.0, \r\n for 1.0
01551        */
01552       if (stripLastCR) {
01553         int newto;
01554         ME_GetSelection(editor, &selStart, &selEnd);
01555         newto = ME_GetCursorOfs(selEnd);
01556         if (newto > to + (editor->bEmulateVersion10 ? 1 : 0)) {
01557           WCHAR lastchar[3] = {'\0', '\0'};
01558           int linebreakSize = editor->bEmulateVersion10 ? 2 : 1;
01559           ME_Cursor linebreakCursor = *selEnd;
01560 
01561           ME_MoveCursorChars(editor, &linebreakCursor, -linebreakSize);
01562           ME_GetTextW(editor, lastchar, 2, &linebreakCursor, linebreakSize, 0);
01563           if (lastchar[0] == '\r' && (lastchar[1] == '\n' || lastchar[1] == '\0')) {
01564             ME_InternalDeleteText(editor, &linebreakCursor, linebreakSize, FALSE);
01565           }
01566         }
01567       }
01568       to = ME_GetCursorOfs(&editor->pCursors[0]);
01569       num_read = to - from;
01570 
01571       style = parser.style;
01572     }
01573     else if (format & SF_TEXT)
01574       num_read = ME_StreamInText(editor, format, &inStream, style);
01575     else
01576       ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
01577     /* put the cursor at the top */
01578     if (!(format & SFF_SELECTION))
01579       ME_SetSelection(editor, 0, 0);
01580   }
01581 
01582   /* Restore saved undo mode */
01583   editor->nUndoMode = nUndoMode;
01584 
01585   /* even if we didn't add an undo, we need to commit anything on the stack */
01586   ME_CommitUndo(editor);
01587 
01588   /* If SFF_SELECTION isn't set, delete any undos from before we started too */
01589   if (!(format & SFF_SELECTION))
01590     ME_EmptyUndoStack(editor);
01591 
01592   ME_ReleaseStyle(style);
01593   editor->nEventMask = nEventMask;
01594   ME_UpdateRepaint(editor, FALSE);
01595   if (!(format & SFF_SELECTION)) {
01596     ME_ClearTempStyle(editor);
01597   }
01598   ITextHost_TxShowCaret(editor->texthost, FALSE);
01599   ME_MoveCaret(editor);
01600   ITextHost_TxShowCaret(editor->texthost, TRUE);
01601   ME_SendSelChange(editor);
01602   ME_SendRequestResize(editor, FALSE);
01603 
01604   return num_read;
01605 }
01606 
01607 
01608 typedef struct tagME_RTFStringStreamStruct
01609 {
01610   char *string;
01611   int pos;
01612   int length;
01613 } ME_RTFStringStreamStruct;
01614 
01615 static DWORD CALLBACK ME_ReadFromRTFString(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
01616 {
01617   ME_RTFStringStreamStruct *pStruct = (ME_RTFStringStreamStruct *)dwCookie;
01618   int count;
01619 
01620   count = min(cb, pStruct->length - pStruct->pos);
01621   memmove(lpBuff, pStruct->string + pStruct->pos, count);
01622   pStruct->pos += count;
01623   *pcb = count;
01624   return 0;
01625 }
01626 
01627 static void
01628 ME_StreamInRTFString(ME_TextEditor *editor, BOOL selection, char *string)
01629 {
01630   EDITSTREAM es;
01631   ME_RTFStringStreamStruct data;
01632 
01633   data.string = string;
01634   data.length = strlen(string);
01635   data.pos = 0;
01636   es.dwCookie = (DWORD_PTR)&data;
01637   es.pfnCallback = ME_ReadFromRTFString;
01638   ME_StreamIn(editor, SF_RTF | (selection ? SFF_SELECTION : 0), &es, FALSE);
01639 }
01640 
01641 
01642 static int
01643 ME_FindText(ME_TextEditor *editor, DWORD flags, const CHARRANGE *chrg, const WCHAR *text, CHARRANGE *chrgText)
01644 {
01645   const int nLen = lstrlenW(text);
01646   const int nTextLen = ME_GetTextLength(editor);
01647   int nMin, nMax;
01648   ME_Cursor cursor;
01649   WCHAR wLastChar = ' ';
01650 
01651   TRACE("flags==0x%08x, chrg->cpMin==%d, chrg->cpMax==%d text==%s\n",
01652         flags, chrg->cpMin, chrg->cpMax, debugstr_w(text));
01653 
01654   if (flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD))
01655     FIXME("Flags 0x%08x not implemented\n",
01656         flags & ~(FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD));
01657 
01658   nMin = chrg->cpMin;
01659   if (chrg->cpMax == -1)
01660     nMax = nTextLen;
01661   else
01662     nMax = chrg->cpMax > nTextLen ? nTextLen : chrg->cpMax;
01663 
01664   /* In 1.0 emulation, if cpMax reaches end of text, add the FR_DOWN flag */
01665   if (editor->bEmulateVersion10 && nMax == nTextLen)
01666   {
01667     flags |= FR_DOWN;
01668   }
01669 
01670   /* In 1.0 emulation, cpMin must always be no greater than cpMax */
01671   if (editor->bEmulateVersion10 && nMax < nMin)
01672   {
01673     if (chrgText)
01674     {
01675       chrgText->cpMin = -1;
01676       chrgText->cpMax = -1;
01677     }
01678     return -1;
01679   }
01680 
01681   /* when searching up, if cpMin < cpMax, then instead of searching
01682    * on [cpMin,cpMax], we search on [0,cpMin], otherwise, search on
01683    * [cpMax, cpMin]. The exception is when cpMax is -1, in which
01684    * case, it is always bigger than cpMin.
01685    */
01686   if (!editor->bEmulateVersion10 && !(flags & FR_DOWN))
01687   {
01688     int nSwap = nMax;
01689 
01690     nMax = nMin > nTextLen ? nTextLen : nMin;
01691     if (nMin < nSwap || chrg->cpMax == -1)
01692       nMin = 0;
01693     else
01694       nMin = nSwap;
01695   }
01696 
01697   if (!nLen || nMin < 0 || nMax < 0 || nMax < nMin)
01698   {
01699     if (chrgText)
01700       chrgText->cpMin = chrgText->cpMax = -1;
01701     return -1;
01702   }
01703 
01704   if (flags & FR_DOWN) /* Forward search */
01705   {
01706     /* If possible, find the character before where the search starts */
01707     if ((flags & FR_WHOLEWORD) && nMin)
01708     {
01709       ME_CursorFromCharOfs(editor, nMin - 1, &cursor);
01710       wLastChar = cursor.pRun->member.run.strText->szData[cursor.nOffset];
01711       ME_MoveCursorChars(editor, &cursor, 1);
01712     } else {
01713       ME_CursorFromCharOfs(editor, nMin, &cursor);
01714     }
01715 
01716     while (cursor.pRun && ME_GetCursorOfs(&cursor) + nLen <= nMax)
01717     {
01718       ME_DisplayItem *pCurItem = cursor.pRun;
01719       int nCurStart = cursor.nOffset;
01720       int nMatched = 0;
01721     
01722       while (pCurItem && ME_CharCompare(pCurItem->member.run.strText->szData[nCurStart + nMatched], text[nMatched], (flags & FR_MATCHCASE)))
01723       {
01724         if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
01725           break;
01726 
01727         nMatched++;
01728         if (nMatched == nLen)
01729         {
01730           ME_DisplayItem *pNextItem = pCurItem;
01731           int nNextStart = nCurStart;
01732           WCHAR wNextChar;
01733 
01734           /* Check to see if next character is a whitespace */
01735           if (flags & FR_WHOLEWORD)
01736           {
01737             if (nCurStart + nMatched == pCurItem->member.run.strText->nLen)
01738             {
01739               pNextItem = ME_FindItemFwd(pCurItem, diRun);
01740               nNextStart = -nMatched;
01741             }
01742 
01743             if (pNextItem)
01744               wNextChar = pNextItem->member.run.strText->szData[nNextStart + nMatched];
01745             else
01746               wNextChar = ' ';
01747 
01748             if (isalnumW(wNextChar))
01749               break;
01750           }
01751 
01752           cursor.nOffset += cursor.pPara->member.para.nCharOfs + cursor.pRun->member.run.nCharOfs;
01753           if (chrgText)
01754           {
01755             chrgText->cpMin = cursor.nOffset;
01756             chrgText->cpMax = cursor.nOffset + nLen;
01757           }
01758           TRACE("found at %d-%d\n", cursor.nOffset, cursor.nOffset + nLen);
01759           return cursor.nOffset;
01760         }
01761         if (nCurStart + nMatched == pCurItem->member.run.strText->nLen)
01762         {
01763           pCurItem = ME_FindItemFwd(pCurItem, diRun);
01764           nCurStart = -nMatched;
01765         }
01766       }
01767       if (pCurItem)
01768         wLastChar = pCurItem->member.run.strText->szData[nCurStart + nMatched];
01769       else
01770         wLastChar = ' ';
01771 
01772       cursor.nOffset++;
01773       if (cursor.nOffset == cursor.pRun->member.run.strText->nLen)
01774       {
01775         ME_NextRun(&cursor.pPara, &cursor.pRun);
01776         cursor.nOffset = 0;
01777       }
01778     }
01779   }
01780   else /* Backward search */
01781   {
01782     /* If possible, find the character after where the search ends */
01783     if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
01784     {
01785       ME_CursorFromCharOfs(editor, nMax + 1, &cursor);
01786       wLastChar = cursor.pRun->member.run.strText->szData[cursor.nOffset];
01787       ME_MoveCursorChars(editor, &cursor, -1);
01788     } else {
01789       ME_CursorFromCharOfs(editor, nMax, &cursor);
01790     }
01791 
01792     while (cursor.pRun && ME_GetCursorOfs(&cursor) - nLen >= nMin)
01793     {
01794       ME_DisplayItem *pCurItem = cursor.pRun;
01795       ME_DisplayItem *pCurPara = cursor.pPara;
01796       int nCurEnd = cursor.nOffset;
01797       int nMatched = 0;
01798 
01799       if (nCurEnd == 0)
01800       {
01801         ME_PrevRun(&pCurPara, &pCurItem);
01802         nCurEnd = pCurItem->member.run.strText->nLen + nMatched;
01803       }
01804 
01805       while (pCurItem && ME_CharCompare(pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1], text[nLen - nMatched - 1], (flags & FR_MATCHCASE)))
01806       {
01807         if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
01808           break;
01809 
01810         nMatched++;
01811         if (nMatched == nLen)
01812         {
01813           ME_DisplayItem *pPrevItem = pCurItem;
01814           int nPrevEnd = nCurEnd;
01815           WCHAR wPrevChar;
01816           int nStart;
01817 
01818           /* Check to see if previous character is a whitespace */
01819           if (flags & FR_WHOLEWORD)
01820           {
01821             if (nPrevEnd - nMatched == 0)
01822             {
01823               pPrevItem = ME_FindItemBack(pCurItem, diRun);
01824               if (pPrevItem)
01825                 nPrevEnd = pPrevItem->member.run.strText->nLen + nMatched;
01826             }
01827 
01828             if (pPrevItem)
01829               wPrevChar = pPrevItem->member.run.strText->szData[nPrevEnd - nMatched - 1];
01830             else
01831               wPrevChar = ' ';
01832 
01833             if (isalnumW(wPrevChar))
01834               break;
01835           }
01836 
01837           nStart = pCurPara->member.para.nCharOfs
01838                    + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
01839           if (chrgText)
01840           {
01841             chrgText->cpMin = nStart;
01842             chrgText->cpMax = nStart + nLen;
01843           }
01844           TRACE("found at %d-%d\n", nStart, nStart + nLen);
01845           return nStart;
01846         }
01847         if (nCurEnd - nMatched == 0)
01848         {
01849           ME_PrevRun(&pCurPara, &pCurItem);
01850           /* Don't care about pCurItem becoming NULL here; it's already taken
01851            * care of in the exterior loop condition */
01852           nCurEnd = pCurItem->member.run.strText->nLen + nMatched;
01853         }
01854       }
01855       if (pCurItem)
01856         wLastChar = pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1];
01857       else
01858         wLastChar = ' ';
01859 
01860       cursor.nOffset--;
01861       if (cursor.nOffset < 0)
01862       {
01863         ME_PrevRun(&cursor.pPara, &cursor.pRun);
01864         cursor.nOffset = cursor.pRun->member.run.strText->nLen;
01865       }
01866     }
01867   }
01868   TRACE("not found\n");
01869   if (chrgText)
01870     chrgText->cpMin = chrgText->cpMax = -1;
01871   return -1;
01872 }
01873 
01874 static int ME_GetTextEx(ME_TextEditor *editor, GETTEXTEX *ex, LPARAM pText)
01875 {
01876     int nChars;
01877     ME_Cursor start;
01878 
01879     if (!ex->cb || !pText) return 0;
01880 
01881     if (ex->flags & ~(GT_SELECTION | GT_USECRLF))
01882       FIXME("GETTEXTEX flags 0x%08x not supported\n", ex->flags & ~(GT_SELECTION | GT_USECRLF));
01883 
01884     if (ex->flags & GT_SELECTION)
01885     {
01886       int from, to;
01887       int nStartCur = ME_GetSelectionOfs(editor, &from, &to);
01888       start = editor->pCursors[nStartCur];
01889       nChars = to - from;
01890     }
01891     else
01892     {
01893       ME_SetCursorToStart(editor, &start);
01894       nChars = INT_MAX;
01895     }
01896     if (ex->codepage == 1200)
01897     {
01898       return ME_GetTextW(editor, (LPWSTR)pText, ex->cb / sizeof(WCHAR) - 1,
01899                          &start, nChars, ex->flags & GT_USECRLF);
01900     }
01901     else
01902     {
01903       /* potentially each char may be a CR, why calculate the exact value with O(N) when
01904         we can just take a bigger buffer? :)
01905         The above assumption still holds with CR/LF counters, since CR->CRLF expansion
01906         occurs only in richedit 2.0 mode, in which line breaks have only one CR
01907        */
01908       int crlfmul = (ex->flags & GT_USECRLF) ? 2 : 1;
01909       DWORD buflen;
01910       LPWSTR buffer;
01911       LRESULT rc;
01912 
01913       buflen = min(crlfmul * nChars, ex->cb - 1);
01914       buffer = heap_alloc((buflen + 1) * sizeof(WCHAR));
01915 
01916       nChars = ME_GetTextW(editor, buffer, buflen, &start, nChars, ex->flags & GT_USECRLF);
01917       rc = WideCharToMultiByte(ex->codepage, 0, buffer, nChars + 1,
01918                                (LPSTR)pText, ex->cb, ex->lpDefaultChar, ex->lpUsedDefChar);
01919       if (rc) rc--; /* do not count 0 terminator */
01920 
01921       heap_free(buffer);
01922       return rc;
01923     }
01924 }
01925 
01926 static int ME_GetTextRange(ME_TextEditor *editor, WCHAR *strText,
01927                            const ME_Cursor *start, int nLen, BOOL unicode)
01928 {
01929     if (!strText) return 0;
01930     if (unicode) {
01931       return ME_GetTextW(editor, strText, INT_MAX, start, nLen, 0);
01932     } else {
01933       int nChars;
01934       WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1);
01935       if (!p) return 0;
01936       nChars = ME_GetTextW(editor, p, nLen, start, nLen, 0);
01937       WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)strText,
01938                           nLen+1, NULL, NULL);
01939       FREE_OBJ(p);
01940       return nChars;
01941     }
01942 }
01943 
01944 typedef struct tagME_GlobalDestStruct
01945 {
01946   HGLOBAL hData;
01947   int nLength;
01948 } ME_GlobalDestStruct;
01949 
01950 static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
01951 {
01952   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
01953   int i;
01954   WORD *pSrc, *pDest;
01955 
01956   cb = cb >> 1;
01957   pDest = (WORD *)lpBuff;
01958   pSrc = GlobalLock(pData->hData);
01959   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
01960     pDest[i] = pSrc[pData->nLength+i];
01961   }
01962   pData->nLength += i;
01963   *pcb = 2*i;
01964   GlobalUnlock(pData->hData);
01965   return 0;
01966 }
01967 
01968 static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
01969 {
01970   ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
01971   int i;
01972   BYTE *pSrc, *pDest;
01973 
01974   pDest = lpBuff;
01975   pSrc = GlobalLock(pData->hData);
01976   for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
01977     pDest[i] = pSrc[pData->nLength+i];
01978   }
01979   pData->nLength += i;
01980   *pcb = i;
01981   GlobalUnlock(pData->hData);
01982   return 0;
01983 }
01984 
01985 static BOOL ME_Paste(ME_TextEditor *editor)
01986 {
01987   DWORD dwFormat = 0;
01988   EDITSTREAM es;
01989   ME_GlobalDestStruct gds;
01990   UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
01991   UINT cf = 0;
01992 
01993   if (IsClipboardFormatAvailable(nRTFFormat))
01994     cf = nRTFFormat, dwFormat = SF_RTF;
01995   else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
01996     cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
01997   else
01998     return FALSE;
01999 
02000   if (!OpenClipboard(editor->hWnd))
02001     return FALSE;
02002   gds.hData = GetClipboardData(cf);
02003   gds.nLength = 0;
02004   es.dwCookie = (DWORD_PTR)&gds;
02005   es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
02006   ME_StreamIn(editor, dwFormat|SFF_SELECTION, &es, FALSE);
02007 
02008   CloseClipboard();
02009   return TRUE;
02010 }
02011 
02012 static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
02013 {
02014   LPDATAOBJECT dataObj = NULL;
02015   HRESULT hr = S_OK;
02016 
02017   if (editor->cPasswordMask)
02018     return FALSE; /* Copying or Cutting masked text isn't allowed */
02019 
02020   if(editor->lpOleCallback)
02021   {
02022     CHARRANGE range;
02023     range.cpMin = ME_GetCursorOfs(start);
02024     range.cpMax = range.cpMin + nChars;
02025     hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj);
02026   }
02027   if(FAILED(hr) || !dataObj)
02028     hr = ME_GetDataObject(editor, start, nChars, &dataObj);
02029   if(SUCCEEDED(hr)) {
02030     hr = OleSetClipboard(dataObj);
02031     IDataObject_Release(dataObj);
02032   }
02033   return SUCCEEDED(hr) != 0;
02034 }
02035 
02036 /* helper to send a msg filter notification */
02037 static BOOL
02038 ME_FilterEvent(ME_TextEditor *editor, UINT msg, WPARAM* wParam, LPARAM* lParam)
02039 {
02040     MSGFILTER msgf;
02041 
02042     if (!editor->hWnd || !editor->hwndParent) return FALSE;
02043     msgf.nmhdr.hwndFrom = editor->hWnd;
02044     msgf.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
02045     msgf.nmhdr.code = EN_MSGFILTER;
02046     msgf.msg = msg;
02047     msgf.wParam = *wParam;
02048     msgf.lParam = *lParam;
02049     if (SendMessageW(editor->hwndParent, WM_NOTIFY, msgf.nmhdr.idFrom, (LPARAM)&msgf))
02050         return FALSE;
02051     *wParam = msgf.wParam;
02052     *lParam = msgf.lParam;
02053     msgf.wParam = *wParam;
02054 
02055     return TRUE;
02056 }
02057 
02058 static void ME_UpdateSelectionLinkAttribute(ME_TextEditor *editor)
02059 {
02060   ME_DisplayItem *startPara, *endPara;
02061   ME_DisplayItem *prev_para;
02062   ME_Cursor *from, *to;
02063   ME_Cursor start;
02064   int nChars;
02065 
02066   if (!editor->AutoURLDetect_bEnable) return;
02067 
02068   ME_GetSelection(editor, &from, &to);
02069 
02070   /* Find paragraph previous to the one that contains start cursor */
02071   startPara = from->pPara;
02072   prev_para = startPara->member.para.prev_para;
02073   if (prev_para->type == diParagraph) startPara = prev_para;
02074 
02075   /* Find paragraph that contains end cursor */
02076   endPara = to->pPara->member.para.next_para;
02077 
02078   start.pPara = startPara;
02079   start.pRun = ME_FindItemFwd(startPara, diRun);
02080   start.nOffset = 0;
02081   nChars = endPara->member.para.nCharOfs - startPara->member.para.nCharOfs;
02082 
02083   ME_UpdateLinkAttribute(editor, &start, nChars);
02084 }
02085 
02086 static BOOL
02087 ME_KeyDown(ME_TextEditor *editor, WORD nKey)
02088 {
02089   BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
02090   BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
02091 
02092   if (editor->bMouseCaptured)
02093       return FALSE;
02094   if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey != VK_MENU)
02095       editor->nSelectionType = stPosition;
02096 
02097   switch (nKey)
02098   {
02099     case VK_LEFT:
02100     case VK_RIGHT:
02101     case VK_HOME:
02102     case VK_END:
02103         editor->nUDArrowX = -1;
02104         /* fall through */
02105     case VK_UP:
02106     case VK_DOWN:
02107     case VK_PRIOR:
02108     case VK_NEXT:
02109       ME_CommitUndo(editor); /* End coalesced undos for typed characters */
02110       ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
02111       return TRUE;
02112     case VK_BACK:
02113     case VK_DELETE:
02114       editor->nUDArrowX = -1;
02115       /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
02116       if (editor->styleFlags & ES_READONLY)
02117         return FALSE;
02118       if (ME_IsSelection(editor))
02119       {
02120         ME_DeleteSelection(editor);
02121         ME_CommitUndo(editor);
02122       }
02123       else if (nKey == VK_DELETE)
02124       {
02125         /* Delete stops group typing.
02126          * (See MSDN remarks on EM_STOPGROUPTYPING message) */
02127         ME_DeleteTextAtCursor(editor, 1, 1);
02128         ME_CommitUndo(editor);
02129       }
02130       else if (ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
02131       {
02132         BOOL bDeletionSucceeded;
02133         /* Backspace can be grouped for a single undo */
02134         ME_ContinueCoalescingTransaction(editor);
02135         bDeletionSucceeded = ME_DeleteTextAtCursor(editor, 1, 1);
02136         if (!bDeletionSucceeded && !editor->bEmulateVersion10) { /* v4.1 */
02137           /* Deletion was prevented so the cursor is moved back to where it was.
02138            * (e.g. this happens when trying to delete cell boundaries)
02139            */
02140           ME_ArrowKey(editor, VK_RIGHT, FALSE, FALSE);
02141         }
02142         ME_CommitCoalescingUndo(editor);
02143       }
02144       else
02145         return TRUE;
02146       ME_MoveCursorFromTableRowStartParagraph(editor);
02147       ME_UpdateSelectionLinkAttribute(editor);
02148       ME_UpdateRepaint(editor, FALSE);
02149       ME_SendRequestResize(editor, FALSE);
02150       return TRUE;
02151     case VK_RETURN:
02152       if (editor->bDialogMode)
02153       {
02154         if (ctrl_is_down)
02155           return TRUE;
02156 
02157         if (!(editor->styleFlags & ES_WANTRETURN))
02158         {
02159           if (editor->hwndParent)
02160           {
02161             DWORD dw;
02162             dw = SendMessageW(editor->hwndParent, DM_GETDEFID, 0, 0);
02163             if (HIWORD(dw) == DC_HASDEFID)
02164             {
02165                 HWND hwDefCtrl = GetDlgItem(editor->hwndParent, LOWORD(dw));
02166                 if (hwDefCtrl)
02167                 {
02168                     SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
02169                     PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
02170                 }
02171             }
02172           }
02173           return TRUE;
02174         }
02175       }
02176 
02177       if (editor->styleFlags & ES_MULTILINE)
02178       {
02179         ME_Cursor cursor = editor->pCursors[0];
02180         ME_DisplayItem *para = cursor.pPara;
02181         int from, to;
02182         const WCHAR endl = '\r';
02183         const WCHAR endlv10[] = {'\r','\n'};
02184         ME_Style *style;
02185 
02186         if (editor->styleFlags & ES_READONLY) {
02187           MessageBeep(MB_ICONERROR);
02188           return TRUE;
02189         }
02190 
02191         ME_GetSelectionOfs(editor, &from, &to);
02192         if (editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
02193         {
02194           if (!editor->bEmulateVersion10) { /* v4.1 */
02195             if (para->member.para.nFlags & MEPF_ROWEND) {
02196               /* Add a new table row after this row. */
02197               para = ME_AppendTableRow(editor, para);
02198               para = para->member.para.next_para;
02199               editor->pCursors[0].pPara = para;
02200               editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
02201               editor->pCursors[0].nOffset = 0;
02202               editor->pCursors[1] = editor->pCursors[0];
02203               ME_CommitUndo(editor);
02204               ME_CheckTablesForCorruption(editor);
02205               ME_UpdateRepaint(editor, FALSE);
02206               return TRUE;
02207             }
02208             else if (para == editor->pCursors[1].pPara &&
02209                      cursor.nOffset + cursor.pRun->member.run.nCharOfs == 0 &&
02210                      para->member.para.prev_para->member.para.nFlags & MEPF_ROWSTART &&
02211                      !para->member.para.prev_para->member.para.nCharOfs)
02212             {
02213               /* Insert a newline before the table. */
02214               para = para->member.para.prev_para;
02215               para->member.para.nFlags &= ~MEPF_ROWSTART;
02216               editor->pCursors[0].pPara = para;
02217               editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
02218               editor->pCursors[1] = editor->pCursors[0];
02219               ME_InsertTextFromCursor(editor, 0, &endl, 1,
02220                                       editor->pCursors[0].pRun->member.run.style);
02221               para = editor->pBuffer->pFirst->member.para.next_para;
02222               ME_SetDefaultParaFormat(para->member.para.pFmt);
02223               para->member.para.nFlags = MEPF_REWRAP;
02224               editor->pCursors[0].pPara = para;
02225               editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
02226               editor->pCursors[1] = editor->pCursors[0];
02227               para->member.para.next_para->member.para.nFlags |= MEPF_ROWSTART;
02228               ME_CommitCoalescingUndo(editor);
02229               ME_CheckTablesForCorruption(editor);
02230               ME_UpdateRepaint(editor, FALSE);
02231               return TRUE;
02232             }
02233           } else { /* v1.0 - 3.0 */
02234             ME_DisplayItem *para = cursor.pPara;
02235             if (ME_IsInTable(para))
02236             {
02237               if (cursor.pRun->member.run.nFlags & MERF_ENDPARA)
02238               {
02239                 if (from == to) {
02240                   ME_ContinueCoalescingTransaction(editor);
02241                   para = ME_AppendTableRow(editor, para);
02242                   editor->pCursors[0].pPara = para;
02243                   editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
02244                   editor->pCursors[0].nOffset = 0;
02245                   editor->pCursors[1] = editor->pCursors[0];
02246                   ME_CommitCoalescingUndo(editor);
02247                   ME_UpdateRepaint(editor, FALSE);
02248                   return TRUE;
02249                 }
02250               } else {
02251                 ME_ContinueCoalescingTransaction(editor);
02252                 if (cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
02253                     !ME_IsInTable(para->member.para.prev_para))
02254                 {
02255                   /* Insert newline before table */
02256                   cursor.pRun = ME_FindItemBack(para, diRun);
02257                   if (cursor.pRun) {
02258                     editor->pCursors[0].pRun = cursor.pRun;
02259                     editor->pCursors[0].pPara = para->member.para.prev_para;
02260                   }
02261                   editor->pCursors[0].nOffset = 0;
02262                   editor->pCursors[1] = editor->pCursors[0];
02263                   ME_InsertTextFromCursor(editor, 0, &endl, 1,
02264                                           editor->pCursors[0].pRun->member.run.style);
02265                 } else {
02266                   editor->pCursors[1] = editor->pCursors[0];
02267                   para = ME_AppendTableRow(editor, para);
02268                   editor->pCursors[0].pPara = para;
02269                   editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
02270                   editor->pCursors[0].nOffset = 0;
02271                   editor->pCursors[1] = editor->pCursors[0];
02272                 }
02273                 ME_CommitCoalescingUndo(editor);
02274                 ME_UpdateRepaint(editor, FALSE);
02275                 return TRUE;
02276               }
02277             }
02278           }
02279 
02280           style = ME_GetInsertStyle(editor, 0);
02281           ME_SaveTempStyle(editor);
02282           ME_ContinueCoalescingTransaction(editor);
02283           if (shift_is_down)
02284             ME_InsertEndRowFromCursor(editor, 0);
02285           else
02286             if (!editor->bEmulateVersion10)
02287               ME_InsertTextFromCursor(editor, 0, &endl, 1, style);
02288             else
02289               ME_InsertTextFromCursor(editor, 0, endlv10, 2, style);
02290           ME_ReleaseStyle(style);
02291           ME_CommitCoalescingUndo(editor);
02292           SetCursor(NULL);
02293 
02294           ME_UpdateSelectionLinkAttribute(editor);
02295           ME_UpdateRepaint(editor, FALSE);
02296         }
02297         return TRUE;
02298       }
02299       break;
02300     case VK_ESCAPE:
02301       if (editor->bDialogMode && editor->hwndParent)
02302         PostMessageW(editor->hwndParent, WM_CLOSE, 0, 0);
02303       return TRUE;
02304     case VK_TAB:
02305       if (editor->bDialogMode && editor->hwndParent)
02306         SendMessageW(editor->hwndParent, WM_NEXTDLGCTL, shift_is_down, 0);
02307       return TRUE;
02308     case 'A':
02309       if (ctrl_is_down)
02310       {
02311         ME_SetSelection(editor, 0, -1);
02312         return TRUE;
02313       }
02314       break;
02315     case 'V':
02316       if (ctrl_is_down)
02317         return ME_Paste(editor);
02318       break;
02319     case 'C':
02320     case 'X':
02321       if (ctrl_is_down)
02322       {
02323         BOOL result;
02324         int nOfs, nChars;
02325         int nStartCur = ME_GetSelectionOfs(editor, &nOfs, &nChars);
02326         ME_Cursor *selStart = &editor->pCursors[nStartCur];
02327 
02328         nChars -= nOfs;
02329         result = ME_Copy(editor, selStart, nChars);
02330         if (result && nKey == 'X')
02331         {
02332           ME_InternalDeleteText(editor, selStart, nChars, FALSE);
02333           ME_CommitUndo(editor);
02334           ME_UpdateRepaint(editor, TRUE);
02335         }
02336         return result;
02337       }
02338       break;
02339     case 'Z':
02340       if (ctrl_is_down)
02341       {
02342         ME_Undo(editor);
02343         return TRUE;
02344       }
02345       break;
02346     case 'Y':
02347       if (ctrl_is_down)
02348       {
02349         ME_Redo(editor);
02350         return TRUE;
02351       }
02352       break;
02353 
02354     default:
02355       if (nKey != VK_SHIFT && nKey != VK_CONTROL && nKey && nKey != VK_MENU)
02356           editor->nUDArrowX = -1;
02357       if (ctrl_is_down)
02358       {
02359         if (nKey == 'W')
02360         {
02361           CHARFORMAT2W chf;
02362           char buf[2048];
02363           chf.cbSize = sizeof(chf);
02364           
02365           ME_GetSelectionCharFormat(editor, &chf);
02366           ME_DumpStyleToBuf(&chf, buf);
02367           MessageBoxA(NULL, buf, "Style dump", MB_OK);
02368         }
02369         if (nKey == 'Q')
02370         {
02371           ME_CheckCharOffsets(editor);
02372         }
02373       }
02374   }
02375   return FALSE;
02376 }
02377 
02378 static LRESULT ME_Char(ME_TextEditor *editor, WPARAM charCode,
02379                        LPARAM flags, BOOL unicode)
02380 {
02381   WCHAR wstr;
02382 
02383   if (editor->bMouseCaptured)
02384     return 0;
02385 
02386   if (unicode)
02387       wstr = (WCHAR)charCode;
02388   else
02389   {
02390       CHAR charA = charCode;
02391       MultiByteToWideChar(CP_ACP, 0, &charA, 1, &wstr, 1);
02392   }
02393 
02394   if (editor->styleFlags & ES_READONLY) {
02395     MessageBeep(MB_ICONERROR);
02396     return 0; /* FIXME really 0 ? */
02397   }
02398 
02399   if ((unsigned)wstr >= ' ' || wstr == '\t')
02400   {
02401     ME_Cursor cursor = editor->pCursors[0];
02402     ME_DisplayItem *para = cursor.pPara;
02403     int from, to;
02404     BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
02405     ME_GetSelectionOfs(editor, &from, &to);
02406     if (wstr == '\t' &&
02407         /* v4.1 allows tabs to be inserted with ctrl key down */
02408         !(ctrl_is_down && !editor->bEmulateVersion10))
02409     {
02410       ME_DisplayItem *para;
02411       BOOL bSelectedRow = FALSE;
02412 
02413       para = cursor.pPara;
02414       if (ME_IsSelection(editor) &&
02415           cursor.pRun->member.run.nCharOfs + cursor.nOffset == 0 &&
02416           to == ME_GetCursorOfs(&editor->pCursors[0]) &&
02417           para->member.para.prev_para->type == diParagraph)
02418       {
02419         para = para->member.para.prev_para;
02420         bSelectedRow = TRUE;
02421       }
02422       if (ME_IsInTable(para))
02423       {
02424         ME_TabPressedInTable(editor, bSelectedRow);
02425         ME_CommitUndo(editor);
02426         return 0;
02427       }
02428     } else if (!editor->bEmulateVersion10) { /* v4.1 */
02429       if (para->member.para.nFlags & MEPF_ROWEND) {
02430         if (from == to) {
02431           para = para->member.para.next_para;
02432           if (para->member.para.nFlags & MEPF_ROWSTART)
02433             para = para->member.para.next_para;
02434           editor->pCursors[0].pPara = para;
02435           editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
02436           editor->pCursors[0].nOffset = 0;
02437           editor->pCursors[1] = editor->pCursors[0];
02438         }
02439       }
02440     } else { /* v1.0 - 3.0 */
02441       if (ME_IsInTable(cursor.pRun) &&
02442           cursor.pRun->member.run.nFlags & MERF_ENDPARA &&
02443           from == to)
02444       {
02445         /* Text should not be inserted at the end of the table. */
02446         MessageBeep(-1);
02447         return 0;
02448       }
02449     }
02450     /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
02451     /* WM_CHAR is restricted to nTextLimit */
02452     if(editor->nTextLimit > ME_GetTextLength(editor) - (to-from))
02453     {
02454       ME_Style *style = ME_GetInsertStyle(editor, 0);
02455       ME_SaveTempStyle(editor);
02456       ME_ContinueCoalescingTransaction(editor);
02457       ME_InsertTextFromCursor(editor, 0, &wstr, 1, style);
02458       ME_ReleaseStyle(style);
02459       ME_CommitCoalescingUndo(editor);
02460       ITextHost_TxSetCursor(editor->texthost, NULL, FALSE);
02461     }
02462 
02463     ME_UpdateSelectionLinkAttribute(editor);
02464     ME_UpdateRepaint(editor, FALSE);
02465   }
02466   return 0;
02467 }
02468 
02469 /* Process the message and calculate the new click count.
02470  *
02471  * returns: The click count if it is mouse down event, else returns 0. */
02472 static int ME_CalculateClickCount(ME_TextEditor *editor, UINT msg, WPARAM wParam,
02473                                   LPARAM lParam)
02474 {
02475     static int clickNum = 0;
02476     if (msg < WM_MOUSEFIRST || msg > WM_MOUSELAST)
02477         return 0;
02478 
02479     if ((msg == WM_LBUTTONDBLCLK) ||
02480         (msg == WM_RBUTTONDBLCLK) ||
02481         (msg == WM_MBUTTONDBLCLK) ||
02482         (msg == WM_XBUTTONDBLCLK))
02483     {
02484         msg -= (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
02485     }
02486 
02487     if ((msg == WM_LBUTTONDOWN) ||
02488         (msg == WM_RBUTTONDOWN) ||
02489         (msg == WM_MBUTTONDOWN) ||
02490         (msg == WM_XBUTTONDOWN))
02491     {
02492         static MSG prevClickMsg;
02493         MSG clickMsg;
02494         /* Compare the editor instead of the hwnd so that the this
02495          * can still be done for windowless richedit controls. */
02496         clickMsg.hwnd = (HWND)editor;
02497         clickMsg.message = msg;
02498         clickMsg.wParam = wParam;
02499         clickMsg.lParam = lParam;
02500         clickMsg.time = GetMessageTime();
02501         clickMsg.pt.x = (short)LOWORD(lParam);
02502         clickMsg.pt.y = (short)HIWORD(lParam);
02503         if ((clickNum != 0) &&
02504             (clickMsg.message == prevClickMsg.message) &&
02505             (clickMsg.hwnd == prevClickMsg.hwnd) &&
02506             (clickMsg.wParam == prevClickMsg.wParam) &&
02507             (clickMsg.time - prevClickMsg.time < GetDoubleClickTime()) &&
02508             (abs(clickMsg.pt.x - prevClickMsg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
02509             (abs(clickMsg.pt.y - prevClickMsg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
02510         {
02511             clickNum++;
02512         } else {
02513             clickNum = 1;
02514         }
02515         prevClickMsg = clickMsg;
02516     } else {
02517         return 0;
02518     }
02519     return clickNum;
02520 }
02521 
02522 static BOOL ME_SetCursor(ME_TextEditor *editor)
02523 {
02524   ME_Cursor cursor;
02525   POINT pt;
02526   BOOL isExact;
02527   SCROLLBARINFO sbi;
02528   DWORD messagePos = GetMessagePos();
02529   pt.x = (short)LOWORD(messagePos);
02530   pt.y = (short)HIWORD(messagePos);
02531 
02532   if (editor->hWnd)
02533   {
02534     sbi.cbSize = sizeof(sbi);
02535     GetScrollBarInfo(editor->hWnd, OBJID_HSCROLL, &sbi);
02536     if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
02537         PtInRect(&sbi.rcScrollBar, pt))
02538     {
02539         ITextHost_TxSetCursor(editor->texthost,
02540                               LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
02541         return TRUE;
02542     }
02543     sbi.cbSize = sizeof(sbi);
02544     GetScrollBarInfo(editor->hWnd, OBJID_VSCROLL, &sbi);
02545     if (!(sbi.rgstate[0] & (STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_OFFSCREEN)) &&
02546         PtInRect(&sbi.rcScrollBar, pt))
02547     {
02548         ITextHost_TxSetCursor(editor->texthost,
02549                               LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
02550         return TRUE;
02551     }
02552   }
02553   ITextHost_TxScreenToClient(editor->texthost, &pt);
02554 
02555   if (editor->nSelectionType == stLine && editor->bMouseCaptured) {
02556       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
02557       return TRUE;
02558   }
02559   if (!editor->bEmulateVersion10 /* v4.1 */ &&
02560       pt.y < editor->rcFormat.top &&
02561       pt.x < editor->rcFormat.left)
02562   {
02563       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
02564       return TRUE;
02565   }
02566   if (pt.y < editor->rcFormat.top || pt.y > editor->rcFormat.bottom)
02567   {
02568       if (editor->bEmulateVersion10) /* v1.0 - 3.0 */
02569           ITextHost_TxSetCursor(editor->texthost,
02570                                 LoadCursorW(NULL, (WCHAR*)IDC_ARROW), FALSE);
02571       else /* v4.1 */
02572           ITextHost_TxSetCursor(editor->texthost,
02573                                 LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
02574       return TRUE;
02575   }
02576   if (pt.x < editor->rcFormat.left)
02577   {
02578       ITextHost_TxSetCursor(editor->texthost, hLeft, FALSE);
02579       return TRUE;
02580   }
02581   ME_CharFromPos(editor, pt.x, pt.y, &cursor, &isExact);
02582   if (isExact)
02583   {
02584       ME_Run *run;
02585 
02586       run = &cursor.pRun->member.run;
02587       if (run->style->fmt.dwMask & CFM_LINK &&
02588           run->style->fmt.dwEffects & CFE_LINK)
02589       {
02590           ITextHost_TxSetCursor(editor->texthost,
02591                                 LoadCursorW(NULL, (WCHAR*)IDC_HAND),
02592                                 FALSE);
02593           return TRUE;
02594       }
02595 
02596       if (ME_IsSelection(editor))
02597       {
02598           int selStart, selEnd;
02599           int offset = ME_GetCursorOfs(&cursor);
02600 
02601           ME_GetSelectionOfs(editor, &selStart, &selEnd);
02602           if (selStart <= offset && selEnd >= offset) {
02603               ITextHost_TxSetCursor(editor->texthost,
02604                                     LoadCursorW(NULL, (WCHAR*)IDC_ARROW),
02605                                     FALSE);
02606               return TRUE;
02607           }
02608       }
02609   }
02610   ITextHost_TxSetCursor(editor->texthost,
02611                         LoadCursorW(NULL, (WCHAR*)IDC_IBEAM), TRUE);
02612   return TRUE;
02613 }
02614 
02615 static void ME_SetDefaultFormatRect(ME_TextEditor *editor)
02616 {
02617   ITextHost_TxGetClientRect(editor->texthost, &editor->rcFormat);
02618   editor->rcFormat.top += editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
02619   editor->rcFormat.left += 1 + editor->selofs;
02620   editor->rcFormat.right -= 1;
02621 }
02622 
02623 static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
02624 {
02625   CHARRANGE selrange;
02626   HMENU menu;
02627   int seltype = 0;
02628   if(!editor->lpOleCallback || !editor->hWnd)
02629     return FALSE;
02630   ME_GetSelectionOfs(editor, &selrange.cpMin, &selrange.cpMax);
02631   if(selrange.cpMin == selrange.cpMax)
02632     seltype |= SEL_EMPTY;
02633   else
02634   {
02635     /* FIXME: Handle objects */
02636     seltype |= SEL_TEXT;
02637     if(selrange.cpMax-selrange.cpMin > 1)
02638       seltype |= SEL_MULTICHAR;
02639   }
02640   if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
02641   {
02642     TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, editor->hwndParent, NULL);
02643     DestroyMenu(menu);
02644   }
02645   return TRUE;
02646 }
02647 
02648 ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
02649 {
02650   ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
02651   int i;
02652   DWORD props;
02653   LONG selbarwidth;
02654 
02655   ed->hWnd = NULL;
02656   ed->hwndParent = NULL;
02657   ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
02658   ed->texthost = texthost;
02659   ed->bEmulateVersion10 = bEmulateVersion10;
02660   ed->styleFlags = 0;
02661   ITextHost_TxGetPropertyBits(texthost,
02662                               (TXTBIT_RICHTEXT|TXTBIT_MULTILINE|
02663                                TXTBIT_READONLY|TXTBIT_USEPASSWORD|
02664                                TXTBIT_HIDESELECTION|TXTBIT_SAVESELECTION|
02665                                TXTBIT_AUTOWORDSEL|TXTBIT_VERTICAL|
02666                                TXTBIT_WORDWRAP|TXTBIT_DISABLEDRAG),
02667                               &props);
02668   ITextHost_TxGetScrollBars(texthost, &ed->styleFlags);
02669   ed->styleFlags &= (WS_VSCROLL|WS_HSCROLL|ES_AUTOVSCROLL|
02670                      ES_AUTOHSCROLL|ES_DISABLENOSCROLL);
02671   ed->pBuffer = ME_MakeText();
02672   ed->nZoomNumerator = ed->nZoomDenominator = 0;
02673   ed->nAvailWidth = 0; /* wrap to client area */
02674   ME_MakeFirstParagraph(ed);
02675   /* The four cursors are for:
02676    * 0 - The position where the caret is shown
02677    * 1 - The anchored end of the selection (for normal selection)
02678    * 2 & 3 - The anchored start and end respectively for word, line,
02679    * or paragraph selection.
02680    */
02681   ed->nCursors = 4;
02682   ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
02683   ME_SetCursorToStart(ed, &ed->pCursors[0]);
02684   ed->pCursors[1] = ed->pCursors[0];
02685   ed->pCursors[2] = ed->pCursors[0];
02686   ed->pCursors[3] = ed->pCursors[1];
02687   ed->nLastTotalLength = ed->nTotalLength = 0;
02688   ed->nLastTotalWidth = ed->nTotalWidth = 0;
02689   ed->nUDArrowX = -1;
02690   ed->nSequence = 0;
02691   ed->rgbBackColor = -1;
02692   ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
02693   ed->bCaretAtEnd = FALSE;
02694   ed->nEventMask = 0;
02695   ed->nModifyStep = 0;
02696   ed->nTextLimit = TEXT_LIMIT_DEFAULT;
02697   ed->pUndoStack = ed->pRedoStack = ed->pUndoStackBottom = NULL;
02698   ed->nUndoStackSize = 0;
02699   ed->nUndoLimit = STACK_SIZE_DEFAULT;
02700   ed->nUndoMode = umAddToUndo;
02701   ed->nParagraphs = 1;
02702   ed->nLastSelStart = ed->nLastSelEnd = 0;
02703   ed->pLastSelStartPara = ed->pLastSelEndPara = ed->pCursors[0].pPara;
02704   ed->bHideSelection = FALSE;
02705   ed->pfnWordBreak = NULL;
02706   ed->lpOleCallback = NULL;
02707   ed->mode = TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
02708   ed->mode |= (props & TXTBIT_RICHTEXT) ? TM_RICHTEXT : TM_PLAINTEXT;
02709   ed->AutoURLDetect_bEnable = FALSE;
02710   ed->bHaveFocus = FALSE;
02711   ed->bDialogMode = FALSE;
02712   ed->bMouseCaptured = FALSE;
02713   for (i=0; i<HFONT_CACHE_SIZE; i++)
02714   {
02715     ed->pFontCache[i].nRefs = 0;
02716     ed->pFontCache[i].nAge = 0;
02717     ed->pFontCache[i].hFont = NULL;
02718   }
02719 
02720   ME_CheckCharOffsets(ed);
02721   ed->bDefaultFormatRect = TRUE;
02722   ITextHost_TxGetSelectionBarWidth(ed->texthost, &selbarwidth);
02723   if (selbarwidth) {
02724     /* FIXME: Convert selbarwidth from HIMETRIC to pixels */
02725     ed->selofs = SELECTIONBAR_WIDTH;
02726     ed->styleFlags |= ES_SELECTIONBAR;
02727   } else {
02728     ed->selofs = 0;
02729   }
02730   ed->nSelectionType = stPosition;
02731 
02732   ed->cPasswordMask = 0;
02733   if (props & TXTBIT_USEPASSWORD)
02734     ITextHost_TxGetPasswordChar(texthost, &ed->cPasswordMask);
02735 
02736   if (props & TXTBIT_AUTOWORDSEL)
02737     ed->styleFlags |= ECO_AUTOWORDSELECTION;
02738   if (props & TXTBIT_MULTILINE) {
02739     ed->styleFlags |= ES_MULTILINE;
02740     ed->bWordWrap = (props & TXTBIT_WORDWRAP) != 0;
02741   } else {
02742     ed->bWordWrap = FALSE;
02743   }
02744   if (props & TXTBIT_READONLY)
02745     ed->styleFlags |= ES_READONLY;
02746   if (!(props & TXTBIT_HIDESELECTION))
02747     ed->styleFlags |= ES_NOHIDESEL;
02748   if (props & TXTBIT_SAVESELECTION)
02749     ed->styleFlags |= ES_SAVESEL;
02750   if (props & TXTBIT_VERTICAL)
02751     ed->styleFlags |= ES_VERTICAL;
02752   if (props & TXTBIT_DISABLEDRAG)
02753     ed->styleFlags |= ES_NOOLEDRAGDROP;
02754 
02755   ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
02756 
02757   /* Default scrollbar information */
02758   ed->vert_si.cbSize = sizeof(SCROLLINFO);
02759   ed->vert_si.nMin = 0;
02760   ed->vert_si.nMax = 0;
02761   ed->vert_si.nPage = 0;
02762   ed->vert_si.nPos = 0;
02763 
02764   ed->horz_si.cbSize = sizeof(SCROLLINFO);
02765   ed->horz_si.nMin = 0;
02766   ed->horz_si.nMax = 0;
02767   ed->horz_si.nPage = 0;
02768   ed->horz_si.nPos = 0;
02769 
02770   OleInitialize(NULL);
02771 
02772   return ed;
02773 }
02774 
02775 static void ME_DestroyEditor(ME_TextEditor *editor)
02776 {
02777   ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
02778   ME_DisplayItem *p = pFirst, *pNext = NULL;
02779   int i;
02780 
02781   ME_ClearTempStyle(editor);
02782   ME_EmptyUndoStack(editor);
02783   while(p) {
02784     pNext = p->next;
02785     ME_DestroyDisplayItem(p);
02786     p = pNext;
02787   }
02788   ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
02789   for (i=0; i<HFONT_CACHE_SIZE; i++)
02790   {
02791     if (editor->pFontCache[i].hFont)
02792       DeleteObject(editor->pFontCache[i].hFont);
02793   }
02794   if (editor->rgbBackColor != -1)
02795     DeleteObject(editor->hbrBackground);
02796   if(editor->lpOleCallback)
02797     IUnknown_Release(editor->lpOleCallback);
02798   IUnknown_Release(editor->texthost);
02799   OleUninitialize();
02800 
02801   FREE_OBJ(editor->pBuffer);
02802   FREE_OBJ(editor->pCursors);
02803 
02804   FREE_OBJ(editor);
02805 }
02806 
02807 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
02808 {
02809     TRACE("\n");
02810     switch (fdwReason)
02811     {
02812     case DLL_PROCESS_ATTACH:
02813       DisableThreadLibraryCalls(hinstDLL);
02814       me_heap = HeapCreate (0, 0x10000, 0);
02815       if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
02816       hLeft = LoadCursorW(hinstDLL, MAKEINTRESOURCEW(OCR_REVERSE));
02817       LookupInit();
02818       break;
02819 
02820     case DLL_PROCESS_DETACH:
02821       UnregisterClassW(RICHEDIT_CLASS20W, 0);
02822       UnregisterClassW(MSFTEDIT_CLASS, 0);
02823       UnregisterClassA(RICHEDIT_CLASS20A, 0);
02824       UnregisterClassA("RichEdit50A", 0);
02825       if (ME_ListBoxRegistered)
02826           UnregisterClassW(REListBox20W, 0);
02827       if (ME_ComboBoxRegistered)
02828           UnregisterClassW(REComboBox20W, 0);
02829       LookupCleanup();
02830       HeapDestroy (me_heap);
02831       me_heap = NULL;
02832       break;
02833     }
02834     return TRUE;
02835 }
02836 
02837 
02838 static const char * const edit_messages[] = {
02839   "EM_GETSEL",
02840   "EM_SETSEL",
02841   "EM_GETRECT",
02842   "EM_SETRECT",
02843   "EM_SETRECTNP",
02844   "EM_SCROLL",
02845   "EM_LINESCROLL",
02846   "EM_SCROLLCARET",
02847   "EM_GETMODIFY",
02848   "EM_SETMODIFY",
02849   "EM_GETLINECOUNT",
02850   "EM_LINEINDEX",
02851   "EM_SETHANDLE",
02852   "EM_GETHANDLE",
02853   "EM_GETTHUMB",
02854   "EM_UNKNOWN_BF",
02855   "EM_UNKNOWN_C0",
02856   "EM_LINELENGTH",
02857   "EM_REPLACESEL",
02858   "EM_UNKNOWN_C3",
02859   "EM_GETLINE",
02860   "EM_LIMITTEXT",
02861   "EM_CANUNDO",
02862   "EM_UNDO",
02863   "EM_FMTLINES",
02864   "EM_LINEFROMCHAR",
02865   "EM_UNKNOWN_CA",
02866   "EM_SETTABSTOPS",
02867   "EM_SETPASSWORDCHAR",
02868   "EM_EMPTYUNDOBUFFER",
02869   "EM_GETFIRSTVISIBLELINE",
02870   "EM_SETREADONLY",
02871   "EM_SETWORDBREAKPROC",
02872   "EM_GETWORDBREAKPROC",
02873   "EM_GETPASSWORDCHAR",
02874   "EM_SETMARGINS",
02875   "EM_GETMARGINS",
02876   "EM_GETLIMITTEXT",
02877   "EM_POSFROMCHAR",
02878   "EM_CHARFROMPOS",
02879   "EM_SETIMESTATUS",
02880   "EM_GETIMESTATUS"
02881 };
02882 
02883 static const char * const richedit_messages[] = {
02884   "EM_CANPASTE",
02885   "EM_DISPLAYBAND",
02886   "EM_EXGETSEL",
02887   "EM_EXLIMITTEXT",
02888   "EM_EXLINEFROMCHAR",
02889   "EM_EXSETSEL",
02890   "EM_FINDTEXT",
02891   "EM_FORMATRANGE",
02892   "EM_GETCHARFORMAT",
02893   "EM_GETEVENTMASK",
02894   "EM_GETOLEINTERFACE",
02895   "EM_GETPARAFORMAT",
02896   "EM_GETSELTEXT",
02897   "EM_HIDESELECTION", 
02898   "EM_PASTESPECIAL",
02899   "EM_REQUESTRESIZE",
02900   "EM_SELECTIONTYPE",
02901   "EM_SETBKGNDCOLOR",
02902   "EM_SETCHARFORMAT",
02903   "EM_SETEVENTMASK",
02904   "EM_SETOLECALLBACK",
02905   "EM_SETPARAFORMAT",
02906   "EM_SETTARGETDEVICE",
02907   "EM_STREAMIN",
02908   "EM_STREAMOUT",
02909   "EM_GETTEXTRANGE",
02910   "EM_FINDWORDBREAK",
02911   "EM_SETOPTIONS",
02912   "EM_GETOPTIONS",
02913   "EM_FINDTEXTEX",
02914   "EM_GETWORDBREAKPROCEX",
02915   "EM_SETWORDBREAKPROCEX",
02916   "EM_SETUNDOLIMIT",
02917   "EM_UNKNOWN_USER_83",
02918   "EM_REDO",
02919   "EM_CANREDO",
02920   "EM_GETUNDONAME",
02921   "EM_GETREDONAME",
02922   "EM_STOPGROUPTYPING",
02923   "EM_SETTEXTMODE",
02924   "EM_GETTEXTMODE",
02925   "EM_AUTOURLDETECT",
02926   "EM_GETAUTOURLDETECT",
02927   "EM_SETPALETTE",
02928   "EM_GETTEXTEX",
02929   "EM_GETTEXTLENGTHEX",
02930   "EM_SHOWSCROLLBAR",
02931   "EM_SETTEXTEX",
02932   "EM_UNKNOWN_USER_98",
02933   "EM_UNKNOWN_USER_99",
02934   "EM_SETPUNCTUATION",
02935   "EM_GETPUNCTUATION",
02936   "EM_SETWORDWRAPMODE",
02937   "EM_GETWORDWRAPMODE",
02938   "EM_SETIMECOLOR",
02939   "EM_GETIMECOLOR",
02940   "EM_SETIMEOPTIONS",
02941   "EM_GETIMEOPTIONS",
02942   "EM_CONVPOSITION",
02943   "EM_UNKNOWN_USER_109",
02944   "EM_UNKNOWN_USER_110",
02945   "EM_UNKNOWN_USER_111",
02946   "EM_UNKNOWN_USER_112",
02947   "EM_UNKNOWN_USER_113",
02948   "EM_UNKNOWN_USER_114",
02949   "EM_UNKNOWN_USER_115",
02950   "EM_UNKNOWN_USER_116",
02951   "EM_UNKNOWN_USER_117",
02952   "EM_UNKNOWN_USER_118",
02953   "EM_UNKNOWN_USER_119",
02954   "EM_SETLANGOPTIONS",
02955   "EM_GETLANGOPTIONS",
02956   "EM_GETIMECOMPMODE",
02957   "EM_FINDTEXTW",
02958   "EM_FINDTEXTEXW",
02959   "EM_RECONVERSION",
02960   "EM_SETIMEMODEBIAS",
02961   "EM_GETIMEMODEBIAS"
02962 };
02963 
02964 static const char *
02965 get_msg_name(UINT msg)
02966 {
02967   if (msg >= EM_GETSEL && msg <= EM_CHARFROMPOS)
02968     return edit_messages[msg - EM_GETSEL];
02969   if (msg >= EM_CANPASTE && msg <= EM_GETIMEMODEBIAS)
02970     return richedit_messages[msg - EM_CANPASTE];
02971   return "";
02972 }
02973 
02974 static void ME_LinkNotify(ME_TextEditor *editor, UINT msg, WPARAM wParam, LPARAM lParam)
02975 {
02976   int x,y;
02977   BOOL isExact;
02978   ME_Cursor cursor; /* The start of the clicked text. */
02979 
02980   ENLINK info;
02981   x = (short)LOWORD(lParam);
02982   y = (short)HIWORD(lParam);
02983   ME_CharFromPos(editor, x, y, &cursor, &isExact);
02984   if (!isExact) return;
02985 
02986   if (cursor.pRun->member.run.style->fmt.dwMask & CFM_LINK &&
02987       cursor.pRun->member.run.style->fmt.dwEffects & CFE_LINK)
02988   { /* The clicked run has CFE_LINK set */
02989     info.nmhdr.hwndFrom = NULL;
02990     info.nmhdr.idFrom = 0;
02991     info.nmhdr.code = EN_LINK;
02992     info.msg = msg;
02993     info.wParam = wParam;
02994     info.lParam = lParam;
02995     cursor.nOffset = 0;
02996     info.chrg.cpMin = ME_GetCursorOfs(&cursor);
02997     info.chrg.cpMax = info.chrg.cpMin + cursor.pRun->member.run.strText->nLen;
02998     ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
02999   }
03000 }
03001 
03002 #define UNSUPPORTED_MSG(e) \
03003   case e:                  \
03004     FIXME(#e ": stub\n");  \
03005     *phresult = S_FALSE;   \
03006     return 0;
03007 
03008 /* Handle messages for windowless and windoweded richedit controls.
03009  *
03010  * The LRESULT that is returned is a return value for window procs,
03011  * and the phresult parameter is the COM return code needed by the
03012  * text services interface. */
03013 LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
03014                          LPARAM lParam, BOOL unicode, HRESULT* phresult)
03015 {
03016   *phresult = S_OK;
03017 
03018   switch(msg) {
03019 
03020   UNSUPPORTED_MSG(EM_DISPLAYBAND)
03021   UNSUPPORTED_MSG(EM_FINDWORDBREAK)
03022   UNSUPPORTED_MSG(EM_FMTLINES)
03023   UNSUPPORTED_MSG(EM_FORMATRANGE)
03024   UNSUPPORTED_MSG(EM_GETBIDIOPTIONS)
03025   UNSUPPORTED_MSG(EM_GETEDITSTYLE)
03026   UNSUPPORTED_MSG(EM_GETIMECOMPMODE)
03027   UNSUPPORTED_MSG(EM_GETIMESTATUS)
03028   UNSUPPORTED_MSG(EM_SETIMESTATUS)
03029   UNSUPPORTED_MSG(EM_GETLANGOPTIONS)
03030   UNSUPPORTED_MSG(EM_GETREDONAME)
03031   UNSUPPORTED_MSG(EM_GETTYPOGRAPHYOPTIONS)
03032   UNSUPPORTED_MSG(EM_GETUNDONAME)
03033   UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX)
03034   UNSUPPORTED_MSG(EM_PASTESPECIAL)
03035   UNSUPPORTED_MSG(EM_SELECTIONTYPE)
03036   UNSUPPORTED_MSG(EM_SETBIDIOPTIONS)
03037   UNSUPPORTED_MSG(EM_SETEDITSTYLE)
03038   UNSUPPORTED_MSG(EM_SETFONTSIZE)
03039   UNSUPPORTED_MSG(EM_SETLANGOPTIONS)
03040   UNSUPPORTED_MSG(EM_SETMARGINS)
03041   UNSUPPORTED_MSG(EM_SETPALETTE)
03042   UNSUPPORTED_MSG(EM_SETTABSTOPS)
03043   UNSUPPORTED_MSG(EM_SETTYPOGRAPHYOPTIONS)
03044   UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX)
03045 
03046 /* Messages specific to Richedit controls */
03047 
03048   case EM_STREAMIN:
03049    return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam, TRUE);
03050   case EM_STREAMOUT:
03051    return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam);
03052   case WM_GETDLGCODE:
03053   {
03054     UINT code = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
03055     if (lParam)
03056       editor->bDialogMode = TRUE;
03057     if (editor->styleFlags & ES_MULTILINE)
03058       code |= DLGC_WANTMESSAGE;
03059     return code;
03060   }
03061   case EM_EMPTYUNDOBUFFER:
03062     ME_EmptyUndoStack(editor);
03063     return 0;
03064   case EM_GETSEL:
03065   {
03066     /* Note: wParam/lParam can be NULL */
03067     UINT from, to;
03068     PUINT pfrom = wParam ? (PUINT)wParam : &from;
03069     PUINT pto = lParam ? (PUINT)lParam : &to;
03070     ME_GetSelectionOfs(editor, (int *)pfrom, (int *)pto);
03071     if ((*pfrom|*pto) & 0xFFFF0000)
03072       return -1;
03073     return MAKELONG(*pfrom,*pto);
03074   }
03075   case EM_EXGETSEL:
03076   {
03077     CHARRANGE *pRange = (CHARRANGE *)lParam;
03078     ME_GetSelectionOfs(editor, &pRange->cpMin, &pRange->cpMax);
03079     TRACE("EM_EXGETSEL = (%d,%d)\n", pRange->cpMin, pRange->cpMax);
03080     return 0;
03081   }
03082   case EM_SETUNDOLIMIT:
03083   {
03084     if ((int)wParam < 0)
03085       editor->nUndoLimit = STACK_SIZE_DEFAULT;
03086     else
03087       editor->nUndoLimit = min(wParam, STACK_SIZE_MAX);
03088     /* Setting a max stack size keeps wine from getting killed 
03089       for hogging memory. Windows allocates all this memory at once, so
03090       no program would realistically set a value above our maximum. */
03091     return editor->nUndoLimit;
03092   }
03093   case EM_CANUNDO:
03094     return editor->pUndoStack != NULL;
03095   case EM_CANREDO:
03096     return editor->pRedoStack != NULL;
03097   case WM_UNDO: /* FIXME: actually not the same */
03098   case EM_UNDO:
03099     return ME_Undo(editor);
03100   case EM_REDO:
03101     return ME_Redo(editor);
03102   case EM_GETOPTIONS:
03103   {
03104     /* these flags are equivalent to the ES_* counterparts */
03105     DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
03106                  ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN | ECO_SELECTIONBAR;
03107     DWORD settings = editor->styleFlags & mask;
03108 
03109     return settings;
03110   }
03111   case EM_SETOPTIONS:
03112   {
03113     /* these flags are equivalent to ES_* counterparts, except for
03114      * ECO_AUTOWORDSELECTION that doesn't have an ES_* counterpart,
03115      * but is still stored in editor->styleFlags. */
03116     const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
03117                        ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
03118                        ECO_SELECTIONBAR | ECO_AUTOWORDSELECTION;
03119     DWORD settings = mask & editor->styleFlags;
03120     DWORD oldSettings = settings;
03121     DWORD changedSettings;
03122 
03123     switch(wParam)
03124     {
03125       case ECOOP_SET:
03126         settings = lParam;
03127         break;
03128       case ECOOP_OR:
03129         settings |= lParam;
03130         break;
03131       case ECOOP_AND:
03132         settings &= lParam;
03133         break;
03134       case ECOOP_XOR:
03135         settings ^= lParam;
03136     }
03137     changedSettings = oldSettings ^ settings;
03138 
03139     if (changedSettings) {
03140       editor->styleFlags = (editor->styleFlags & ~mask) | (settings & mask);
03141 
03142       if (changedSettings & ECO_SELECTIONBAR)
03143       {
03144         ITextHost_TxInvalidateRect(editor->texthost, &editor->rcFormat, TRUE);
03145         if (settings & ECO_SELECTIONBAR) {
03146           assert(!editor->selofs);
03147           editor->selofs = SELECTIONBAR_WIDTH;
03148           editor->rcFormat.left += editor->selofs;
03149         } else {
03150           editor->rcFormat.left -= editor->selofs;
03151           editor->selofs = 0;
03152         }
03153         ME_RewrapRepaint(editor);
03154       }
03155 
03156       if (changedSettings & settings & ECO_VERTICAL)
03157         FIXME("ECO_VERTICAL not implemented yet!\n");
03158       if (changedSettings & settings & ECO_AUTOHSCROLL)
03159         FIXME("ECO_AUTOHSCROLL not implemented yet!\n");
03160       if (changedSettings & settings & ECO_AUTOVSCROLL)
03161         FIXME("ECO_AUTOVSCROLL not implemented yet!\n");
03162       if (changedSettings & settings & ECO_NOHIDESEL)
03163         FIXME("ECO_NOHIDESEL not implemented yet!\n");
03164       if (changedSettings & settings & ECO_WANTRETURN)
03165         FIXME("ECO_WANTRETURN not implemented yet!\n");
03166       if (changedSettings & settings & ECO_AUTOWORDSELECTION)
03167         FIXME("ECO_AUTOWORDSELECTION not implemented yet!\n");
03168     }
03169 
03170     return settings;
03171   }
03172   case EM_SETSEL:
03173   {
03174     ME_InvalidateSelection(editor);
03175     ME_SetSelection(editor, wParam, lParam);
03176     ME_InvalidateSelection(editor);
03177     ITextHost_TxShowCaret(editor->texthost, FALSE);
03178     ME_ShowCaret(editor);
03179     ME_SendSelChange(editor);
03180     return 0;
03181   }
03182   case EM_SETSCROLLPOS:
03183   {
03184     POINT *point = (POINT *)lParam;
03185     ME_ScrollAbs(editor, point->x, point->y);
03186     return 0;
03187   }
03188   case EM_AUTOURLDETECT:
03189   {
03190     if (wParam==1 || wParam ==0)
03191     {
03192         editor->AutoURLDetect_bEnable = (BOOL)wParam;
03193         return 0;
03194     }
03195     return E_INVALIDARG;
03196   }
03197   case EM_GETAUTOURLDETECT:
03198   {
03199     return editor->AutoURLDetect_bEnable;
03200   }
03201   case EM_EXSETSEL:
03202   {
03203     int end;
03204     CHARRANGE range = *(CHARRANGE *)lParam;
03205 
03206     TRACE("EM_EXSETSEL (%d,%d)\n", range.cpMin, range.cpMax);
03207 
03208     ME_InvalidateSelection(editor);
03209     end = ME_SetSelection(editor, range.cpMin, range.cpMax);
03210     ME_InvalidateSelection(editor);
03211     ITextHost_TxShowCaret(editor->texthost, FALSE);
03212     ME_ShowCaret(editor);
03213     ME_SendSelChange(editor);
03214 
03215     return end;
03216   }
03217   case EM_SHOWSCROLLBAR:
03218   {
03219     DWORD flags;
03220 
03221     switch (wParam)
03222     {
03223       case SB_HORZ:
03224         flags = WS_HSCROLL;
03225         break;
03226       case SB_VERT:
03227         flags = WS_VSCROLL;
03228         break;
03229       case SB_BOTH:
03230         flags = WS_HSCROLL|WS_VSCROLL;
03231         break;
03232       default:
03233         return 0;
03234     }
03235 
03236     if (lParam) {
03237       editor->styleFlags |= flags;
03238       if (flags & WS_HSCROLL)
03239         ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ,
03240                           editor->nTotalWidth > editor->sizeWindow.cx);
03241       if (flags & WS_VSCROLL)
03242         ITextHost_TxShowScrollBar(editor->texthost, SB_VERT,
03243                           editor->nTotalLength > editor->sizeWindow.cy);
03244     } else {
03245       editor->styleFlags &= ~flags;
03246       ITextHost_TxShowScrollBar(editor->texthost, wParam, FALSE);
03247     }
03248     return 0;
03249   }
03250   case EM_SETTEXTEX:
03251   {
03252     LPWSTR wszText;
03253     SETTEXTEX *pStruct = (SETTEXTEX *)wParam;
03254     size_t len = 0;
03255     int from, to;
03256     ME_Style *style;
03257     BOOL bRtf, bUnicode, bSelection;
03258     int oldModify = editor->nModifyStep;
03259 
03260     if (!pStruct) return 0;
03261 
03262     /* If we detect ascii rtf at the start of the string,
03263      * we know it isn't unicode. */
03264     bRtf = (lParam && (!strncmp((char *)lParam, "{\\rtf", 5) ||
03265                          !strncmp((char *)lParam, "{\\urtf", 6)));
03266     bUnicode = !bRtf && pStruct->codepage == 1200;
03267 
03268     TRACE("EM_SETTEXTEX - %s, flags %d, cp %d\n",
03269           bUnicode ? debugstr_w((LPCWSTR)lParam) : debugstr_a((LPCSTR)lParam),
03270           pStruct->flags, pStruct->codepage);
03271 
03272     bSelection = (pStruct->flags & ST_SELECTION) != 0;
03273     if (bSelection) {
03274       int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
03275       style = ME_GetSelectionInsertStyle(editor);
03276       ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to - from, FALSE);
03277     } else {
03278       ME_Cursor start;
03279       ME_SetCursorToStart(editor, &start);
03280       ME_InternalDeleteText(editor, &start, ME_GetTextLength(editor), FALSE);
03281       style = editor->pBuffer->pDefaultStyle;
03282     }
03283 
03284     if (bRtf) {
03285       ME_StreamInRTFString(editor, bSelection, (char *)lParam);
03286       if (bSelection) {
03287         /* FIXME: The length returned doesn't include the rtf control
03288          * characters, only the actual text. */
03289         len = lParam ? strlen((char *)lParam) : 0;
03290       }
03291     } else {
03292       /* FIXME: make use of pStruct->codepage in the to unicode translation */
03293       wszText = lParam ? ME_ToUnicode(bUnicode, (void *)lParam) : NULL;
03294       len = wszText ? lstrlenW(wszText) : 0;
03295       ME_InsertTextFromCursor(editor, 0, wszText, len, style);
03296       ME_EndToUnicode(bUnicode, wszText);
03297     }
03298 
03299     if (bSelection) {
03300       ME_ReleaseStyle(style);
03301       ME_UpdateSelectionLinkAttribute(editor);
03302     } else {
03303       ME_Cursor cursor;
03304       len = 1;
03305       ME_SetCursorToStart(editor, &cursor);
03306       ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
03307     }
03308     ME_CommitUndo(editor);
03309     if (!(pStruct->flags & ST_KEEPUNDO))
03310     {
03311       editor->nModifyStep = oldModify;
03312       ME_EmptyUndoStack(editor);
03313     }
03314     ME_UpdateRepaint(editor, FALSE);
03315     return len;
03316   }
03317   case EM_SETBKGNDCOLOR:
03318   {
03319     LRESULT lColor;
03320     if (editor->rgbBackColor != -1) {
03321       DeleteObject(editor->hbrBackground);
03322       lColor = editor->rgbBackColor;
03323     }
03324     else lColor = ITextHost_TxGetSysColor(editor->texthost, COLOR_WINDOW);
03325 
03326     if (wParam)
03327     {
03328       editor->rgbBackColor = -1;
03329       editor->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
03330     }
03331     else
03332     {
03333       editor->rgbBackColor = lParam;
03334       editor->hbrBackground = CreateSolidBrush(editor->rgbBackColor);
03335     }
03336     ITextHost_TxInvalidateRect(editor->texthost, NULL, TRUE);
03337     ITextHost_TxViewChange(editor->texthost, TRUE);
03338     return lColor;
03339   }
03340   case EM_GETMODIFY:
03341     return editor->nModifyStep == 0 ? 0 : -1;
03342   case EM_SETMODIFY:
03343   {
03344     if (wParam)
03345       editor->nModifyStep = 1;
03346     else
03347       editor->nModifyStep = 0;
03348     
03349     return 0;
03350   }
03351   case EM_SETREADONLY:
03352   {
03353     if (wParam)
03354       editor->styleFlags |= ES_READONLY;
03355     else
03356       editor->styleFlags &= ~ES_READONLY;
03357     return 0;
03358   }
03359   case EM_SETEVENTMASK:
03360   {
03361     DWORD nOldMask = editor->nEventMask;
03362     
03363     editor->nEventMask = lParam;
03364     return nOldMask;
03365   }
03366   case EM_GETEVENTMASK:
03367     return editor->nEventMask;
03368   case EM_SETCHARFORMAT:
03369   {
03370     CHARFORMAT2W buf, *p;
03371     BOOL bRepaint = TRUE;
03372     p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
03373     if (p == NULL) return 0;
03374     if (wParam & SCF_ALL) {
03375       if (editor->mode & TM_PLAINTEXT) {
03376         ME_SetDefaultCharFormat(editor, p);
03377       } else {
03378         ME_Cursor start;
03379         ME_SetCursorToStart(editor, &start);
03380         ME_SetCharFormat(editor, &start, NULL, p);
03381         editor->nModifyStep = 1;
03382       }
03383     } else if (wParam & SCF_SELECTION) {
03384       if (editor->mode & TM_PLAINTEXT)
03385         return 0;
03386       if (wParam & SCF_WORD) {
03387         FIXME("EM_SETCHARFORMAT: word selection not supported\n");
03388         return 0;
03389       }
03390       bRepaint = ME_IsSelection(editor);
03391       ME_SetSelectionCharFormat(editor, p);
03392       if (bRepaint) editor->nModifyStep = 1;
03393     } else { /* SCF_DEFAULT */
03394       ME_SetDefaultCharFormat(editor, p);
03395     }
03396     ME_CommitUndo(editor);
03397     if (bRepaint)
03398     {
03399       ME_WrapMarkedParagraphs(editor);
03400       ME_UpdateScrollBar(editor);
03401       ME_Repaint(editor);
03402     }
03403     return 1;
03404   }
03405   case EM_GETCHARFORMAT:
03406   {
03407     CHARFORMAT2W tmp, *dst = (CHARFORMAT2W *)lParam;
03408     if (dst->cbSize != sizeof(CHARFORMATA) &&
03409         dst->cbSize != sizeof(CHARFORMATW) &&
03410         dst->cbSize != sizeof(CHARFORMAT2A) &&
03411         dst->cbSize != sizeof(CHARFORMAT2W))
03412       return 0;
03413     tmp.cbSize = sizeof(tmp);
03414     if (!wParam)
03415       ME_GetDefaultCharFormat(editor, &tmp);
03416     else
03417       ME_GetSelectionCharFormat(editor, &tmp);
03418     ME_CopyToCFAny(dst, &tmp);
03419     return tmp.dwMask;
03420   }
03421   case EM_SETPARAFORMAT:
03422   {
03423     BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
03424     ME_WrapMarkedParagraphs(editor);
03425     ME_UpdateScrollBar(editor);
03426     ME_Repaint(editor);
03427     ME_CommitUndo(editor);
03428     return result;
03429   }
03430   case EM_GETPARAFORMAT:
03431     ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
03432     return ((PARAFORMAT2 *)lParam)->dwMask;
03433   case EM_GETFIRSTVISIBLELINE:
03434   {
03435     ME_DisplayItem *p = editor->pBuffer->pFirst;
03436     int y = editor->vert_si.nPos;
03437     int ypara = 0;
03438     int count = 0;
03439     int ystart, yend;
03440     while(p) {
03441       p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd);
03442       if (p->type == diTextEnd)
03443         break;
03444       if (p->type == diParagraph) {
03445         ypara = p->member.para.pt.y;
03446         continue;
03447       }
03448       ystart = ypara + p->member.row.pt.y;
03449       yend = ystart + p->member.row.nHeight;
03450       if (y < yend) {
03451         break;
03452       }
03453       count++;
03454     }
03455     return count;
03456   }
03457   case EM_HIDESELECTION:
03458   {
03459      editor->bHideSelection = (wParam != 0);
03460      ME_InvalidateSelection(editor);
03461      return 0;
03462   }
03463   case EM_LINESCROLL:
03464   {
03465     if (!(editor->styleFlags & ES_MULTILINE))
03466       return FALSE;
03467     ME_ScrollDown(editor, lParam * 8); /* FIXME follow the original */
03468     return TRUE;
03469   }
03470   case WM_CLEAR:
03471   {
03472     int from, to;
03473     int nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
03474     ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
03475     ME_CommitUndo(editor);
03476     ME_UpdateRepaint(editor, TRUE);
03477     return 0;
03478   }
03479   case EM_REPLACESEL:
03480   {
03481     int from, to, nStartCursor;
03482     ME_Style *style;
03483     LPWSTR wszText = lParam ? ME_ToUnicode(unicode, (void *)lParam) : NULL;
03484     size_t len = wszText ? lstrlenW(wszText) : 0;
03485     TRACE("EM_REPLACESEL - %s\n", debugstr_w(wszText));
03486 
03487     nStartCursor = ME_GetSelectionOfs(editor, &from, &to);
03488     style = ME_GetSelectionInsertStyle(editor);
03489     ME_InternalDeleteText(editor, &editor->pCursors[nStartCursor], to-from, FALSE);
03490     ME_InsertTextFromCursor(editor, 0, wszText, len, style);
03491     ME_ReleaseStyle(style);
03492     /* drop temporary style if line end */
03493     /*
03494      * FIXME question: does abc\n mean: put abc,
03495      * clear temp style, put \n? (would require a change)
03496      */
03497     if (len>0 && wszText[len-1] == '\n')
03498       ME_ClearTempStyle(editor);
03499     ME_EndToUnicode(unicode, wszText);
03500     ME_CommitUndo(editor);
03501     ME_UpdateSelectionLinkAttribute(editor);
03502     if (!wParam)
03503       ME_EmptyUndoStack(editor);
03504     ME_UpdateRepaint(editor, FALSE);
03505     return len;
03506   }
03507   case EM_SCROLLCARET:
03508     ME_EnsureVisible(editor, &editor->pCursors[0]);
03509     return 0;
03510   case WM_SETFONT:
03511   {
03512     LOGFONTW lf;
03513     CHARFORMAT2W fmt;
03514     HDC hDC;
03515     BOOL bRepaint = LOWORD(lParam);
03516 
03517     if (!wParam)
03518       wParam = (WPARAM)GetStockObject(SYSTEM_FONT);
03519     GetObjectW((HGDIOBJ)wParam, sizeof(LOGFONTW), &lf);
03520     hDC = ITextHost_TxGetDC(editor->texthost);
03521     ME_CharFormatFromLogFont(hDC, &lf, &fmt);
03522     ITextHost_TxReleaseDC(editor->texthost, hDC);
03523     if (editor->mode & TM_RICHTEXT) {
03524       ME_Cursor start;
03525       ME_SetCursorToStart(editor, &start);
03526       ME_SetCharFormat(editor, &start, NULL, &fmt);
03527     }
03528     ME_SetDefaultCharFormat(editor, &fmt);
03529 
03530     ME_CommitUndo(editor);
03531     ME_MarkAllForWrapping(editor);
03532     ME_WrapMarkedParagraphs(editor);
03533     ME_UpdateScrollBar(editor);
03534     if (bRepaint)
03535       ME_Repaint(editor);
03536     return 0;
03537   }
03538   case WM_SETTEXT:
03539   {
03540     ME_Cursor cursor;
03541     ME_SetCursorToStart(editor, &cursor);
03542     ME_InternalDeleteText(editor, &cursor, ME_GetTextLength(editor), FALSE);
03543     if (lParam)
03544     {
03545       TRACE("WM_SETTEXT lParam==%lx\n",lParam);
03546       if (!strncmp((char *)lParam, "{\\rtf", 5) ||
03547           !strncmp((char *)lParam, "{\\urtf", 6))
03548       {
03549         /* Undocumented: WM_SETTEXT supports RTF text */
03550         ME_StreamInRTFString(editor, 0, (char *)lParam);
03551       }
03552       else
03553       {
03554         LPWSTR wszText = ME_ToUnicode(unicode, (void *)lParam);
03555         TRACE("WM_SETTEXT - %s\n", debugstr_w(wszText)); /* debugstr_w() */
03556         if (lstrlenW(wszText) > 0)
03557         {
03558           int len = -1;
03559 
03560           /* uses default style! */
03561           if (!(editor->styleFlags & ES_MULTILINE))
03562           {
03563             WCHAR * p;
03564 
03565             p = wszText;
03566             while (*p != '\0' && *p != '\r' && *p != '\n') p++;
03567             len = p - wszText;
03568           }
03569           ME_InsertTextFromCursor(editor, 0, wszText, len, editor->pBuffer->pDefaultStyle);
03570         }
03571         ME_EndToUnicode(unicode, wszText);
03572       }
03573     }
03574     else
03575       TRACE("WM_SETTEXT - NULL\n");
03576     ME_SetCursorToStart(editor, &cursor);
03577     ME_UpdateLinkAttribute(editor, &cursor, INT_MAX);
03578     ME_SetSelection(editor, 0, 0);
03579     editor->nModifyStep = 0;
03580     ME_CommitUndo(editor);
03581     ME_EmptyUndoStack(editor);
03582     ME_UpdateRepaint(editor, FALSE);
03583     return 1;
03584   }
03585   case EM_CANPASTE:
03586   {
03587     UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
03588     if (IsClipboardFormatAvailable(nRTFFormat))
03589       return TRUE;
03590     if (IsClipboardFormatAvailable(CF_UNICODETEXT))
03591       return TRUE;
03592     return FALSE;
03593   }
03594   case WM_PASTE:
03595     ME_Paste(editor);
03596     return 0;
03597   case WM_CUT:
03598   case WM_COPY:
03599   {
03600     int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
03601     int nChars = nTo - nFrom;
03602     ME_Cursor *selStart = &editor->pCursors[nStartCur];
03603 
03604     if (ME_Copy(editor, selStart, nChars) && msg == WM_CUT)
03605     {
03606       ME_InternalDeleteText(editor, selStart, nChars, FALSE);
03607       ME_CommitUndo(editor);
03608       ME_UpdateRepaint(editor, TRUE);
03609     }
03610     return 0;
03611   }
03612   case WM_GETTEXTLENGTH:
03613   {
03614     GETTEXTLENGTHEX how;
03615 
03616     /* CR/LF conversion required in 2.0 mode, verbatim in 1.0 mode */
03617     how.flags = GTL_CLOSE | (editor->bEmulateVersion10 ? 0 : GTL_USECRLF) | GTL_NUMCHARS;
03618     how.codepage = unicode ? 1200 : CP_ACP;
03619     return ME_GetTextLengthEx(editor, &how);
03620   }
03621   case EM_GETTEXTLENGTHEX:
03622     return ME_GetTextLengthEx(editor, (GETTEXTLENGTHEX *)wParam);
03623   case WM_GETTEXT:
03624   {
03625     GETTEXTEX ex;
03626     ex.cb = wParam * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
03627     ex.flags = GT_USECRLF;
03628     ex.codepage = unicode ? 1200 : CP_ACP;
03629     ex.lpDefaultChar = NULL;
03630     ex.lpUsedDefChar = NULL;
03631     return ME_GetTextEx(editor, &ex, lParam);
03632   }
03633   case EM_GETTEXTEX:
03634     return ME_GetTextEx(editor, (GETTEXTEX*)wParam, lParam);
03635   case EM_GETSELTEXT:
03636   {
03637     int nFrom, nTo, nStartCur = ME_GetSelectionOfs(editor, &nFrom, &nTo);
03638     ME_Cursor *from = &editor->pCursors[nStartCur];
03639     return ME_GetTextRange(editor, (WCHAR *)lParam, from,
03640                            nTo - nFrom, unicode);
03641   }
03642   case EM_GETSCROLLPOS:
03643   {
03644     POINT *point = (POINT *)lParam;
03645     point->x = editor->horz_si.nPos;
03646     point->y = editor->vert_si.nPos;
03647     /* 16-bit scaled value is returned as stored in scrollinfo */
03648     if (editor->horz_si.nMax > 0xffff)
03649       point->x = MulDiv(point->x, 0xffff, editor->horz_si.nMax);
03650     if (editor->vert_si.nMax > 0xffff)
03651       point->y = MulDiv(point->y, 0xffff, editor->vert_si.nMax);
03652     return 1;
03653   }
03654   case EM_GETTEXTRANGE:
03655   {
03656     TEXTRANGEW *rng = (TEXTRANGEW *)lParam;
03657     ME_Cursor start;
03658     int nStart = rng->chrg.cpMin;
03659     int nEnd = rng->chrg.cpMax;
03660     int textlength = ME_GetTextLength(editor);
03661 
03662     TRACE("EM_GETTEXTRANGE min=%d max=%d unicode=%d textlength=%d\n",
03663           rng->chrg.cpMin, rng->chrg.cpMax, unicode, textlength);
03664     if (nStart < 0) return 0;
03665     if ((nStart == 0 && nEnd == -1) || nEnd > textlength)
03666       nEnd = textlength;
03667     if (nStart >= nEnd) return 0;
03668 
03669     ME_CursorFromCharOfs(editor, nStart, &start);
03670     return ME_GetTextRange(editor, rng->lpstrText, &start, nEnd - nStart, unicode);
03671   }
03672   case EM_GETLINE:
03673   {
03674     ME_DisplayItem *run;
03675     const unsigned int nMaxChars = *(WORD *) lParam;
03676     unsigned int nCharsLeft = nMaxChars;
03677     char *dest = (char *) lParam;
03678     BOOL wroteNull = FALSE;
03679 
03680     TRACE("EM_GETLINE: row=%d, nMaxChars=%d (%s)\n", (int) wParam, nMaxChars,
03681           unicode ? "Unicode" : "Ansi");
03682 
03683     run = ME_FindRowWithNumber(editor, wParam);
03684     if (run == NULL)
03685       return 0;
03686 
03687     while (nCharsLeft && (run = ME_FindItemFwd(run, diRunOrStartRow))
03688            && run->type == diRun)
03689     {
03690       unsigned int nCopy;
03691       ME_String *strText;
03692 
03693       strText = run->member.run.strText;
03694       nCopy = min(nCharsLeft, strText->nLen);
03695 
03696       if (unicode)
03697         memcpy(dest, strText->szData, nCopy * sizeof(WCHAR));
03698       else
03699         nCopy = WideCharToMultiByte(CP_ACP, 0, strText->szData, nCopy, dest,
03700                                     nCharsLeft, NULL, NULL);
03701       dest += nCopy * (unicode ? sizeof(WCHAR) : 1);
03702       nCharsLeft -= nCopy;
03703     }
03704 
03705     /* append line termination, space allowing */
03706     if (nCharsLeft > 0)
03707     {
03708       if (unicode)
03709         *((WCHAR *)dest) = '\0';
03710       else
03711         *dest = '\0';
03712       nCharsLeft--;
03713       wroteNull = TRUE;
03714     }
03715 
03716     TRACE("EM_GETLINE: got %u characters\n", nMaxChars - nCharsLeft);
03717     return nMaxChars - nCharsLeft - (wroteNull ? 1 : 0);
03718   }
03719   case EM_GETLINECOUNT:
03720   {
03721     ME_DisplayItem *item = editor->pBuffer->pFirst->next;
03722     int nRows = 0;
03723 
03724     ME_DisplayItem *prev_para = NULL, *last_para = NULL;
03725 
03726     while (item != editor->pBuffer->pLast)
03727     {
03728       assert(item->type == diParagraph);
03729       prev_para = ME_FindItemBack(item, diRun);
03730       if (prev_para) {
03731         assert(prev_para->member.run.nFlags & MERF_ENDPARA);
03732       }
03733       nRows += item->member.para.nRows;
03734       item = item->member.para.next_para;
03735     }
03736     last_para = ME_FindItemBack(item, diRun);
03737     assert(last_para);
03738     assert(last_para->member.run.nFlags & MERF_ENDPARA);
03739     if (editor->bEmulateVersion10 && prev_para &&
03740         last_para->member.run.nCharOfs == 0 &&
03741         prev_para->member.run.strText->nLen == 1 &&
03742         prev_para->member.run.strText->szData[0] == '\r')
03743     {
03744       /* In 1.0 emulation, the last solitary \r at the very end of the text
03745          (if one exists) is NOT a line break.
03746          FIXME: this is an ugly hack. This should have a more regular model. */
03747       nRows--;
03748     }
03749 
03750     TRACE("EM_GETLINECOUNT: nRows==%d\n", nRows);
03751     return max(1, nRows);
03752   }
03753   case EM_LINEFROMCHAR:
03754   {
03755     if (wParam == -1)
03756       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
03757     else
03758       return ME_RowNumberFromCharOfs(editor, wParam);
03759   }
03760   case EM_EXLINEFROMCHAR:
03761   {
03762     if (lParam == -1)
03763       return ME_RowNumberFromCharOfs(editor, ME_GetCursorOfs(&editor->pCursors[1]));
03764     else    
03765       return ME_RowNumberFromCharOfs(editor, lParam);
03766   }
03767   case EM_LINEINDEX:
03768   {
03769     ME_DisplayItem *item, *para;
03770     int nCharOfs;
03771     
03772     if (wParam == -1)
03773       item = ME_FindItemBack(editor->pCursors[0].pRun, diStartRow);
03774     else
03775       item = ME_FindRowWithNumber(editor, wParam);
03776     if (!item)
03777       return -1;
03778     para = ME_GetParagraph(item);
03779     item = ME_FindItemFwd(item, diRun);
03780     nCharOfs = para->member.para.nCharOfs + item->member.run.nCharOfs;
03781     TRACE("EM_LINEINDEX: nCharOfs==%d\n", nCharOfs);
03782     return nCharOfs;
03783   }
03784   case EM_LINELENGTH:
03785   {
03786     ME_DisplayItem *item, *item_end;
03787     int nChars = 0, nThisLineOfs = 0, nNextLineOfs = 0;
03788     ME_DisplayItem *para, *run;
03789 
03790     if (wParam > ME_GetTextLength(editor))
03791       return 0;
03792     if (wParam == -1)
03793     {
03794       FIXME("EM_LINELENGTH: returning number of unselected characters on lines with selection unsupported.\n");
03795       return 0;
03796     }
03797     ME_RunOfsFromCharOfs(editor, wParam, &para, &run, NULL);
03798     item = ME_RowStart(run);
03799     nThisLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item, diRun), 0);
03800     item_end = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd);
03801     if (item_end->type == diStartRow) {
03802       nNextLineOfs = ME_CharOfsFromRunOfs(editor, para, ME_FindItemFwd(item_end, diRun), 0);
03803     } else {
03804       ME_DisplayItem *endRun = ME_FindItemBack(item_end, diRun);
03805       assert(endRun && endRun->member.run.nFlags & MERF_ENDPARA);
03806       nNextLineOfs = item_end->member.para.nCharOfs - endRun->member.run.strText->nLen;
03807     }
03808     nChars = nNextLineOfs - nThisLineOfs;
03809     TRACE("EM_LINELENGTH(%ld)==%d\n",wParam, nChars);
03810     return nChars;
03811   }
03812   case EM_EXLIMITTEXT:
03813   {
03814     if ((int)lParam < 0)
03815      return 0;
03816     if (lParam == 0)
03817       editor->nTextLimit = 65536;
03818     else
03819       editor->nTextLimit = (int) lParam;
03820     return 0;
03821   }
03822   case EM_LIMITTEXT:
03823   {
03824     if (wParam == 0)
03825       editor->nTextLimit = 65536;
03826     else
03827       editor->nTextLimit = (int) wParam;
03828     return 0;
03829   }
03830   case EM_GETLIMITTEXT:
03831   {
03832     return editor->nTextLimit;
03833   }
03834   case EM_FINDTEXT:
03835   {
03836     FINDTEXTA *ft = (FINDTEXTA *)lParam;
03837     int nChars = MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, NULL, 0);
03838     WCHAR *tmp;
03839     LRESULT r;
03840 
03841     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
03842       MultiByteToWideChar(CP_ACP, 0, ft->lpstrText, -1, tmp, nChars);
03843     r = ME_FindText(editor, wParam, &ft->chrg, tmp, NULL);
03844     FREE_OBJ( tmp );
03845     return r;
03846   }
03847   case EM_FINDTEXTEX:
03848   {
03849     FINDTEXTEXA *ex = (FINDTEXTEXA *)lParam;
03850     int nChars = MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, NULL, 0);
03851     WCHAR *tmp;
03852     LRESULT r;
03853 
03854     if ((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL)
03855       MultiByteToWideChar(CP_ACP, 0, ex->lpstrText, -1, tmp, nChars);
03856     r = ME_FindText(editor, wParam, &ex->chrg, tmp, &ex->chrgText);
03857     FREE_OBJ( tmp );
03858     return r;
03859   }
03860   case EM_FINDTEXTW:
03861   {
03862     FINDTEXTW *ft = (FINDTEXTW *)lParam;
03863     return ME_FindText(editor, wParam, &ft->chrg, ft->lpstrText, NULL);
03864   }
03865   case EM_FINDTEXTEXW:
03866   {
03867     FINDTEXTEXW *ex = (FINDTEXTEXW *)lParam;
03868     return ME_FindText(editor, wParam, &ex->chrg, ex->lpstrText, &ex->chrgText);
03869   }
03870   case EM_GETZOOM:
03871     if (!wParam || !lParam)
03872       return FALSE;
03873     *(int *)wParam = editor->nZoomNumerator;
03874     *(int *)lParam = editor->nZoomDenominator;
03875     return TRUE;
03876   case EM_SETZOOM:
03877     return ME_SetZoom(editor, wParam, lParam);
03878   case EM_CHARFROMPOS:
03879   {
03880     ME_Cursor cursor;
03881     if (ME_CharFromPos(editor, ((POINTL *)lParam)->x, ((POINTL *)lParam)->y,
03882                        &cursor, NULL))
03883       return ME_GetCursorOfs(&cursor);
03884     else
03885       return -1;
03886   }
03887   case EM_POSFROMCHAR:
03888   {
03889     ME_DisplayItem *pPara, *pRun;
03890     int nCharOfs, nOffset, nLength;
03891     POINTL pt = {0,0};
03892 
03893     nCharOfs = wParam;
03894     /* detect which API version we're dealing with */
03895     if (wParam >= 0x40000)
03896         nCharOfs = lParam;
03897     nLength = ME_GetTextLength(editor);
03898     nCharOfs = min(nCharOfs, nLength);
03899     nCharOfs = max(nCharOfs, 0);
03900 
03901     ME_RunOfsFromCharOfs(editor, nCharOfs, &pPara, &pRun, &nOffset);
03902     assert(pRun->type == diRun);
03903     pt.y = pRun->member.run.pt.y;
03904     pt.x = pRun->member.run.pt.x + ME_PointFromChar(editor, &pRun->member.run, nOffset);
03905     pt.y += pPara->member.para.pt.y + editor->rcFormat.top;
03906     pt.x += editor->rcFormat.left;
03907 
03908     pt.x -= editor->horz_si.nPos;
03909     pt.y -= editor->vert_si.nPos;
03910 
03911     if (wParam >= 0x40000) {
03912         *(POINTL *)wParam = pt;
03913     }
03914     return (wParam >= 0x40000) ? 0 : MAKELONG( pt.x, pt.y );
03915   }
03916   case WM_CREATE:
03917   {
03918     INT max;
03919 
03920     ME_SetDefaultFormatRect(editor);
03921 
03922     max = (editor->styleFlags & ES_DISABLENOSCROLL) ? 1 : 0;
03923     if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_VSCROLL)
03924       ITextHost_TxSetScrollRange(editor->texthost, SB_VERT, 0, max, TRUE);
03925 
03926     if (~editor->styleFlags & ES_DISABLENOSCROLL || editor->styleFlags & WS_HSCROLL)
03927       ITextHost_TxSetScrollRange(editor->texthost, SB_HORZ, 0, max, TRUE);
03928 
03929     if (editor->styleFlags & ES_DISABLENOSCROLL)
03930     {
03931       if (editor->styleFlags & WS_VSCROLL)
03932       {
03933         ITextHost_TxEnableScrollBar(editor->texthost, SB_VERT, ESB_DISABLE_BOTH);
03934         ITextHost_TxShowScrollBar(editor->texthost, SB_VERT, TRUE);
03935       }
03936       if (editor->styleFlags & WS_HSCROLL)
03937       {
03938         ITextHost_TxEnableScrollBar(editor->texthost, SB_HORZ, ESB_DISABLE_BOTH);
03939         ITextHost_TxShowScrollBar(editor->texthost, SB_HORZ, TRUE);
03940       }
03941     }
03942 
03943     ME_CommitUndo(editor);
03944     ME_WrapMarkedParagraphs(editor);
03945     ME_MoveCaret(editor);
03946     return 0;
03947   }
03948   case WM_DESTROY:
03949     ME_DestroyEditor(editor);
03950     return 0;
03951   case WM_SETCURSOR:
03952   {
03953     return ME_SetCursor(editor);
03954   }
03955   case WM_LBUTTONDBLCLK:
03956   case WM_LBUTTONDOWN:
03957   {
03958     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
03959     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
03960         !ME_FilterEvent(editor, msg, &wParam, &lParam))
03961       return 0;
03962     ITextHost_TxSetFocus(editor->texthost);
03963     ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam),
03964                    ME_CalculateClickCount(editor, msg, wParam, lParam));
03965     ITextHost_TxSetCapture(editor->texthost, TRUE);
03966     editor->bMouseCaptured = TRUE;
03967     ME_LinkNotify(editor,msg,wParam,lParam);
03968     if (!ME_SetCursor(editor)) goto do_default;
03969     break;
03970   }
03971   case WM_MOUSEMOVE:
03972     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
03973         !ME_FilterEvent(editor, msg, &wParam, &lParam))
03974       return 0;
03975     if (editor->bMouseCaptured)
03976       ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam));
03977     ME_LinkNotify(editor,msg,wParam,lParam);
03978     /* Set cursor if mouse is captured, since WM_SETCURSOR won't be received. */
03979     if (editor->bMouseCaptured)
03980         ME_SetCursor(editor);
03981     break;
03982   case WM_LBUTTONUP:
03983     if (editor->bMouseCaptured) {
03984       ITextHost_TxSetCapture(editor->texthost, FALSE);
03985       editor->bMouseCaptured = FALSE;
03986     }
03987     if (editor->nSelectionType == stDocument)
03988       editor->nSelectionType = stPosition;
03989     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
03990         !ME_FilterEvent(editor, msg, &wParam, &lParam))
03991       return 0;
03992     else
03993     {
03994       ME_SetCursor(editor);
03995       ME_LinkNotify(editor,msg,wParam,lParam);
03996     }
03997     break;
03998   case WM_RBUTTONUP:
03999   case WM_RBUTTONDOWN:
04000     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
04001     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
04002         !ME_FilterEvent(editor, msg, &wParam, &lParam))
04003       return 0;
04004     goto do_default;
04005   case WM_CONTEXTMENU:
04006     if (!ME_ShowContextMenu(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)))
04007       goto do_default;
04008     break;
04009   case WM_SETFOCUS:
04010     editor->bHaveFocus = TRUE;
04011     ME_ShowCaret(editor);
04012     ME_SendOldNotify(editor, EN_SETFOCUS);
04013     return 0;
04014   case WM_KILLFOCUS:
04015     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
04016     editor->bHaveFocus = FALSE;
04017     ME_HideCaret(editor);
04018     ME_SendOldNotify(editor, EN_KILLFOCUS);
04019     return 0;
04020   case WM_COMMAND:
04021     TRACE("editor wnd command = %d\n", LOWORD(wParam));
04022     return 0;
04023   case WM_KEYUP:
04024     if ((editor->nEventMask & ENM_KEYEVENTS) &&
04025         !ME_FilterEvent(editor, msg, &wParam, &lParam))
04026       return 0;
04027     goto do_default;
04028   case WM_KEYDOWN:
04029     if ((editor->nEventMask & ENM_KEYEVENTS) &&
04030         !ME_FilterEvent(editor, msg, &wParam, &lParam))
04031       return 0;
04032     if (ME_KeyDown(editor, LOWORD(wParam)))
04033       return 0;
04034     goto do_default;
04035   case WM_CHAR:
04036     return ME_Char(editor, wParam, lParam, unicode);
04037   case WM_UNICHAR:
04038     if (unicode)
04039     {
04040         if(wParam == UNICODE_NOCHAR) return TRUE;
04041         if(wParam <= 0x000fffff)
04042         {
04043             if(wParam > 0xffff) /* convert to surrogates */
04044             {
04045                 wParam -= 0x10000;
04046                 ME_Char(editor, (wParam >> 10) + 0xd800, 0, TRUE);
04047                 ME_Char(editor, (wParam & 0x03ff) + 0xdc00, 0, TRUE);
04048             } else {
04049               ME_Char(editor, wParam, 0, TRUE);
04050             }
04051         }
04052         return 0;
04053     }
04054     break;
04055   case EM_STOPGROUPTYPING:
04056     ME_CommitUndo(editor); /* End coalesced undos for typed characters */
04057     return 0;
04058   case WM_HSCROLL:
04059   {
04060     const int scrollUnit = 7;
04061 
04062     switch(LOWORD(wParam))
04063     {
04064       case SB_LEFT:
04065         ME_ScrollAbs(editor, 0, 0);
04066         break;
04067       case SB_RIGHT:
04068         ME_ScrollAbs(editor,
04069                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
04070                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
04071         break;
04072       case SB_LINELEFT:
04073         ME_ScrollLeft(editor, scrollUnit);
04074         break;
04075       case SB_LINERIGHT:
04076         ME_ScrollRight(editor, scrollUnit);
04077         break;
04078       case SB_PAGELEFT:
04079         ME_ScrollLeft(editor, editor->sizeWindow.cx);
04080         break;
04081       case SB_PAGERIGHT:
04082         ME_ScrollRight(editor, editor->sizeWindow.cx);
04083         break;
04084       case SB_THUMBTRACK:
04085       case SB_THUMBPOSITION:
04086       {
04087         int pos = HIWORD(wParam);
04088         if (editor->horz_si.nMax > 0xffff)
04089           pos = MulDiv(pos, editor->horz_si.nMax, 0xffff);
04090         ME_HScrollAbs(editor, pos);
04091         break;
04092       }
04093     }
04094     break;
04095   }
04096   case EM_SCROLL: /* fall through */
04097   case WM_VSCROLL:
04098   {
04099     int origNPos;
04100     int lineHeight;
04101 
04102     origNPos = editor->vert_si.nPos;
04103     lineHeight = 24;
04104 
04105     if (editor->pBuffer && editor->pBuffer->pDefaultStyle)
04106       lineHeight = editor->pBuffer->pDefaultStyle->tm.tmHeight;
04107     if (lineHeight <= 0) lineHeight = 24;
04108 
04109     switch(LOWORD(wParam))
04110     {
04111       case SB_TOP:
04112         ME_ScrollAbs(editor, 0, 0);
04113         break;
04114       case SB_BOTTOM:
04115         ME_ScrollAbs(editor,
04116                      editor->horz_si.nMax - (int)editor->horz_si.nPage,
04117                      editor->vert_si.nMax - (int)editor->vert_si.nPage);
04118         break;
04119       case SB_LINEUP:
04120         ME_ScrollUp(editor,lineHeight);
04121         break;
04122       case SB_LINEDOWN:
04123         ME_ScrollDown(editor,lineHeight);
04124         break;
04125       case SB_PAGEUP:
04126         ME_ScrollUp(editor,editor->sizeWindow.cy);
04127         break;
04128       case SB_PAGEDOWN:
04129         ME_ScrollDown(editor,editor->sizeWindow.cy);
04130         break;
04131       case SB_THUMBTRACK:
04132       case SB_THUMBPOSITION:
04133       {
04134         int pos = HIWORD(wParam);
04135         if (editor->vert_si.nMax > 0xffff)
04136           pos = MulDiv(pos, editor->vert_si.nMax, 0xffff);
04137         ME_VScrollAbs(editor, pos);
04138         break;
04139       }
04140     }
04141     if (msg == EM_SCROLL)
04142       return 0x00010000 | (((editor->vert_si.nPos - origNPos)/lineHeight) & 0xffff);
04143     break;
04144   }
04145   case WM_MOUSEWHEEL:
04146   {
04147     int gcWheelDelta;
04148     UINT pulScrollLines;
04149     BOOL ctrl_is_down;
04150 
04151     if ((editor->nEventMask & ENM_MOUSEEVENTS) &&
04152         !ME_FilterEvent(editor, msg, &wParam, &lParam))
04153       return 0;
04154 
04155     ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
04156 
04157     gcWheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
04158 
04159     if (abs(gcWheelDelta) >= WHEEL_DELTA)
04160     {
04161       if (ctrl_is_down) {
04162         int numerator;
04163         if (!editor->nZoomNumerator || !editor->nZoomDenominator)
04164         {
04165           numerator = 100;
04166         } else {
04167           numerator = editor->nZoomNumerator * 100 / editor->nZoomDenominator;
04168         }
04169         numerator = numerator + (gcWheelDelta / WHEEL_DELTA) * 10;
04170         if (numerator >= 10 && numerator <= 500)
04171           ME_SetZoom(editor, numerator, 100);
04172       } else {
04173         SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
04174         /* FIXME follow the original */
04175         if (pulScrollLines)
04176           ME_ScrollDown(editor,pulScrollLines * (-gcWheelDelta / WHEEL_DELTA) * 8);
04177       }
04178     }
04179     break;
04180   }
04181   case EM_GETRECT:
04182   {
04183     *((RECT *)lParam) = editor->rcFormat;
04184     if (editor->bDefaultFormatRect)
04185       ((RECT *)lParam)->left -= editor->selofs;
04186     return 0;
04187   }
04188   case EM_SETRECT:
04189   case EM_SETRECTNP:
04190   {
04191     if (lParam)
04192     {
04193       int border = 0;
04194       RECT clientRect;
04195       RECT *rc = (RECT *)lParam;
04196 
04197       border = editor->exStyleFlags & WS_EX_CLIENTEDGE ? 1 : 0;
04198       ITextHost_TxGetClientRect(editor->texthost, &clientRect);
04199       if (wParam == 0)
04200       {
04201         editor->rcFormat.top = max(0, rc->top - border);
04202         editor->rcFormat.left = max(0, rc->left - border);
04203         editor->rcFormat.bottom = min(clientRect.bottom, rc->bottom);
04204         editor->rcFormat.right = min(clientRect.right, rc->right + border);
04205       } else if (wParam == 1) {
04206         /* MSDN incorrectly says a wParam value of 1 causes the
04207          * lParam rect to be used as a relative offset,
04208          * however, the tests show it just prevents min/max bound
04209          * checking. */
04210         editor->rcFormat.top = rc->top - border;
04211         editor->rcFormat.left = rc->left - border;
04212         editor->rcFormat.bottom = rc->bottom;
04213         editor->rcFormat.right = rc->right + border;
04214       } else {
04215         return 0;
04216       }
04217       editor->bDefaultFormatRect = FALSE;
04218     }
04219     else
04220     {
04221       ME_SetDefaultFormatRect(editor);
04222       editor->bDefaultFormatRect = TRUE;
04223     }
04224     ME_MarkAllForWrapping(editor);
04225     ME_WrapMarkedParagraphs(editor);
04226     ME_UpdateScrollBar(editor);
04227     if (msg != EM_SETRECTNP)
04228       ME_Repaint(editor);
04229     return 0;
04230   }
04231   case EM_REQUESTRESIZE:
04232     ME_SendRequestResize(editor, TRUE);
04233     return 0;
04234   case WM_SETREDRAW:
04235     goto do_default;
04236   case WM_SIZE:
04237   {
04238     RECT clientRect;
04239 
04240     ITextHost_TxGetClientRect(editor->texthost, &clientRect);
04241     if (editor->bDefaultFormatRect) {
04242       ME_SetDefaultFormatRect(editor);
04243     } else {
04244       editor->rcFormat.right += clientRect.right - editor->prevClientRect.right;
04245       editor->rcFormat.bottom += clientRect.bottom - editor->prevClientRect.bottom;
04246     }
04247     editor->prevClientRect = clientRect;
04248     ME_RewrapRepaint(editor);
04249     goto do_default;
04250   }
04251   /* IME messages to make richedit controls IME aware */
04252   case WM_IME_SETCONTEXT:
04253   case WM_IME_CONTROL:
04254   case WM_IME_SELECT:
04255   case WM_IME_COMPOSITIONFULL:
04256     return 0;
04257   case WM_IME_STARTCOMPOSITION:
04258   {
04259     editor->imeStartIndex=ME_GetCursorOfs(&editor->pCursors[0]);
04260     ME_DeleteSelection(editor);
04261     ME_CommitUndo(editor);
04262     ME_UpdateRepaint(editor, FALSE);
04263     return 0;
04264   }
04265   case WM_IME_COMPOSITION:
04266   {
04267     HIMC hIMC;
04268 
04269     ME_Style *style = ME_GetInsertStyle(editor, 0);
04270     hIMC = ITextHost_TxImmGetContext(editor->texthost);
04271     ME_DeleteSelection(editor);
04272     ME_SaveTempStyle(editor);
04273     if (lParam & (GCS_RESULTSTR|GCS_COMPSTR))
04274     {
04275         LPWSTR lpCompStr = NULL;
04276         DWORD dwBufLen;
04277         DWORD dwIndex = lParam & GCS_RESULTSTR;
04278         if (!dwIndex)
04279           dwIndex = GCS_COMPSTR;
04280 
04281         dwBufLen = ImmGetCompositionStringW(hIMC, dwIndex, NULL, 0);
04282         lpCompStr = HeapAlloc(GetProcessHeap(),0,dwBufLen + sizeof(WCHAR));
04283         ImmGetCompositionStringW(hIMC, dwIndex, lpCompStr, dwBufLen);
04284         lpCompStr[dwBufLen/sizeof(WCHAR)] = 0;
04285         ME_InsertTextFromCursor(editor,0,lpCompStr,dwBufLen/sizeof(WCHAR),style);
04286         HeapFree(GetProcessHeap(), 0, lpCompStr);
04287 
04288         if (dwIndex == GCS_COMPSTR)
04289           ME_SetSelection(editor,editor->imeStartIndex,
04290                           editor->imeStartIndex + dwBufLen/sizeof(WCHAR));
04291     }
04292     ME_ReleaseStyle(style);
04293     ME_CommitUndo(editor);
04294     ME_UpdateRepaint(editor, FALSE);
04295     return 0;
04296   }
04297   case WM_IME_ENDCOMPOSITION:
04298   {
04299     ME_DeleteSelection(editor);
04300     editor->imeStartIndex=-1;
04301     return 0;
04302   }
04303   case EM_GETOLEINTERFACE:
04304   {
04305     LPVOID *ppvObj = (LPVOID*) lParam;
04306     return CreateIRichEditOle(editor, ppvObj);
04307   }
04308   case EM_GETPASSWORDCHAR:
04309   {
04310     return editor->cPasswordMask;
04311   }
04312   case EM_SETOLECALLBACK:
04313     if(editor->lpOleCallback)
04314       IUnknown_Release(editor->lpOleCallback);
04315     editor->lpOleCallback = (LPRICHEDITOLECALLBACK)lParam;
04316     if(editor->lpOleCallback)
04317       IUnknown_AddRef(editor->lpOleCallback);
04318     return TRUE;
04319   case EM_GETWORDBREAKPROC:
04320     return (LRESULT)editor->pfnWordBreak;
04321   case EM_SETWORDBREAKPROC:
04322   {
04323     EDITWORDBREAKPROCW pfnOld = editor->pfnWordBreak;
04324 
04325     editor->pfnWordBreak = (EDITWORDBREAKPROCW)lParam;
04326     return (LRESULT)pfnOld;
04327   }
04328   case EM_GETTEXTMODE:
04329     return editor->mode;
04330   case EM_SETTEXTMODE:
04331   {
04332     int mask = 0;
04333     int changes = 0;
04334 
04335     if (ME_GetTextLength(editor) || editor->pUndoStack || editor->pRedoStack)
04336       return E_UNEXPECTED;
04337 
04338     /* Check for mutually exclusive flags in adjacent bits of wParam */
04339     if ((wParam & (TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE)) &
04340         (wParam & (TM_PLAINTEXT | TM_SINGLELEVELUNDO | TM_SINGLECODEPAGE)) << 1)
04341       return E_INVALIDARG;
04342 
04343     if (wParam & (TM_RICHTEXT | TM_PLAINTEXT))
04344     {
04345       mask |= TM_RICHTEXT | TM_PLAINTEXT;
04346       changes |= wParam & (TM_RICHTEXT | TM_PLAINTEXT);
04347       if (wParam & TM_PLAINTEXT) {
04348         /* Clear selection since it should be possible to select the
04349          * end of text run for rich text */
04350         ME_InvalidateSelection(editor);
04351         ME_SetCursorToStart(editor, &editor->pCursors[0]);
04352         editor->pCursors[1] = editor->pCursors[0];
04353         /* plain text can only have the default style. */
04354         ME_ClearTempStyle(editor);
04355         ME_AddRefStyle(editor->pBuffer->pDefaultStyle);
04356         ME_ReleaseStyle(editor->pCursors[0].pRun->member.run.style);
04357         editor->pCursors[0].pRun->member.run.style = editor->pBuffer->pDefaultStyle;
04358       }
04359     }
04360     /* FIXME: Currently no support for undo level and code page options */
04361     editor->mode = (editor->mode & ~mask) | changes;
04362     return 0;
04363   }
04364   case EM_SETPASSWORDCHAR:
04365   {
04366     editor->cPasswordMask = wParam;
04367     ME_RewrapRepaint(editor);
04368     return 0;
04369   }
04370   case EM_SETTARGETDEVICE:
04371     if (wParam == 0)
04372     {
04373       BOOL new = (lParam == 0 && (editor->styleFlags & ES_MULTILINE));
04374       if (editor->nAvailWidth || editor->bWordWrap != new)
04375       {
04376         editor->bWordWrap = new;
04377         editor->nAvailWidth = 0; /* wrap to client area */
04378         ME_RewrapRepaint(editor);
04379       }
04380     } else {
04381       int width = max(0, lParam);
04382       if ((editor->styleFlags & ES_MULTILINE) &&
04383           (!editor->bWordWrap || editor->nAvailWidth != width))
04384       {
04385         editor->nAvailWidth = width;
04386         editor->bWordWrap = TRUE;
04387         ME_RewrapRepaint(editor);
04388       }
04389       FIXME("EM_SETTARGETDEVICE doesn't use non-NULL target devices\n");
04390     }
04391     return TRUE;
04392   default:
04393   do_default:
04394     *phresult = S_FALSE;
04395     break;
04396   }
04397   return 0L;
04398 }
04399 
04400 static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
04401                                       LPARAM lParam, BOOL unicode)
04402 {
04403   ME_TextEditor *editor;
04404   HRESULT hresult;
04405   LRESULT lresult = 0;
04406 
04407   TRACE("enter hwnd %p msg %04x (%s) %lx %lx, unicode %d\n",
04408         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode);
04409 
04410   editor = (ME_TextEditor *)GetWindowLongPtrW(hWnd, 0);
04411   if (!editor)
04412   {
04413     if (msg == WM_NCCREATE)
04414     {
04415       CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
04416       ITextHost *texthost;
04417 
04418       TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
04419       texthost = ME_CreateTextHost(hWnd, pcs, FALSE);
04420       return texthost != NULL;
04421     }
04422     else
04423     {
04424       return DefWindowProcW(hWnd, msg, wParam, lParam);
04425     }
04426   }
04427 
04428   switch (msg)
04429   {
04430     case WM_PAINT:
04431     {
04432       HDC hDC;
04433       RECT rc;
04434       PAINTSTRUCT ps;
04435 
04436       hDC = BeginPaint(editor->hWnd, &ps);
04437       if (!editor->bEmulateVersion10 || (editor->nEventMask & ENM_UPDATE))
04438         ME_SendOldNotify(editor, EN_UPDATE);
04439       /* Erase area outside of the formatting rectangle */
04440       if (ps.rcPaint.top < editor->rcFormat.top)
04441       {
04442         rc = ps.rcPaint;
04443         rc.bottom = editor->rcFormat.top;
04444         FillRect(hDC, &rc, editor->hbrBackground);
04445         ps.rcPaint.top = editor->rcFormat.top;
04446       }
04447       if (ps.rcPaint.bottom > editor->rcFormat.bottom) {
04448         rc = ps.rcPaint;
04449         rc.top = editor->rcFormat.bottom;
04450         FillRect(hDC, &rc, editor->hbrBackground);
04451         ps.rcPaint.bottom = editor->rcFormat.bottom;
04452       }
04453       if (ps.rcPaint.left < editor->rcFormat.left) {
04454         rc = ps.rcPaint;
04455         rc.right = editor->rcFormat.left;
04456         FillRect(hDC, &rc, editor->hbrBackground);
04457         ps.rcPaint.left = editor->rcFormat.left;
04458       }
04459       if (ps.rcPaint.right > editor->rcFormat.right) {
04460         rc = ps.rcPaint;
04461         rc.left = editor->rcFormat.right;
04462         FillRect(hDC, &rc, editor->hbrBackground);
04463         ps.rcPaint.right = editor->rcFormat.right;
04464       }
04465 
04466       ME_PaintContent(editor, hDC, &ps.rcPaint);
04467       EndPaint(editor->hWnd, &ps);
04468       return 0;
04469     }
04470     case WM_ERASEBKGND:
04471     {
04472       HDC hDC = (HDC)wParam;
04473       RECT rc;
04474 
04475       if (GetUpdateRect(editor->hWnd, &rc, TRUE))
04476         FillRect(hDC, &rc, editor->hbrBackground);
04477       return 1;
04478     }
04479     case EM_SETOPTIONS:
04480     {
04481       DWORD dwStyle;
04482       const DWORD mask = ECO_VERTICAL | ECO_AUTOHSCROLL | ECO_AUTOVSCROLL |
04483                          ECO_NOHIDESEL | ECO_READONLY | ECO_WANTRETURN |
04484                          ECO_SELECTIONBAR;
04485       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
04486       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
04487       dwStyle = (dwStyle & ~mask) | (lresult & mask);
04488       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
04489       return lresult;
04490     }
04491     case EM_SETREADONLY:
04492     {
04493       DWORD dwStyle;
04494       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
04495       dwStyle = GetWindowLongW(hWnd, GWL_STYLE);
04496       dwStyle &= ~ES_READONLY;
04497       if (wParam)
04498         dwStyle |= ES_READONLY;
04499       SetWindowLongW(hWnd, GWL_STYLE, dwStyle);
04500       return lresult;
04501     }
04502     default:
04503       lresult = ME_HandleMessage(editor, msg, wParam, lParam, unicode, &hresult);
04504   }
04505 
04506   if (hresult == S_FALSE)
04507     lresult = DefWindowProcW(hWnd, msg, wParam, lParam);
04508 
04509   TRACE("exit hwnd %p msg %04x (%s) %lx %lx, unicode %d -> %lu\n",
04510         hWnd, msg, get_msg_name(msg), wParam, lParam, unicode, lresult);
04511 
04512   return lresult;
04513 }
04514 
04515 static LRESULT WINAPI RichEditWndProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
04516 {
04517     BOOL unicode = TRUE;
04518 
04519     /* Under Win9x RichEdit20W returns ANSI strings, see the tests. */
04520     if (msg == WM_GETTEXT && (GetVersion() & 0x80000000))
04521         unicode = FALSE;
04522 
04523     return RichEditWndProc_common(hWnd, msg, wParam, lParam, unicode);
04524 }
04525 
04526 static LRESULT WINAPI RichEditWndProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
04527 {
04528     return RichEditWndProc_common(hWnd, msg, wParam, lParam, FALSE);
04529 }
04530 
04531 /******************************************************************
04532  *        RichEditANSIWndProc (RICHED20.10)
04533  */
04534 LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
04535 {
04536     return RichEditWndProcA(hWnd, msg, wParam, lParam);
04537 }
04538 
04539 /******************************************************************
04540  *        RichEdit10ANSIWndProc (RICHED20.9)
04541  */
04542 LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
04543 {
04544   if (msg == WM_NCCREATE && !GetWindowLongPtrW(hWnd, 0))
04545   {
04546     ITextHost *texthost;
04547     CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam;
04548 
04549     TRACE("WM_NCCREATE: hWnd %p style 0x%08x\n", hWnd, pcs->style);
04550     texthost = ME_CreateTextHost(hWnd, pcs, TRUE);
04551     return texthost != NULL;
04552   }
04553   return RichEditANSIWndProc(hWnd, msg, wParam, lParam);
04554 }
04555 
04556 void ME_SendOldNotify(ME_TextEditor *editor, int nCode)
04557 {
04558   ITextHost_TxNotify(editor->texthost, nCode, NULL);
04559 }
04560 
04561 /* Fill buffer with srcChars unicode characters from the start cursor.
04562  *
04563  * buffer: destination buffer
04564  * buflen: length of buffer in characters excluding the NULL terminator.
04565  * start: start of editor text to copy into buffer.
04566  * srcChars: Number of characters to use from the editor text.
04567  * bCRLF: if true, replaces all end of lines with \r\n pairs.
04568  *
04569  * returns the number of characters written excluding the NULL terminator.
04570  *
04571  * The written text is always NULL terminated.
04572  */
04573 int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int buflen,
04574                 const ME_Cursor *start, int srcChars, BOOL bCRLF)
04575 {
04576   ME_DisplayItem *pRun, *pNextRun;
04577   const WCHAR *pStart = buffer;
04578   const WCHAR cr_lf[] = {'\r', '\n', 0};
04579   const WCHAR *str;
04580   int nLen;
04581 
04582   /* bCRLF flag is only honored in 2.0 and up. 1.0 must always return text verbatim */
04583   if (editor->bEmulateVersion10) bCRLF = 0;
04584 
04585   pRun = start->pRun;
04586   assert(pRun);
04587   pNextRun = ME_FindItemFwd(pRun, diRun);
04588 
04589   nLen = pRun->member.run.strText->nLen - start->nOffset;
04590   str = pRun->member.run.strText->szData + start->nOffset;
04591 
04592   /* No '\r' is appended to the last paragraph. */
04593   while (srcChars && buflen && pNextRun)
04594   {
04595     int nFlags = pRun->member.run.nFlags;
04596 
04597     if (bCRLF && nFlags & MERF_ENDPARA && ~nFlags & MERF_ENDCELL)
04598     {
04599       if (buflen == 1) break;
04600       /* FIXME: native fails to reduce srcChars here for WM_GETTEXT or
04601        *        EM_GETTEXTEX, however, this is done for copying text which
04602        *        also uses this function. */
04603       srcChars -= min(nLen, srcChars);
04604       nLen = 2;
04605       str = cr_lf;
04606     } else {
04607       nLen = min(nLen, srcChars);
04608       srcChars -= nLen;
04609     }
04610 
04611     nLen = min(nLen, buflen);
04612     buflen -= nLen;
04613 
04614     CopyMemory(buffer, str, sizeof(WCHAR) * nLen);
04615 
04616     buffer += nLen;
04617 
04618     pRun = pNextRun;
04619     pNextRun = ME_FindItemFwd(pRun, diRun);
04620 
04621     nLen = pRun->member.run.strText->nLen;
04622     str = pRun->member.run.strText->szData;
04623   }
04624   *buffer = 0;
04625   return buffer - pStart;
04626 }
04627 
04628 static BOOL ME_RegisterEditorClass(HINSTANCE hInstance)
04629 {
04630   WNDCLASSW wcW;
04631   WNDCLASSA wcA;
04632   
04633   wcW.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
04634   wcW.lpfnWndProc = RichEditWndProcW;
04635   wcW.cbClsExtra = 0;
04636   wcW.cbWndExtra = sizeof(ME_TextEditor *);
04637   wcW.hInstance = NULL; /* hInstance would register DLL-local class */
04638   wcW.hIcon = NULL;
04639   wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
04640   wcW.hbrBackground = GetStockObject(NULL_BRUSH);
04641   wcW.lpszMenuName = NULL;
04642 
04643   if (is_version_nt())
04644   {
04645     wcW.lpszClassName = RICHEDIT_CLASS20W;
04646     if (!RegisterClassW(&wcW)) return FALSE;
04647     wcW.lpszClassName = MSFTEDIT_CLASS;
04648     if (!RegisterClassW(&wcW)) return FALSE;
04649   }
04650   else
04651   {
04652     /* WNDCLASSA/W have the same layout */
04653     wcW.lpszClassName = (LPCWSTR)"RichEdit20W";
04654     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
04655     wcW.lpszClassName = (LPCWSTR)"RichEdit50W";
04656     if (!RegisterClassA((WNDCLASSA *)&wcW)) return FALSE;
04657   }
04658 
04659   wcA.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
04660   wcA.lpfnWndProc = RichEditWndProcA;
04661   wcA.cbClsExtra = 0;
04662   wcA.cbWndExtra = sizeof(ME_TextEditor *);
04663   wcA.hInstance = NULL; /* hInstance would register DLL-local class */
04664   wcA.hIcon = NULL;
04665   wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM));
04666   wcA.hbrBackground = GetStockObject(NULL_BRUSH);
04667   wcA.lpszMenuName = NULL;
04668   wcA.lpszClassName = RICHEDIT_CLASS20A;
04669   if (!RegisterClassA(&wcA)) return FALSE;
04670   wcA.lpszClassName = "RichEdit50A";
04671   if (!RegisterClassA(&wcA)) return FALSE;
04672 
04673   return TRUE;
04674 }
04675 
04676 static LRESULT WINAPI REComboWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
04677   /* FIXME: Not implemented */
04678   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
04679         hWnd, msg, get_msg_name(msg), wParam, lParam);
04680   return DefWindowProcW(hWnd, msg, wParam, lParam);
04681 }
04682 
04683 static LRESULT WINAPI REListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
04684   /* FIXME: Not implemented */
04685   TRACE("hWnd %p msg %04x (%s) %08lx %08lx\n",
04686         hWnd, msg, get_msg_name(msg), wParam, lParam);
04687   return DefWindowProcW(hWnd, msg, wParam, lParam);
04688 }
04689 
04690 /******************************************************************
04691  *        REExtendedRegisterClass (RICHED20.8)
04692  *
04693  * FIXME undocumented
04694  * Need to check for errors and implement controls and callbacks 
04695  */
04696 LRESULT WINAPI REExtendedRegisterClass(void)
04697 {
04698   WNDCLASSW wcW;
04699   UINT result;
04700 
04701   FIXME("semi stub\n");
04702 
04703   wcW.cbClsExtra = 0;
04704   wcW.cbWndExtra = 4;
04705   wcW.hInstance = NULL;
04706   wcW.hIcon = NULL;
04707   wcW.hCursor = NULL;
04708   wcW.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
04709   wcW.lpszMenuName = NULL;
04710 
04711   if (!ME_ListBoxRegistered)
04712   {
04713       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS;
04714       wcW.lpfnWndProc = REListWndProc;
04715       wcW.lpszClassName = REListBox20W;
04716       if (RegisterClassW(&wcW)) ME_ListBoxRegistered = TRUE;
04717   }
04718 
04719   if (!ME_ComboBoxRegistered)
04720   {
04721       wcW.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
04722       wcW.lpfnWndProc = REComboWndProc;
04723       wcW.lpszClassName = REComboBox20W;
04724       if (RegisterClassW(&wcW)) ME_ComboBoxRegistered = TRUE;  
04725   }
04726 
04727   result = 0;
04728   if (ME_ListBoxRegistered)
04729       result += 1;
04730   if (ME_ComboBoxRegistered)
04731       result += 2;
04732 
04733   return result;
04734 }
04735 
04736 static BOOL isurlspecial(WCHAR c)
04737 {
04738   static const WCHAR special_chars[] = {'.','/','%','@','*','|','\\','+','#',0};
04739   return strchrW( special_chars, c ) != NULL;
04740 }
04741 
04749 static BOOL ME_FindNextURLCandidate(ME_TextEditor *editor,
04750                                     const ME_Cursor *start,
04751                                     int nChars,
04752                                     ME_Cursor *candidate_min,
04753                                     ME_Cursor *candidate_max)
04754 {
04755   ME_Cursor cursor = *start;
04756   BOOL foundColon = FALSE;
04757   BOOL candidateStarted = FALSE;
04758   WCHAR lastAcceptedChar = '\0';
04759 
04760   while (nChars > 0)
04761   {
04762     WCHAR *strStart = cursor.pRun->member.run.strText->szData;
04763     WCHAR *str = strStart + cursor.nOffset;
04764     int nLen = cursor.pRun->member.run.strText->nLen - cursor.nOffset;
04765     nChars -= nLen;
04766 
04767     if (~cursor.pRun->member.run.nFlags & MERF_ENDPARA)
04768     {
04769       /* Find start of candidate */
04770       if (!candidateStarted)
04771       {
04772         while (nLen)
04773         {
04774           nLen--;
04775           if (isalnumW(*str) || isurlspecial(*str))
04776           {
04777             cursor.nOffset = str - strStart;
04778             *candidate_min = cursor;
04779             candidateStarted = TRUE;
04780             lastAcceptedChar = *str++;
04781             break;
04782           }
04783           str++;
04784         }
04785       }
04786 
04787       /* Find end of candidate */
04788       if (candidateStarted) {
04789         while (nLen)
04790         {
04791           nLen--;
04792           if (*str == ':' && !foundColon) {
04793             foundColon = TRUE;
04794           } else if (!isalnumW(*str) && !isurlspecial(*str)) {
04795             cursor.nOffset = str - strStart;
04796             if (lastAcceptedChar == ':')
04797               ME_MoveCursorChars(editor, &cursor, -1);
04798             *candidate_max = cursor;
04799             return TRUE;
04800           }
04801           lastAcceptedChar = *str++;
04802         }
04803       }
04804     } else {
04805       /* End of paragraph: skip it if before candidate span, or terminates
04806          current active span */
04807       if (candidateStarted) {
04808         if (lastAcceptedChar == ':')
04809           ME_MoveCursorChars(editor, &cursor, -1);
04810         *candidate_max = cursor;
04811         return TRUE;
04812       }
04813     }
04814 
04815     /* Reaching this point means no span was found, so get next span */
04816     if (!ME_NextRun(&cursor.pPara, &cursor.pRun)) {
04817       if (candidateStarted) {
04818         /* There are no further runs, so take end of text as end of candidate */
04819         cursor.nOffset = str - strStart;
04820         if (lastAcceptedChar == ':')
04821           ME_MoveCursorChars(editor, &cursor, -1);
04822         *candidate_max = cursor;
04823         return TRUE;
04824       }
04825       *candidate_max = *candidate_min = cursor;
04826       return FALSE;
04827     }
04828     cursor.nOffset = 0;
04829   }
04830 
04831   if (candidateStarted) {
04832     /* There are no further runs, so take end of text as end of candidate */
04833     if (lastAcceptedChar == ':')
04834       ME_MoveCursorChars(editor, &cursor, -1);
04835     *candidate_max = cursor;
04836     return TRUE;
04837   }
04838   *candidate_max = *candidate_min = cursor;
04839   return FALSE;
04840 }
04841 
04845 static BOOL ME_IsCandidateAnURL(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
04846 {
04847 #define MAX_PREFIX_LEN 9
04848   struct prefix_s {
04849     const WCHAR text[MAX_PREFIX_LEN];
04850     int length;
04851   }prefixes[] = {
04852     {{'p','r','o','s','p','e','r','o',':'}, 9},
04853     {{'t','e','l','n','e','t',':'}, 7},
04854     {{'g','o','p','h','e','r',':'}, 7},
04855     {{'m','a','i','l','t','o',':'}, 7},
04856     {{'h','t','t','p','s',':'}, 6},
04857     {{'f','i','l','e',':'}, 5},
04858     {{'n','e','w','s',':'}, 5},
04859     {{'w','a','i','s',':'}, 5},
04860     {{'n','n','t','p',':'}, 5},
04861     {{'h','t','t','p',':'}, 5},
04862     {{'w','w','w','.'}, 4},
04863     {{'f','t','p',':'}, 4},
04864   };
04865   WCHAR bufferW[MAX_PREFIX_LEN + 1];
04866   unsigned int i;
04867 
04868   ME_GetTextW(editor, bufferW, MAX_PREFIX_LEN, start, nChars, 0);
04869   for (i = 0; i < sizeof(prefixes) / sizeof(*prefixes); i++)
04870   {
04871     if (nChars < prefixes[i].length) continue;
04872     if (!memcmp(prefixes[i].text, bufferW, prefixes[i].length * sizeof(WCHAR)))
04873       return TRUE;
04874   }
04875   return FALSE;
04876 #undef MAX_PREFIX_LEN
04877 }
04878 
04892 static BOOL ME_UpdateLinkAttribute(ME_TextEditor *editor, ME_Cursor *start, int nChars)
04893 {
04894   BOOL modified = FALSE;
04895   ME_Cursor startCur = *start;
04896 
04897   if (!editor->AutoURLDetect_bEnable) return FALSE;
04898 
04899   do
04900   {
04901     CHARFORMAT2W link;
04902     ME_Cursor candidateStart, candidateEnd;
04903 
04904     if (ME_FindNextURLCandidate(editor, &startCur, nChars,
04905                                 &candidateStart, &candidateEnd))
04906     {
04907       /* Section before candidate is not an URL */
04908       int cMin = ME_GetCursorOfs(&candidateStart);
04909       int cMax = ME_GetCursorOfs(&candidateEnd);
04910 
04911       if (!ME_IsCandidateAnURL(editor, &candidateStart, cMax - cMin))
04912         candidateStart = candidateEnd;
04913       nChars -= cMax - ME_GetCursorOfs(&startCur);
04914     }
04915     else
04916     {
04917       /* No more candidates until end of selection */
04918       nChars = 0;
04919     }
04920 
04921     if (startCur.pRun != candidateStart.pRun ||
04922         startCur.nOffset != candidateStart.nOffset)
04923     {
04924       /* CFE_LINK effect should be consistently unset */
04925       link.cbSize = sizeof(link);
04926       ME_GetCharFormat(editor, &startCur, &candidateStart, &link);
04927       if (!(link.dwMask & CFM_LINK) || (link.dwEffects & CFE_LINK))
04928       {
04929         /* CFE_LINK must be unset from this range */
04930         memset(&link, 0, sizeof(CHARFORMAT2W));
04931         link.cbSize = sizeof(link);
04932         link.dwMask = CFM_LINK;
04933         link.dwEffects = 0;
04934         ME_SetCharFormat(editor, &startCur, &candidateStart, &link);
04935         /* Update candidateEnd since setting character formats may split
04936          * runs, which can cause a cursor to be at an invalid offset within
04937          * a split run. */
04938         while (candidateEnd.nOffset >= candidateEnd.pRun->member.run.strText->nLen)
04939         {
04940           candidateEnd.nOffset -= candidateEnd.pRun->member.run.strText->nLen;
04941           candidateEnd.pRun = ME_FindItemFwd(candidateEnd.pRun, diRun);
04942         }
04943         modified = TRUE;
04944       }
04945     }
04946     if (candidateStart.pRun != candidateEnd.pRun ||
04947         candidateStart.nOffset != candidateEnd.nOffset)
04948     {
04949       /* CFE_LINK effect should be consistently set */
04950       link.cbSize = sizeof(link);
04951       ME_GetCharFormat(editor, &candidateStart, &candidateEnd, &link);
04952       if (!(link.dwMask & CFM_LINK) || !(link.dwEffects & CFE_LINK))
04953       {
04954         /* CFE_LINK must be set on this range */
04955         memset(&link, 0, sizeof(CHARFORMAT2W));
04956         link.cbSize = sizeof(link);
04957         link.dwMask = CFM_LINK;
04958         link.dwEffects = CFE_LINK;
04959         ME_SetCharFormat(editor, &candidateStart, &candidateEnd, &link);
04960         modified = TRUE;
04961       }
04962     }
04963     startCur = candidateEnd;
04964   } while (nChars > 0);
04965   return modified;
04966 }

Generated on Sun May 27 2012 04:24:52 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.