Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

edit.cpp

Go to the documentation of this file.
00001 /* $Id: edit.cpp,v 1.44 2001/07/09 18:39:28 sandervl Exp $ */
00002 /*
00003  *      Edit control
00004  *
00005  *      Copyright  David W. Metcalfe, 1994
00006  *      Copyright  William Magro, 1995, 1996
00007  *      Copyright  Frans van Dorsselaer, 1996, 1997
00008  *
00009  *      Copyright  1999 Christoph Bratschi
00010  *
00011  * Corel version: 20000513
00012  * (WINE version: 991212)
00013  *
00014  * Status:  complete
00015  * Version: 5.00
00016  */
00017 
00018 /* CB: todo
00019   - EN_ALIGN_LTR_EC, EN_ALIGN_RTL_EC -> undocumented notifications
00020   - text alignment for single and multi line (ES_LEFT, ES_RIGHT, ES_CENTER)
00021     new in Win98, Win2k: for single line too
00022   - WinNT/Win2k: higher size limits (single: 0x7FFFFFFE, multi: none)
00023     SvL: Limits removed. EM_SETTEXTLIMIT has no effect in NT4, SP6 (EM_GETTEXTLIMIT
00024          only returns that value); limits are simply ignored, no EN_MAXTEXT is ever sent)
00025   - too many redraws and recalculations!
00026 */
00027 
00028 #include <stdlib.h>
00029 #include <os2win.h>
00030 #include <string.h>
00031 #include "controls.h"
00032 #include "combo.h"
00033 
00034 #define DBG_LOCALLOG    DBG_edit
00035 #include "dbglocal.h"
00036 
00037 #ifdef DEBUG
00038 char *GetMsgText(int Msg);
00039 #endif
00040 
00041 #define BUFLIMIT_MULTI          65534   /* maximum buffer size (not including '\0')
00042                                            FIXME: BTW, Win95 specs say 65535 (do you dare ???) */
00043 #define BUFLIMIT_SINGLE         32766   /* maximum buffer size (not including '\0') */
00044 
00045 #ifdef __WIN32OS2__
00046 #define BUFLIMIT_SINGLE_NT      0x7FFFFFFF
00047 #endif
00048 //#define BUFLIMIT_MULTI  0xFFFFFFFE
00049 //#define BUFLIMIT_SINGLE 0x7FFFFFFF
00050 
00051 #define BUFSTART_MULTI          1024    /* starting size */
00052 #define BUFSTART_SINGLE         256     /* starting size */
00053 #define GROWLENGTH              64      /* buffers grow by this much */
00054 #define HSCROLL_FRACTION        3       /* scroll window by 1/3 width */
00055 
00056 /*
00057  *      extra flags for EDITSTATE.flags field
00058  */
00059 #define EF_MODIFIED             0x0001  /* text has been modified */
00060 #define EF_FOCUSED              0x0002  /* we have input focus */
00061 #define EF_UPDATE               0x0004  /* notify parent of changed state on next WM_PAINT */
00062 #define EF_VSCROLL_TRACK        0x0008  /* don't SetScrollPos() since we are tracking the thumb */
00063 #define EF_HSCROLL_TRACK        0x0010  /* don't SetScrollPos() since we are tracking the thumb */
00064 #define EF_VSCROLL_HACK         0x0020  /* we already have informed the user of the hacked handler */
00065 #define EF_HSCROLL_HACK         0x0040  /* we already have informed the user of the hacked handler */
00066 #define EF_AFTER_WRAP           0x0080  /* the caret is displayed after the last character of a
00067                                            wrapped line, instead of in front of the next character */
00068 #define EF_USE_SOFTBRK          0x0100  /* Enable soft breaks in text. */
00069 
00070 typedef enum
00071 {
00072         END_0 = 0,      /* line ends with terminating '\0' character */
00073         END_WRAP,       /* line is wrapped */
00074         END_HARD,       /* line ends with a hard return '\r\n' */
00075         END_SOFT        /* line ends with a soft return '\r\r\n' */
00076 } LINE_END;
00077 
00078 typedef struct tagLINEDEF {
00079         INT length;             /* bruto length of a line in bytes */
00080         INT net_length; /* netto length of a line in visible characters */
00081         LINE_END ending;
00082         INT width;              /* width of the line in pixels */
00083         struct tagLINEDEF *next;
00084 } LINEDEF;
00085 
00086 typedef struct
00087 {
00088         HANDLE heap;                    /* our own heap */
00089         LPSTR text;                     /* the actual contents of the control */
00090         INT buffer_size;                /* the size of the buffer */
00091         INT buffer_limit;               /* the maximum size to which the buffer may grow */
00092         HFONT font;                     /* NULL means standard system font */
00093         INT x_offset;                   /* scroll offset        for multi lines this is in pixels
00094                                                                 for single lines it's in characters */
00095         INT line_height;                /* height of a screen line in pixels */
00096         INT char_width;         /* average character width in pixels */
00097         DWORD style;                    /* sane version of wnd->dwStyle */
00098         WORD flags;                     /* flags that are not in es->style or wnd->flags (EF_XXX) */
00099         INT undo_insert_count;  /* number of characters inserted in sequence */
00100         INT undo_position;              /* character index of the insertion and deletion */
00101         LPSTR undo_text;                /* deleted text */
00102         INT undo_buffer_size;           /* size of the deleted text buffer */
00103         INT selection_start;            /* == selection_end if no selection */
00104         INT selection_end;              /* == current caret position */
00105         CHAR password_char;             /* == 0 if no password char, and for multi line controls */
00106         INT left_margin;                /* in pixels */
00107         INT right_margin;               /* in pixels */
00108         RECT format_rect;
00109         INT region_posx;                /* Position of cursor relative to region: */
00110         INT region_posy;                /* -1: to left, 0: within, 1: to right */
00111         EDITWORDBREAKPROCA word_break_procA;
00112         INT line_count;         /* number of lines */
00113         INT y_offset;                   /* scroll offset in number of lines */
00114         BOOL bCaptureState; /* flag indicating whether mouse was captured */
00115         BOOL bEnableState;             /* flag keeping the enable state */
00116         HWND hwndListBox;              /* handle of ComboBox's listbox or NULL */
00117         /*
00118          *      only for multi line controls
00119          */
00120         INT lock_count;         /* amount of re-entries in the EditWndProc */
00121         INT tabs_count;
00122         LPINT tabs;
00123         INT text_width;         /* width of the widest line in pixels */
00124         LINEDEF *first_line_def;        /* linked list of (soft) linebreaks */
00125         HLOCAL hloc;          /* for controls receiving EM_GETHANDLE */
00126 } EDITSTATE;
00127 
00128 
00129 #define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
00130 #define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
00131 
00132 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
00133 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
00134 
00135 /* used for disabled or read-only edit control */
00136 #define EDIT_SEND_CTLCOLORSTATIC(hwnd,hdc) \
00137         (SendMessageA(GetParent(hwnd), WM_CTLCOLORSTATIC, \
00138                         (WPARAM)(hdc), (LPARAM)hwnd))
00139 #define EDIT_SEND_CTLCOLOR(hwnd,hdc) \
00140         (SendMessageA(GetParent(hwnd), WM_CTLCOLOREDIT, \
00141                         (WPARAM)(hdc), (LPARAM)hwnd))
00142 #define EDIT_NOTIFY_PARENT(hwnd, wNotifyCode) \
00143         (SendMessageA(GetParent(hwnd), WM_COMMAND, \
00144                      MAKEWPARAM(GetWindowLongA(hwnd,GWL_ID), wNotifyCode), (LPARAM)hwnd))
00145 
00146 /*********************************************************************
00147  *
00148  *      Declarations
00149  *
00150  */
00151 
00152 /*
00153  *      These functions have trivial implementations
00154  *      We still like to call them internally
00155  *      "static inline" makes them more like macro's
00156  */
00157 static inline BOOL      EDIT_EM_CanUndo(HWND hwnd, EDITSTATE *es);
00158 static inline void      EDIT_EM_EmptyUndoBuffer(HWND hwnd, EDITSTATE *es);
00159 static inline void      EDIT_WM_Clear(HWND hwnd, EDITSTATE *es);
00160 static inline void      EDIT_WM_Cut(HWND hwnd, EDITSTATE *es);
00161 
00162 /*
00163  *      Helper functions only valid for one type of control
00164  */
00165 static void     EDIT_BuildLineDefs_ML(HWND hwnd,EDITSTATE *es);
00166 static LPSTR    EDIT_GetPasswordPointer_SL(HWND hwnd, EDITSTATE *es);
00167 static void     EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
00168 static void     EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
00169 static void     EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
00170 static void     EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
00171 static VOID     EDIT_UpdateScrollBars(HWND hwnd,EDITSTATE *es,BOOL updateHorz,BOOL updateVert);
00172 /*
00173  *      Helper functions valid for both single line _and_ multi line controls
00174  */
00175 static INT      EDIT_CallWordBreakProc(HWND hwnd, EDITSTATE *es, INT start, INT index, INT count, INT action);
00176 static INT      EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
00177 static void     EDIT_ConfinePoint(HWND hwnd, EDITSTATE *es, LPINT x, LPINT y);
00178 static void     EDIT_GetLineRect(HWND hwnd,HDC dc,EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
00179 static void     EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end);
00180 static void     EDIT_LockBuffer(HWND hwnd, EDITSTATE *es);
00181 static BOOL     EDIT_MakeFit(HWND hwnd, EDITSTATE *es, INT size);
00182 static BOOL     EDIT_MakeUndoFit(HWND hwnd, EDITSTATE *es, INT size);
00183 static void     EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
00184 static void     EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend);
00185 static void     EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend);
00186 static void     EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend);
00187 static void     EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
00188 static void     EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend);
00189 static void     EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
00190 static VOID     EDIT_PaintText(HWND hwnd, EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
00191 static void     EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos, BOOL after_wrap);
00192 static void     EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT lprc);
00193 static void     EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force);
00194 static INT      EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action);
00195 static VOID     EDIT_Draw(HWND hwnd,EDITSTATE *es,HDC hdc);
00196 static VOID     EDIT_Refresh(HWND hwnd,EDITSTATE *es,BOOL useCache);
00197 
00198 /*
00199  *      EM_XXX message handlers
00200  */
00201 static LRESULT  EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y);
00202 static BOOL     EDIT_EM_FmtLines(HWND hwnd, EDITSTATE *es, BOOL add_eol);
00203 static INT      EDIT_EM_GetFirstVisibleLine(HWND hwnd,EDITSTATE *es);
00204 static HLOCAL   EDIT_EM_GetHandle(HWND hwnd, EDITSTATE *es);
00205 static INT      EDIT_EM_GetLimitText(HWND hwnd,EDITSTATE *es);
00206 static INT      EDIT_EM_GetLine(HWND hwnd, EDITSTATE *es, INT line, LPSTR lpch);
00207 static INT      EDIT_EM_GetLineCount(HWND hwnd,EDITSTATE *es);
00208 static LONG     EDIT_EM_GetMargins(HWND hwnd,EDITSTATE *es);
00209 static BOOL     EDIT_EM_GetModify(HWND hwnd,EDITSTATE *es);
00210 static CHAR     EDIT_EM_GetPasswordChar(HWND hwnd,EDITSTATE *es);
00211 static VOID     EDIT_EM_GetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc);
00212 static LRESULT  EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end);
00213 static LRESULT  EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es);
00214 static PVOID    EDIT_EM_GetWordbreakProc(HWND hwnd,EDITSTATE *es);
00215 static INT      EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index);
00216 static INT      EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line);
00217 static INT      EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index);
00218 static BOOL     EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy);
00219 static LRESULT  EDIT_EM_PosFromChar(HWND hwnd,HDC dc, EDITSTATE *es, INT index, BOOL after_wrap);
00220 static void     EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace);
00221 static LRESULT  EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action);
00222 static void     EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es);
00223 static void     EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc);
00224 static void     EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit);
00225 static void     EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action, INT left, INT right);
00226 static void     EDIT_EM_SetModify(HWND hwnd,EDITSTATE *es,BOOL fModified);
00227 static void     EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c);
00228 static BOOL     EDIT_EM_SetReadOnly(HWND hwnd,EDITSTATE *es,BOOL fReadOnly);
00229 static void     EDIT_EM_SetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc);
00230 static void     EDIT_EM_SetRectNP(HWND hwnd,EDITSTATE *es,LPRECT lprc);
00231 static void     EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
00232 static BOOL     EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs);
00233 static void     EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp);
00234 static BOOL     EDIT_EM_Undo(HWND hwnd, EDITSTATE *es);
00235 static LRESULT  EDIT_EM_SetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
00236 static LRESULT  EDIT_EM_GetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
00237 /*
00238  *      WM_XXX message handlers
00239  */
00240 static void     EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data);
00241 static void     EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
00242 static void     EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y);
00243 static void     EDIT_WM_Copy(HWND hwnd, EDITSTATE *es);
00244 static LRESULT  EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs);
00245 static void     EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es);
00246 static LRESULT  EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc);
00247 static INT      EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text);
00248 static LRESULT  EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
00249 static LRESULT  EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data);
00250 static LRESULT  EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus);
00251 static LRESULT  EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
00252 static LRESULT  EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
00253 static LRESULT  EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
00254 static LRESULT  EDIT_WM_CaptureChanged(HWND hwnd,EDITSTATE *es);
00255 static LRESULT  EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
00256 static LRESULT  EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs);
00257 static void     EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam);
00258 static void     EDIT_WM_Paste(HWND hwnd, EDITSTATE *es);
00259 static void     EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus);
00260 static void     EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw);
00261 static void     EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text);
00262 static void     EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height);
00263 static LRESULT  EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data);
00264 static void     EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc);
00265 static LRESULT  EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
00266 static LRESULT EDIT_WM_MouseWheel(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
00267 
00268 /*********************************************************************
00269  *
00270  *      EM_CANUNDO
00271  *
00272  */
00273 static inline BOOL EDIT_EM_CanUndo(HWND hwnd, EDITSTATE *es)
00274 {
00275         return (es->undo_insert_count || lstrlenA(es->undo_text));
00276 }
00277 
00278 
00279 /*********************************************************************
00280  *
00281  *      EM_EMPTYUNDOBUFFER
00282  *
00283  */
00284 static inline void EDIT_EM_EmptyUndoBuffer(HWND hwnd, EDITSTATE *es)
00285 {
00286         es->undo_insert_count = 0;
00287         *es->undo_text = '\0';
00288 }
00289 
00290 
00291 /*********************************************************************
00292  *
00293  *      WM_CLEAR
00294  *
00295  */
00296 static inline void EDIT_WM_Clear(HWND hwnd, EDITSTATE *es)
00297 {
00298         EDIT_EM_ReplaceSel(hwnd, es, TRUE, "");
00299         if (es->flags & EF_UPDATE) {
00300                 es->flags &= ~EF_UPDATE;
00301                 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
00302         }
00303 }
00304 
00305 
00306 /*********************************************************************
00307  *
00308  *      WM_CUT
00309  *
00310  */
00311 static inline void EDIT_WM_Cut(HWND hwnd, EDITSTATE *es)
00312 {
00313         EDIT_WM_Copy(hwnd, es);
00314         EDIT_WM_Clear(hwnd, es);
00315 }
00316 
00317 
00318 /*********************************************************************
00319  *
00320  *      EditWndProc()
00321  *
00322  *      The messages are in the order of the actual integer values
00323  *      (which can be found in include/windows.h)
00324  *      Whereever possible the 16 bit versions are converted to
00325  *      the 32 bit ones, so that we can 'fall through' to the
00326  *      helper functions.  These are mostly 32 bit (with a few
00327  *      exceptions, clearly indicated by a '16' extension to their
00328  *      names).
00329  *
00330  */
00331 LRESULT WINAPI EditWndProc( HWND hwnd, UINT msg,
00332                             WPARAM wParam, LPARAM lParam )
00333 {
00334         EDITSTATE *es = (EDITSTATE*)GetInfoPtr(hwnd);
00335         LRESULT result = 0;
00336 
00337 //      dprintf(("EditWndProc hwnd: %04x, msg %s, wp %08x lp %08lx\n",
00338 //               hwnd, GetMsgText(msg), wParam, lParam));
00339 
00340         switch (msg) {
00341         case WM_DESTROY:
00342                 //DPRINTF_EDIT_MSG32("WM_DESTROY");
00343                 EDIT_WM_Destroy(hwnd, es);
00344                 result = 0;
00345                 goto END;
00346 
00347         case WM_NCCREATE:
00348                 //DPRINTF_EDIT_MSG32("WM_NCCREATE");
00349                 result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTA)lParam);
00350                 goto END;
00351         }
00352 
00353         if (!es)
00354         {
00355             result = DefWindowProcA(hwnd, msg, wParam, lParam);
00356             goto END;
00357         }
00358 
00359 
00360         EDIT_LockBuffer(hwnd, es);
00361         switch (msg) {
00362         case EM_GETSEL:
00363                 //DPRINTF_EDIT_MSG32("EM_GETSEL");
00364                 result = EDIT_EM_GetSel(hwnd, es, (LPUINT)wParam, (LPUINT)lParam);
00365                 break;
00366 
00367         case EM_SETSEL:
00368                 //DPRINTF_EDIT_MSG32("EM_SETSEL");
00369                 EDIT_EM_SetSel(hwnd, es, wParam, lParam, FALSE);
00370                 EDIT_EM_ScrollCaret(hwnd,es);
00371                 result = 1;
00372                 break;
00373 
00374         case EM_GETRECT:
00375                 //DPRINTF_EDIT_MSG32("EM_GETRECT");
00376                 EDIT_EM_GetRect(hwnd,es,(LPRECT)lParam);
00377                 break;
00378 
00379         case EM_SETRECT:
00380                 //DPRINTF_EDIT_MSG32("EM_SETRECT");
00381                 EDIT_EM_SetRect(hwnd,es,(LPRECT)lParam);
00382                 break;
00383 
00384         case EM_SETRECTNP:
00385                 //DPRINTF_EDIT_MSG32("EM_SETRECTNP");
00386                 EDIT_EM_SetRectNP(hwnd,es,(LPRECT)lParam);
00387                 break;
00388 
00389         case EM_SCROLL:
00390                 //DPRINTF_EDIT_MSG32("EM_SCROLL");
00391                 result = EDIT_EM_Scroll(hwnd, es, (INT)wParam);
00392                 break;
00393 
00394         case EM_LINESCROLL:
00395                 //DPRINTF_EDIT_MSG32("EM_LINESCROLL");
00396                 result = (LRESULT)EDIT_EM_LineScroll(hwnd, es, (INT)wParam, (INT)lParam);
00397                 break;
00398 
00399         case EM_SCROLLCARET:
00400                 //DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
00401                 EDIT_EM_ScrollCaret(hwnd, es);
00402                 result = 1;
00403                 break;
00404 
00405         case EM_GETMODIFY:
00406                 //DPRINTF_EDIT_MSG32("EM_GETMODIFY");
00407                 result = (LRESULT)EDIT_EM_GetModify(hwnd,es);
00408                 break;
00409 
00410         case EM_SETMODIFY:
00411                 //DPRINTF_EDIT_MSG32("EM_SETMODIFY");
00412                 EDIT_EM_SetModify(hwnd,es,(BOOL)wParam);
00413                 break;
00414 
00415         case EM_GETLINECOUNT:
00416                 //DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
00417                 result = (LRESULT)EDIT_EM_GetLineCount(hwnd,es);
00418                 break;
00419 
00420         case EM_LINEINDEX:
00421                 //DPRINTF_EDIT_MSG32("EM_LINEINDEX");
00422                 result = (LRESULT)EDIT_EM_LineIndex(hwnd, es, (INT)wParam);
00423                 break;
00424 
00425         case EM_SETHANDLE:
00426                 //DPRINTF_EDIT_MSG32("EM_SETHANDLE");
00427                 EDIT_EM_SetHandle(hwnd, es, (HLOCAL)wParam);
00428                 break;
00429 
00430         case EM_GETHANDLE:
00431                 //DPRINTF_EDIT_MSG32("EM_GETHANDLE");
00432                 result = (LRESULT)EDIT_EM_GetHandle(hwnd, es);
00433                 break;
00434 
00435         case EM_GETTHUMB:
00436                 //DPRINTF_EDIT_MSG32("EM_GETTHUMB");
00437                 result = EDIT_EM_GetThumb(hwnd, es);
00438                 break;
00439 
00440         /* messages 0x00bf and 0x00c0 missing from specs */
00441 
00442         case WM_USER+15:
00443                 //DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
00444                 /* fall through */
00445         case 0x00bf:
00446                 //DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
00447                 result = DefWindowProcA(hwnd, msg, wParam, lParam);
00448                 break;
00449 
00450         case WM_USER+16:
00451                 //DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
00452                 /* fall through */
00453         case 0x00c0:
00454                 //DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
00455                 result = DefWindowProcA(hwnd, msg, wParam, lParam);
00456                 break;
00457 
00458         case EM_LINELENGTH:
00459                 //DPRINTF_EDIT_MSG32("EM_LINELENGTH");
00460                 result = (LRESULT)EDIT_EM_LineLength(hwnd, es, (INT)wParam);
00461                 break;
00462 
00463         case EM_REPLACESEL:
00464                 //DPRINTF_EDIT_MSG32("EM_REPLACESEL");
00465                 EDIT_EM_ReplaceSel(hwnd, es, (BOOL)wParam, (LPCSTR)lParam);
00466                 result = 1;
00467                 break;
00468 
00469         /* message 0x00c3 missing from specs */
00470 
00471         case WM_USER+19:
00472                 //DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
00473                 /* fall through */
00474         case 0x00c3:
00475                 //DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
00476                 result = DefWindowProcA(hwnd, msg, wParam, lParam);
00477                 break;
00478 
00479         case EM_GETLINE:
00480                 //DPRINTF_EDIT_MSG32("EM_GETLINE");
00481                 result = (LRESULT)EDIT_EM_GetLine(hwnd, es, (INT)wParam, (LPSTR)lParam);
00482                 break;
00483 
00484         case EM_SETLIMITTEXT:
00485                 //DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
00486                 EDIT_EM_SetLimitText(hwnd, es, (INT)wParam);
00487                 break;
00488 
00489         case EM_CANUNDO:
00490                 //DPRINTF_EDIT_MSG32("EM_CANUNDO");
00491                 result = (LRESULT)EDIT_EM_CanUndo(hwnd, es);
00492                 break;
00493 
00494         case EM_UNDO:
00495                 /* fall through */
00496         case WM_UNDO:
00497                 //DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
00498                 result = (LRESULT)EDIT_EM_Undo(hwnd, es);
00499                 break;
00500 
00501         case EM_FMTLINES:
00502                 //DPRINTF_EDIT_MSG32("EM_FMTLINES");
00503                 result = (LRESULT)EDIT_EM_FmtLines(hwnd, es, (BOOL)wParam);
00504                 break;
00505 
00506         case EM_LINEFROMCHAR:
00507                 //DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
00508                 result = (LRESULT)EDIT_EM_LineFromChar(hwnd, es, (INT)wParam);
00509                 break;
00510 
00511         /* message 0x00ca missing from specs */
00512 
00513         case WM_USER+26:
00514                 //DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
00515                 /* fall through */
00516         case 0x00ca:
00517                 //DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
00518                 result = DefWindowProcA(hwnd, msg, wParam, lParam);
00519                 break;
00520 
00521         case EM_SETTABSTOPS:
00522                 //DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
00523                 result = (LRESULT)EDIT_EM_SetTabStops(hwnd, es, (INT)wParam, (LPINT)lParam);
00524                 break;
00525 
00526         case EM_SETPASSWORDCHAR:
00527                 //DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
00528                 EDIT_EM_SetPasswordChar(hwnd, es, (CHAR)wParam);
00529                 break;
00530 
00531         case EM_EMPTYUNDOBUFFER:
00532                 //DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
00533                 EDIT_EM_EmptyUndoBuffer(hwnd, es);
00534                 break;
00535 
00536         case EM_GETFIRSTVISIBLELINE:
00537                 //DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
00538                 result = (LRESULT)EDIT_EM_GetFirstVisibleLine(hwnd,es);
00539                 break;
00540 
00541         case EM_SETREADONLY:
00542                 //DPRINTF_EDIT_MSG32("EM_SETREADONLY");
00543                 result = (LRESULT)EDIT_EM_SetReadOnly(hwnd,es,(BOOL)wParam);
00544                 break;
00545 
00546         case EM_SETWORDBREAKPROC:
00547                 //DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
00548                 EDIT_EM_SetWordBreakProc(hwnd, es, (EDITWORDBREAKPROCA)lParam);
00549                 break;
00550 
00551         case EM_GETWORDBREAKPROC:
00552                 //DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
00553                 result = (LRESULT)EDIT_EM_GetWordbreakProc(hwnd,es);
00554                 break;
00555 
00556         case EM_GETPASSWORDCHAR:
00557                 //DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
00558                 result = (LRESULT)EDIT_EM_GetPasswordChar(hwnd,es);
00559                 break;
00560 
00561         /* The following EM_xxx are new to win95 and don't exist for 16 bit */
00562 
00563         case EM_SETMARGINS:
00564                 //DPRINTF_EDIT_MSG32("EM_SETMARGINS");
00565                 EDIT_EM_SetMargins(hwnd, es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
00566                 break;
00567 
00568         case EM_GETMARGINS:
00569                 //DPRINTF_EDIT_MSG32("EM_GETMARGINS");
00570                 result = EDIT_EM_GetMargins(hwnd,es);
00571                 break;
00572 
00573         case EM_GETLIMITTEXT:
00574                 //DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
00575                 result = (LRESULT)EDIT_EM_GetLimitText(hwnd,es);
00576                 break;
00577 
00578         case EM_POSFROMCHAR:
00579                 //DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
00580                 result = EDIT_EM_PosFromChar(hwnd,0, es, (INT)wParam, FALSE);
00581                 break;
00582 
00583         case EM_CHARFROMPOS:
00584                 //DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
00585                 result = EDIT_EM_CharFromPos(hwnd, es, SLOWORD(lParam), SHIWORD(lParam));
00586                 break;
00587 
00588         case EM_SETIMESTATUS:
00589                 result = EDIT_EM_SetIMEStatus(hwnd,es,wParam,lParam);
00590                 break;
00591 
00592         case EM_GETIMESTATUS:
00593                 result = EDIT_EM_GetIMEStatus(hwnd,es,wParam,lParam);
00594                 break;
00595 
00596         case WM_GETDLGCODE:
00597                 //DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
00598                 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
00599 
00600                 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
00601                 {
00602                    int vk = (int)((LPMSG)lParam)->wParam;
00603 
00604                    if ((GetWindowLongA(hwnd,GWL_STYLE) & ES_WANTRETURN) && vk == VK_RETURN)
00605                    {
00606                       result |= DLGC_WANTMESSAGE;
00607                    }
00608                    else if (es->hwndListBox && (vk == VK_RETURN || vk == VK_ESCAPE))
00609                    {
00610                       if (SendMessageA(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
00611                          result |= DLGC_WANTMESSAGE;
00612                    }
00613                 }
00614                 break;
00615 
00616         case WM_CHAR:
00617                 //DPRINTF_EDIT_MSG32("WM_CHAR");
00618                 if (((CHAR)wParam == VK_RETURN || (CHAR)wParam == VK_ESCAPE) && es->hwndListBox)
00619                 {
00620                    HWND hwndParent = GetParent(hwnd);
00621                 
00622                    if (SendMessageA(hwndParent, CB_GETDROPPEDSTATE, 0, 0))
00623                       SendMessageA(hwndParent, WM_KEYDOWN, wParam, 0);
00624                    break;
00625                 }
00626                 EDIT_WM_Char(hwnd, es, (CHAR)wParam, (DWORD)lParam);
00627                 break;
00628 
00629         case WM_CLEAR:
00630                 //DPRINTF_EDIT_MSG32("WM_CLEAR");
00631                 EDIT_WM_Clear(hwnd, es);
00632                 break;
00633 
00634         case WM_COMMAND:
00635                 //DPRINTF_EDIT_MSG32("WM_COMMAND");
00636                 EDIT_WM_Command(hwnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
00637                 break;
00638 
00639         case WM_CONTEXTMENU:
00640                 //DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
00641                 EDIT_WM_ContextMenu(hwnd, es, (HWND)wParam, SLOWORD(lParam), SHIWORD(lParam));
00642                 break;
00643 
00644         case WM_COPY:
00645                 //DPRINTF_EDIT_MSG32("WM_COPY");
00646                 EDIT_WM_Copy(hwnd, es);
00647                 break;
00648 
00649         case WM_CREATE:
00650                 //DPRINTF_EDIT_MSG32("WM_CREATE");
00651                 result = EDIT_WM_Create(hwnd, es, (LPCREATESTRUCTA)lParam);
00652                 break;
00653 
00654         case WM_CUT:
00655                 //DPRINTF_EDIT_MSG32("WM_CUT");
00656                 EDIT_WM_Cut(hwnd, es);
00657                 break;
00658 
00659         case WM_ENABLE:
00660                 //DPRINTF_EDIT_MSG32("WM_ENABLE");
00661                 es->bEnableState = (BOOL)wParam;
00662                 EDIT_Refresh(hwnd,es,FALSE);
00663                 break;
00664 
00665         case WM_ERASEBKGND:
00666                 //DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
00667                 result = EDIT_WM_EraseBkGnd(hwnd, es, (HDC)wParam);
00668                 break;
00669 
00670         case WM_GETFONT:
00671                 //DPRINTF_EDIT_MSG32("WM_GETFONT");
00672                 result = (LRESULT)es->font;
00673                 break;
00674 
00675         case WM_GETTEXT:
00676                 //DPRINTF_EDIT_MSG32("WM_GETTEXT");
00677                 result = (LRESULT)EDIT_WM_GetText(hwnd, es, (INT)wParam, (LPSTR)lParam);
00678                 break;
00679 
00680         case WM_GETTEXTLENGTH:
00681                 //DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
00682                 result = lstrlenA(es->text);
00683                 break;
00684 
00685         case WM_HSCROLL:
00686                 //DPRINTF_EDIT_MSG32("WM_HSCROLL");
00687                 result = EDIT_WM_HScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)lParam);
00688                 break;
00689 
00690         case WM_KEYDOWN:
00691                 //DPRINTF_EDIT_MSG32("WM_KEYDOWN");
00692                 result = EDIT_WM_KeyDown(hwnd, es, (INT)wParam, (DWORD)lParam);
00693                 break;
00694 
00695         case WM_KILLFOCUS:
00696                 //DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
00697                 result = EDIT_WM_KillFocus(hwnd, es, (HWND)wParam);
00698                 break;
00699 
00700         case WM_LBUTTONDBLCLK:
00701                 //DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
00702                 result = EDIT_WM_LButtonDblClk(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
00703                 break;
00704 
00705         case WM_LBUTTONDOWN:
00706                 //DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
00707                 result = EDIT_WM_LButtonDown(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
00708                 break;
00709 
00710         case WM_LBUTTONUP:
00711                 //DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
00712                 result = EDIT_WM_LButtonUp(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
00713                 break;
00714 
00715         case WM_CAPTURECHANGED:
00716                 result = EDIT_WM_CaptureChanged(hwnd,es);
00717                 break;
00718 
00719         case WM_MOUSEACTIVATE:
00720                 /*
00721                  *      FIXME: maybe DefWindowProc() screws up, but it seems that
00722                  *              modalless dialog boxes need this.  If we don't do this, the focus
00723                  *              will _not_ be set by DefWindowProc() for edit controls in a
00724                  *              modalless dialog box ???
00725                  */
00726                 //DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
00727                 result = MA_ACTIVATE;
00728                 break;
00729 
00730         case WM_MOUSEMOVE:
00731                 /*
00732                  *      DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
00733                  */
00734                 result = EDIT_WM_MouseMove(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
00735                 break;
00736 
00737         case WM_PAINT:
00738                 //DPRINTF_EDIT_MSG32("WM_PAINT");
00739                 EDIT_WM_Paint(hwnd, es,wParam);
00740                 break;
00741 
00742         case WM_PASTE:
00743                 //DPRINTF_EDIT_MSG32("WM_PASTE");
00744                 EDIT_WM_Paste(hwnd, es);
00745                 break;
00746 
00747         case WM_SETFOCUS:
00748                 //DPRINTF_EDIT_MSG32("WM_SETFOCUS");
00749                 EDIT_WM_SetFocus(hwnd, es, (HWND)wParam);
00750                 break;
00751 
00752         case WM_SETFONT:
00753                 //DPRINTF_EDIT_MSG32("WM_SETFONT");
00754                 EDIT_WM_SetFont(hwnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
00755                 break;
00756 
00757         case WM_SETTEXT:
00758                 //DPRINTF_EDIT_MSG32("WM_SETTEXT");
00759                 EDIT_WM_SetText(hwnd, es, (LPCSTR)lParam);
00760                 result = TRUE;
00761                 break;
00762 
00763         case WM_SIZE:
00764                 //DPRINTF_EDIT_MSG32("WM_SIZE");
00765                 EDIT_WM_Size(hwnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
00766                 break;
00767 
00768         case WM_SYSKEYDOWN:
00769                 //DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
00770                 result = EDIT_WM_SysKeyDown(hwnd, es, (INT)wParam, (DWORD)lParam);
00771                 break;
00772 
00773         case WM_TIMER:
00774                 //DPRINTF_EDIT_MSG32("WM_TIMER");
00775                 EDIT_WM_Timer(hwnd, es, (INT)wParam, (TIMERPROC)lParam);
00776                 break;
00777 
00778         case WM_VSCROLL:
00779                 //DPRINTF_EDIT_MSG32("WM_VSCROLL");
00780                 result = EDIT_WM_VScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)(lParam));
00781                 break;
00782 
00783         case WM_MOUSEWHEEL:
00784                 result = EDIT_WM_MouseWheel(hwnd,es,wParam,lParam);
00785                 break;
00786 
00787         default:
00788                 result = DefWindowProcA(hwnd, msg, wParam, lParam);
00789                 break;
00790         }
00791         EDIT_UnlockBuffer(hwnd, es, FALSE);
00792     END:
00793         return result;
00794 
00795 }
00796 
00797 
00798 /*********************************************************************
00799  *
00800  *      EDIT_BuildLineDefs_ML
00801  *
00802  *      Build linked list of text lines.
00803  *      Lines can end with '\0' (last line), a character (if it is wrapped),
00804  *      a soft return '\r\r\n' or a hard return '\r\n'
00805  *
00806  */
00807 static void EDIT_BuildLineDefs_ML(HWND hwnd,EDITSTATE *es)
00808 {
00809         HDC hdc = 0;
00810         HFONT old_font = 0;
00811         LPSTR start, cp;
00812         INT fw;
00813         LINEDEF *current_def;
00814         LINEDEF **previous_next;
00815 
00816         current_def = es->first_line_def;
00817         do {
00818                 LINEDEF *next_def = current_def->next;
00819                 HeapFree(es->heap, 0, current_def);
00820                 current_def = next_def;
00821         } while (current_def);
00822         es->line_count = 0;
00823         es->text_width = 0;
00824 
00825         fw = es->format_rect.right - es->format_rect.left;
00826         start = es->text;
00827         previous_next = &es->first_line_def;
00828         do {
00829                 current_def = (LINEDEF*)HeapAlloc(es->heap, 0, sizeof(LINEDEF));
00830                 current_def->next = NULL;
00831                 cp = start;
00832                 while (*cp) {
00833                         if ((*cp == '\r') && (*(cp + 1) == '\n'))
00834                                 break;
00835                         cp++;
00836                 }
00837                 if (!(*cp)) {
00838                         current_def->ending = END_0;
00839                         current_def->net_length = lstrlenA(start);
00840                 } else if ((cp > start) && (*(cp - 1) == '\r')) {
00841                         current_def->ending = END_SOFT;
00842                         current_def->net_length = cp - start - 1;
00843                 } else {
00844                         current_def->ending = END_HARD;
00845                         current_def->net_length = cp - start;
00846                 }
00847 
00848                 if (!hdc)
00849                 {
00850                   hdc = GetDC(hwnd);
00851                   if (es->font)
00852                     old_font = SelectObject(hdc, es->font);
00853                 }
00854 
00855                 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
00856                                         start, current_def->net_length,
00857                                         es->tabs_count, es->tabs));
00858                 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
00859                 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
00860                         INT next = 0;
00861                         INT prev;
00862                         do {
00863                                 prev = next;
00864                                 next = EDIT_CallWordBreakProc(hwnd, es, start - es->text,
00865                                                 prev + 1, current_def->net_length, WB_RIGHT);
00866                                 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
00867                                                         start, next, es->tabs_count, es->tabs));
00868                         } while (current_def->width <= fw);
00869                         if (!prev) {
00870                                 next = 0;
00871                                 do {
00872                                         prev = next;
00873                                         next++;
00874                                         current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
00875                                                                 start, next, es->tabs_count, es->tabs));
00876                                 } while (current_def->width <= fw);
00877                                 if (!prev)
00878                                         prev = 1;
00879                         }
00880                         current_def->net_length = prev;
00881                         current_def->ending = END_WRAP;
00882                         current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc, start,
00883                                                 current_def->net_length, es->tabs_count, es->tabs));
00884                 }
00885                 switch (current_def->ending) {
00886                 case END_SOFT:
00887                         current_def->length = current_def->net_length + 3;
00888                         break;
00889                 case END_HARD:
00890                         current_def->length = current_def->net_length + 2;
00891                         break;
00892                 case END_WRAP:
00893                 case END_0:
00894                         current_def->length = current_def->net_length;
00895                         break;
00896                 }
00897                 es->text_width = MAX(es->text_width, current_def->width);
00898                 start += current_def->length;
00899                 *previous_next = current_def;
00900                 previous_next = &current_def->next;
00901                 es->line_count++;
00902         } while (current_def->ending != END_0);
00903         if (hdc)
00904         {
00905           if (es->font)
00906                   SelectObject(hdc, old_font);
00907           ReleaseDC(hwnd, hdc);
00908         }
00909         EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
00910 }
00911 
00912 
00913 /*********************************************************************
00914  *
00915  *      EDIT_CallWordBreakProc
00916  *
00917  *      Call appropriate WordBreakProc (internal or external).
00918  *
00919  *      Note: The "start" argument should always be an index refering
00920  *              to es->text.  The actual wordbreak proc might be
00921  *              16 bit, so we can't always pass any 32 bit LPSTR.
00922  *              Hence we assume that es->text is the buffer that holds
00923  *              the string under examination (we can decide this for ourselves).
00924  *
00925  */
00926 static INT EDIT_CallWordBreakProc(HWND hwnd, EDITSTATE *es, INT start, INT index, INT count, INT action)
00927 {
00928         if (es->word_break_procA)
00929         {
00930             //TRACE_(relay)("(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
00931             //               es->word_break_proc32A, es->text + start, index,
00932             //               count, action );
00933             return (INT)es->word_break_procA( es->text + start, index,
00934                                                   count, action );
00935         }
00936         else
00937             return EDIT_WordBreakProc(es->text + start, index, count, action);
00938 }
00939 
00940 
00941 /*********************************************************************
00942  *
00943  *      EDIT_CharFromPos
00944  *
00945  *      Beware: This is not the function called on EM_CHARFROMPOS
00946  *              The position _can_ be outside the formatting / client
00947  *              rectangle
00948  *              The return value is only the character index
00949  *
00950  */
00951 static INT EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
00952 {
00953         INT index;
00954         HDC dc = 0;
00955         HFONT old_font = 0;
00956 
00957         if (es->style & ES_MULTILINE) {
00958                 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
00959                 INT line_index = 0;
00960                 LINEDEF *line_def = es->first_line_def;
00961                 INT low, high;
00962                 while ((line > 0) && line_def->next) {
00963                         line_index += line_def->length;
00964                         line_def = line_def->next;
00965                         line--;
00966                 }
00967                 x += es->x_offset - es->format_rect.left;
00968                 if (x >= line_def->width) {
00969                         if (after_wrap)
00970                                 *after_wrap = (line_def->ending == END_WRAP);
00971                         return line_index + line_def->net_length;
00972                 }
00973                 if (x <= 0) {
00974                         if (after_wrap)
00975                                 *after_wrap = FALSE;
00976                         return line_index;
00977                 }
00978                 low = line_index + 1;
00979                 high = line_index + line_def->net_length + 1;
00980                 if (low < high-1)
00981                 {
00982                   if (!dc)
00983                   {
00984                     dc = GetDC(hwnd);
00985                     if (es->font)
00986                       old_font = SelectObject(dc, es->font);
00987                   }
00988 
00989                   while (low < high-1)
00990                   {
00991                     INT mid = (low + high) / 2;
00992                     if (LOWORD(GetTabbedTextExtentA(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
00993                     else low = mid;
00994                   }
00995                 }
00996                 index = low;
00997 
00998                 if (after_wrap)
00999                         *after_wrap = ((index == line_index + line_def->net_length) &&
01000                                                         (line_def->ending == END_WRAP));
01001         } else {
01002                 LPSTR text;
01003                 SIZE size;
01004                 if (after_wrap)
01005                         *after_wrap = FALSE;
01006                 x -= es->format_rect.left;
01007                 if (!x)
01008                         return es->x_offset;
01009                 text = EDIT_GetPasswordPointer_SL(hwnd, es);
01010                 if (x < 0)
01011                 {
01012                   INT low = 0;
01013                   INT high = es->x_offset;
01014 
01015                   if (low < high-1)
01016                   {
01017                     if (!dc)
01018                     {
01019                       dc = GetDC(hwnd);
01020                       if (es->font)
01021                         old_font = SelectObject(dc, es->font);
01022                     }
01023 
01024                     while (low < high-1)
01025                     {
01026                       INT mid = (low + high) / 2;
01027 
01028                       GetTextExtentPoint32A( dc, text + mid,
01029                                              es->x_offset - mid, &size );
01030                       if (size.cx > -x) low = mid;
01031                       else high = mid;
01032                     }
01033                   }
01034                   index = low;
01035                 } else
01036                 {
01037                   INT low = es->x_offset;
01038                   INT high = lstrlenA(es->text) + 1;
01039 
01040                   if (low < high-1)
01041                   {
01042                     if (!dc)
01043                     {
01044                       dc = GetDC(hwnd);
01045                       if (es->font)
01046                         old_font = SelectObject(dc, es->font);
01047                     }
01048 
01049                     while (low < high-1)
01050                     {
01051                       INT mid = (low + high) / 2;
01052 
01053                       GetTextExtentPoint32A( dc, text + es->x_offset,
01054                                              mid - es->x_offset, &size );
01055                       if (size.cx > x) high = mid;
01056                       else low = mid;
01057                     }
01058                   }
01059                   index = low;
01060                 }
01061                 if (es->style & ES_PASSWORD)
01062                         HeapFree(es->heap, 0 ,text);
01063         }
01064         if (dc)
01065         {
01066           if (es->font)
01067             SelectObject(dc, old_font);
01068           ReleaseDC(hwnd, dc);
01069         }
01070         return index;
01071 }
01072 
01073 
01074 /*********************************************************************
01075  *
01076  *      EDIT_ConfinePoint
01077  *
01078  *      adjusts the point to be within the formatting rectangle
01079  *      (so CharFromPos returns the nearest _visible_ character)
01080  *
01081  */
01082 static void EDIT_ConfinePoint(HWND hwnd, EDITSTATE *es, LPINT x, LPINT y)
01083 {
01084         *x = MIN(MAX(*x, es->format_rect.left), es->format_rect.right - 1);
01085         *y = MIN(MAX(*y, es->format_rect.top), es->format_rect.bottom - 1);
01086 }
01087 
01088 
01089 /*********************************************************************
01090  *
01091  *      EDIT_GetLineRect
01092  *
01093  *      Calculates the bounding rectangle for a line from a starting
01094  *      column to an ending column.
01095  *
01096  */
01097 static void EDIT_GetLineRect(HWND hwnd,HDC dc,EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
01098 {
01099         INT line_index =  EDIT_EM_LineIndex(hwnd, es, line);
01100 
01101         if (es->style & ES_MULTILINE)
01102                 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
01103         else
01104                 rc->top = es->format_rect.top;
01105         rc->bottom = rc->top + es->line_height;
01106 
01107         rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(hwnd,dc, es, line_index + scol, TRUE));
01108         rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(hwnd,dc, es, line_index + ecol, TRUE))+1;
01109 }
01110 
01111 
01112 /*********************************************************************
01113  *
01114  *      EDIT_GetPasswordPointer_SL
01115  *
01116  *      note: caller should free the (optionally) allocated buffer
01117  *
01118  */
01119 static LPSTR EDIT_GetPasswordPointer_SL(HWND hwnd, EDITSTATE *es)
01120 {
01121         if (es->style & ES_PASSWORD) {
01122                 INT len = lstrlenA(es->text);
01123                 LPSTR text = (LPSTR)HeapAlloc(es->heap, 0, len + 1);
01124                 RtlFillMemory(text, len, es->password_char);
01125                 text[len] = '\0';
01126                 return text;
01127         } else
01128                 return es->text;
01129 }
01130 
01131 
01132 /*********************************************************************
01133  *
01134  *      EDIT_LockBuffer
01135  *
01136  *      This acts as a LOCAL_Lock(), but it locks only once.  This way
01137  *      you can call it whenever you like, without unlocking.
01138  *
01139  */
01140 static void EDIT_LockBuffer(HWND hwnd, EDITSTATE *es)
01141 {
01142         if (!es) {
01143                 //ERR_(edit)("no EDITSTATE ... please report\n");
01144                 return;
01145         }
01146         if (!(es->style & ES_MULTILINE))
01147                 return;
01148         if (!es->text) {
01149                 if (es->hloc)
01150                         es->text = (char*)LocalLock(es->hloc);
01151                 else {
01152                         //ERR_(edit)("no buffer ... please report\n");
01153                         return;
01154                 }
01155         }
01156         es->lock_count++;
01157 }
01158 
01159 
01160 /*********************************************************************
01161  *
01162  *      EDIT_SL_InvalidateText
01163  *
01164  *      Called from EDIT_InvalidateText().
01165  *      Does the job for single-line controls only.
01166  *
01167  */
01168 static void EDIT_SL_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
01169 {
01170         RECT line_rect;
01171         RECT rc;
01172 
01173         EDIT_GetLineRect(hwnd,0, es, 0, start, end, &line_rect);
01174 
01175         if (IntersectRect(&rc, &line_rect, &es->format_rect))
01176         {
01177           HideCaret(hwnd);
01178           InvalidateRect(hwnd, &rc, TRUE);
01179           ShowCaret(hwnd);
01180         }
01181 }
01182 
01183 
01184 /*********************************************************************
01185  *
01186  *      EDIT_ML_InvalidateText
01187  *
01188  *      Called from EDIT_InvalidateText().
01189  *      Does the job for multi-line controls only.
01190  *
01191  */
01192 static void EDIT_ML_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
01193 {
01194         INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
01195         INT sl = EDIT_EM_LineFromChar(hwnd, es, start);
01196         INT el = EDIT_EM_LineFromChar(hwnd, es, end);
01197         INT sc;
01198         INT ec;
01199         RECT rc1;
01200         RECT rcWnd;
01201         RECT rcLine;
01202         RECT rcUpdate;
01203         INT l;
01204 
01205         if ((el < es->y_offset) || (sl > es->y_offset + vlc))
01206                 return;
01207 
01208         sc = start - EDIT_EM_LineIndex(hwnd, es, sl);
01209         ec = end - EDIT_EM_LineIndex(hwnd, es, el);
01210         if (sl < es->y_offset) {
01211                 sl = es->y_offset;
01212                 sc = 0;
01213         }
01214         if (el > es->y_offset + vlc) {
01215                 el = es->y_offset + vlc;
01216                 ec = EDIT_EM_LineLength(hwnd, es, EDIT_EM_LineIndex(hwnd, es, el));
01217         }
01218         GetClientRect(hwnd, &rc1);
01219         IntersectRect(&rcWnd, &rc1, &es->format_rect);
01220         HideCaret(hwnd);
01221         if (sl == el) {
01222                 EDIT_GetLineRect(hwnd,0, es, sl, sc, ec, &rcLine);
01223 
01224                 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
01225                         InvalidateRect(hwnd, &rcUpdate, TRUE);
01226         } else {
01227                 EDIT_GetLineRect(hwnd,0, es, sl, sc,
01228                                 EDIT_EM_LineLength(hwnd, es,
01229                                         EDIT_EM_LineIndex(hwnd, es, sl)),
01230                                 &rcLine);
01231                 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
01232                         InvalidateRect(hwnd, &rcUpdate, TRUE);
01233                 for (l = sl + 1 ; l < el ; l++) {
01234                         EDIT_GetLineRect(hwnd,0, es, l, 0,
01235                                 EDIT_EM_LineLength(hwnd, es,
01236                                         EDIT_EM_LineIndex(hwnd, es, l)),
01237                                 &rcLine);
01238                         if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
01239                                 InvalidateRect(hwnd, &rcUpdate, TRUE);
01240                 }
01241                 EDIT_GetLineRect(hwnd,0, es, el, 0, ec, &rcLine);
01242                 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
01243                         InvalidateRect(hwnd, &rcUpdate, TRUE);
01244         }
01245         ShowCaret(hwnd);
01246 }
01247 
01248 
01249 /*********************************************************************
01250  *
01251  *      EDIT_InvalidateText
01252  *
01253  *      Invalidate the text from offset start upto, but not including,
01254  *      offset end.  Useful for (re)painting the selection.
01255  *      Regions outside the linewidth are not invalidated.
01256  *      end == -1 means end == TextLength.
01257  *      start and end need not be ordered.
01258  *
01259  */
01260 static void EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
01261 {
01262         if (end == start)
01263                 return;
01264 
01265         if (end == -1)
01266                 end = lstrlenA(es->text);
01267 
01268         ORDER_INT(start, end);
01269 
01270         if (es->style & ES_MULTILINE)
01271                 EDIT_ML_InvalidateText(hwnd, es, start, end);
01272         else
01273                 EDIT_SL_InvalidateText(hwnd, es, start, end);
01274 }
01275 
01276 
01277 /*********************************************************************
01278  *
01279  *      EDIT_MakeFit
01280  *
01281  *      Try to fit size + 1 bytes in the buffer.  Constrain to limits.
01282  *
01283  */
01284 static BOOL EDIT_MakeFit(HWND hwnd, EDITSTATE *es, INT size)
01285 {
01286         HLOCAL hNew32;
01287 
01288 #ifndef __WIN32OS2__
01289         if (size > es->buffer_limit) {
01290                 EDIT_NOTIFY_PARENT(hwnd, EN_MAXTEXT);
01291                 return FALSE;
01292         }
01293 #endif
01294 
01295         if (size <= es->buffer_size)
01296                 return TRUE;
01297         size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
01298 
01299 #ifndef __WIN32OS2__
01300         if (size > es->buffer_limit)
01301                 size = es->buffer_limit;
01302 #endif
01303 
01304         //TRACE_(edit)("trying to ReAlloc to %d+1\n", size);
01305 
01306         EDIT_UnlockBuffer(hwnd, es, TRUE);
01307         if (es->text) {
01308                 es->text = (char*)HeapReAlloc(es->heap, 0, es->text, size + 1);
01309                 if (es->text)
01310 #ifdef __WIN32OS2__
01311                         es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
01312 #else
01313                         es->buffer_size = MIN(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
01314 #endif
01315                 else
01316                         es->buffer_size = 0;
01317         } else if (es->hloc) {
01318                 hNew32 = LocalReAlloc(es->hloc, size + 1, 0);
01319                 if (hNew32) {
01320                         //TRACE_(edit)("Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
01321                         es->hloc = hNew32;
01322 #ifdef __WIN32OS2__
01323                         es->buffer_size = LocalSize(es->hloc) - 1;
01324 #else
01325                         es->buffer_size = MIN(LocalSize(es->hloc) - 1, es->buffer_limit);
01326 #endif
01327                 }
01328         }
01329         if (es->buffer_size < size) {
01330                 EDIT_LockBuffer(hwnd, es);
01331                 //WARN_(edit)("FAILED !  We now have %d+1\n", es->buffer_size);
01332                 EDIT_NOTIFY_PARENT(hwnd, EN_ERRSPACE);
01333                 return FALSE;
01334         } else {
01335                 EDIT_LockBuffer(hwnd, es);
01336                 //TRACE_(edit)("We now have %d+1\n", es->buffer_size);
01337                 return TRUE;
01338         }
01339 }
01340 
01341 
01342 /*********************************************************************
01343  *
01344  *      EDIT_MakeUndoFit
01345  *
01346  *      Try to fit size + 1 bytes in the undo buffer.
01347  *
01348  */
01349 static BOOL EDIT_MakeUndoFit(HWND hwnd, EDITSTATE *es, INT size)
01350 {
01351         if (size <= es->undo_buffer_size)
01352                 return TRUE;
01353         size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
01354 
01355         //TRACE_(edit)("trying to ReAlloc to %d+1\n", size);
01356         es->undo_text = (char*)HeapReAlloc(es->heap, 0, es->undo_text, size + 1);
01357         if (es->undo_text) {
01358                 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
01359                 if (es->undo_buffer_size < size) {
01360                         //WARN_(edit)("FAILED !  We now have %d+1\n", es->undo_buffer_size);
01361                         return FALSE;
01362                 }
01363                 return TRUE;
01364         }
01365         return FALSE;
01366 }
01367 
01368 
01369 /*********************************************************************
01370  *
01371  *      EDIT_MoveBackward
01372  *
01373  */
01374 static void EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
01375 {
01376         INT e = es->selection_end;
01377 
01378         if (e) {
01379                 e--;
01380                 if ((es->style & ES_MULTILINE) && e &&
01381                                 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
01382                         e--;
01383                         if (e && (es->text[e - 1] == '\r'))
01384                                 e--;
01385                 }
01386         }
01387         EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
01388         EDIT_EM_ScrollCaret(hwnd, es);
01389 }
01390 
01391 
01392 /*********************************************************************
01393  *
01394  *      EDIT_MoveDown_ML
01395  *
01396  *      Only for multi line controls
01397  *      Move the caret one line down, on a column with the nearest
01398  *      x coordinate on the screen (might be a different column).
01399  *
01400  */
01401 static void EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
01402 {
01403         INT s = es->selection_start;
01404         INT e = es->selection_end;
01405         BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
01406         LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
01407         INT x = SLOWORD(pos);
01408         INT y = SHIWORD(pos);
01409 
01410         e = EDIT_CharFromPos(hwnd, es, x, y + es->line_height, &after_wrap);
01411         if (!extend)
01412                 s = e;
01413         EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
01414         EDIT_EM_ScrollCaret(hwnd, es);
01415 }
01416 
01417 
01418 /*********************************************************************
01419  *
01420  *      EDIT_MoveEnd
01421  *
01422  */
01423 static void EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend)
01424 {
01425         BOOL after_wrap = FALSE;
01426         INT e;
01427 
01428         /* Pass a high value in x to make sure of receiving the en of the line */
01429         if (es->style & ES_MULTILINE)
01430                 e = EDIT_CharFromPos(hwnd, es, 0x3fffffff,
01431                         HIWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
01432         else
01433                 e = lstrlenA(es->text);
01434         EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, after_wrap);
01435         EDIT_EM_ScrollCaret(hwnd, es);
01436 }
01437 
01438 
01439 /*********************************************************************
01440  *
01441  *      EDIT_MoveForward
01442  *
01443  */
01444 static void EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend)
01445 {
01446         INT e = es->selection_end;
01447 
01448         if (es->text[e]) {
01449                 e++;
01450                 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
01451                         if (es->text[e] == '\n')
01452                                 e++;
01453                         else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
01454                                 e += 2;
01455                 }
01456         }
01457         EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
01458         EDIT_EM_ScrollCaret(hwnd, es);
01459 }
01460 
01461 
01462 /*********************************************************************
01463  *
01464  *      EDIT_MoveHome
01465  *
01466  *      Home key: move to beginning of line.
01467  *
01468  */
01469 static void EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend)
01470 {
01471         INT e;
01472 
01473         /* Pass the x_offset in x to make sure of receiving the first position of the line */
01474         if (es->style & ES_MULTILINE)
01475                 e = EDIT_CharFromPos(hwnd, es, -es->x_offset,
01476                         HIWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
01477         else
01478                 e = 0;
01479         EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
01480         EDIT_EM_ScrollCaret(hwnd, es);
01481 }
01482 
01483 
01484 /*********************************************************************
01485  *
01486  *      EDIT_MovePageDown_ML
01487  *
01488  *      Only for multi line controls
01489  *      Move the caret one page down, on a column with the nearest
01490  *      x coordinate on the screen (might be a different column).
01491  *
01492  */
01493 static void EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
01494 {
01495         INT s = es->selection_start;
01496         INT e = es->selection_end;
01497         BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
01498         LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
01499         INT x = SLOWORD(pos);
01500         INT y = SHIWORD(pos);
01501 
01502         e = EDIT_CharFromPos(hwnd, es, x,
01503                 y + (es->format_rect.bottom - es->format_rect.top),
01504                 &after_wrap);
01505         if (!extend)
01506                 s = e;
01507         EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
01508         EDIT_EM_ScrollCaret(hwnd, es);
01509 }
01510 
01511 
01512 /*********************************************************************
01513  *
01514  *      EDIT_MovePageUp_ML
01515  *
01516  *      Only for multi line controls
01517  *      Move the caret one page up, on a column with the nearest
01518  *      x coordinate on the screen (might be a different column).
01519  *
01520  */
01521 static void EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
01522 {
01523         INT s = es->selection_start;
01524         INT e = es->selection_end;
01525         BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
01526         LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
01527         INT x = SLOWORD(pos);
01528         INT y = SHIWORD(pos);
01529 
01530         e = EDIT_CharFromPos(hwnd, es, x,
01531                 y - (es->format_rect.bottom - es->format_rect.top),
01532                 &after_wrap);
01533         if (!extend)
01534                 s = e;
01535         EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
01536         EDIT_EM_ScrollCaret(hwnd, es);
01537 }
01538 
01539 
01540 /*********************************************************************
01541  *
01542  *      EDIT_MoveUp_ML
01543  *
01544  *      Only for multi line controls
01545  *      Move the caret one line up, on a column with the nearest
01546  *      x coordinate on the screen (might be a different column).
01547  *
01548  */
01549 static void EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
01550 {
01551         INT s = es->selection_start;
01552         INT e = es->selection_end;
01553         BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
01554         LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
01555         INT x = SLOWORD(pos);
01556         INT y = SHIWORD(pos);
01557 
01558         e = EDIT_CharFromPos(hwnd, es, x, y - es->line_height, &after_wrap);
01559         if (!extend)
01560                 s = e;
01561         EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
01562         EDIT_EM_ScrollCaret(hwnd, es);
01563 }
01564 
01565 
01566 /*********************************************************************
01567  *
01568  *      EDIT_MoveWordBackward
01569  *
01570  */
01571 static void EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
01572 {
01573         INT s = es->selection_start;
01574         INT e = es->selection_end;
01575         INT l;
01576         INT ll;
01577         INT li;
01578 
01579         l = EDIT_EM_LineFromChar(hwnd, es, e);
01580         ll = EDIT_EM_LineLength(hwnd, es, e);
01581         li = EDIT_EM_LineIndex(hwnd, es, l);
01582         if (e - li == 0) {
01583                 if (l) {
01584                         li = EDIT_EM_LineIndex(hwnd, es, l - 1);
01585                         e = li + EDIT_EM_LineLength(hwnd, es, li);
01586                 }
01587         } else {
01588                 e = li + (INT)EDIT_CallWordBreakProc(hwnd, es,
01589                                 li, e - li, ll, WB_LEFT);
01590         }
01591         if (!extend)
01592                 s = e;
01593         EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
01594         EDIT_EM_ScrollCaret(hwnd, es);
01595 }
01596 
01597 
01598 /*********************************************************************
01599  *
01600  *      EDIT_MoveWordForward
01601  *
01602  */
01603 static void EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend)
01604 {
01605         INT s = es->selection_start;
01606         INT e = es->selection_end;
01607         INT l;
01608         INT ll;
01609         INT li;
01610 
01611         l = EDIT_EM_LineFromChar(hwnd, es, e);
01612         ll = EDIT_EM_LineLength(hwnd, es, e);
01613         li = EDIT_EM_LineIndex(hwnd, es, l);
01614         if (e - li == ll) {
01615                 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
01616                         e = EDIT_EM_LineIndex(hwnd, es, l + 1);
01617         } else {
01618                 e = li + EDIT_CallWordBreakProc(hwnd, es,
01619                                 li, e - li + 1, ll, WB_RIGHT);
01620         }
01621         if (!extend)
01622                 s = e;
01623         EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
01624         EDIT_EM_ScrollCaret(hwnd, es);
01625 }
01626 
01627 
01628 /*********************************************************************
01629  *
01630  *      EDIT_PaintLine
01631  *
01632  */
01633 static void EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
01634 {
01635         INT s = es->selection_start;
01636         INT e = es->selection_end;
01637         INT li;
01638         INT ll;
01639         INT x;
01640         INT y;
01641         LRESULT pos;
01642 
01643         if (es->style & ES_MULTILINE) {
01644                 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
01645                 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
01646                         return;
01647         } else if (line)
01648                 return;
01649 
01650         //TRACE_(edit)("line=%d\n", line);
01651 
01652         pos = EDIT_EM_PosFromChar(hwnd,dc, es, EDIT_EM_LineIndex(hwnd, es, line), FALSE);
01653         x = SLOWORD(pos);
01654         y = SHIWORD(pos);
01655         li = EDIT_EM_LineIndex(hwnd, es, line);
01656         ll = EDIT_EM_LineLength(hwnd, es, li);
01657         s = es->selection_start;
01658         e = es->selection_end;
01659         ORDER_INT(s, e);
01660         s = MIN(li + ll, MAX(li, s));
01661         e = MIN(li + ll, MAX(li, e));
01662 
01663         if (rev && (s != e) && ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL)))
01664         {
01665           HRGN oldRgn,newRgn,combRgn;
01666           RECT rect;
01667           //CB: OS/2 has problems with relative string positions (i.e. Communicator)
01668           //    fix: always calculate string from starting point, tab bugs fixed too
01669           //    otherwise we have 'dancing characters'
01670 
01671           if (!(es->style & ES_MULTILINE))
01672           {
01673             SIZE size;
01674 
01675             rect.top = y;
01676             rect.bottom = y+es->line_height;
01677             GetTextExtentPoint32A(dc,es->text+li,s-li,&size);
01678             rect.left = x+size.cx;
01679             GetTextExtentPoint32A(dc,es->text+li,e-li,&size);
01680             rect.right = x+size.cx;
01681 
01682             oldRgn = CreateRectRgnIndirect(&rect); //dummy parameter
01683             GetClipRgn(dc,oldRgn);
01684             newRgn = CreateRectRgnIndirect(&rect);
01685             combRgn = CreateRectRgnIndirect(&rect); //dummy parameter
01686             CombineRgn(combRgn,oldRgn,newRgn,RGN_XOR);
01687             CombineRgn(combRgn,oldRgn,combRgn,RGN_AND);
01688             SelectClipRgn(dc,combRgn);
01689             EDIT_PaintText(hwnd,es,dc,x,y,line,0,ll,FALSE);
01690             CombineRgn(combRgn,oldRgn,newRgn,RGN_AND);
01691             SelectClipRgn(dc,combRgn);
01692             EDIT_PaintText(hwnd,es,dc,x,y,line,0,e-li,TRUE);
01693             DeleteObject(oldRgn);
01694             DeleteObject(newRgn);
01695             DeleteObject(combRgn);
01696           } else
01697           {
01698             rect.top = y;
01699             rect.bottom = y+es->line_height;
01700             rect.left = x+LOWORD(TabbedTextOutA(dc,x,y,es->text+li,s-li,es->tabs_count,es->tabs,es->format_rect.left-es->x_offset));
01701             rect.right = x+LOWORD(TabbedTextOutA(dc,x,y,es->text+li,e-li,es->tabs_count,es->tabs,es->format_rect.left-es->x_offset));
01702 
01703             oldRgn = CreateRectRgnIndirect(&rect); //dummy parameter
01704             GetClipRgn(dc,oldRgn);
01705             newRgn = CreateRectRgnIndirect(&rect);
01706             combRgn = CreateRectRgnIndirect(&rect); //dummy parameter
01707             CombineRgn(combRgn,oldRgn,newRgn,RGN_XOR);
01708             CombineRgn(combRgn,oldRgn,combRgn,RGN_AND);
01709             SelectClipRgn(dc,combRgn);
01710             EDIT_PaintText(hwnd,es,dc,x,y,line,0,ll,FALSE);
01711             CombineRgn(combRgn,oldRgn,newRgn,RGN_AND);
01712             SelectClipRgn(dc,combRgn);
01713             EDIT_PaintText(hwnd,es,dc,x,y,line,0,e-li,TRUE);
01714             SelectClipRgn(dc,oldRgn);
01715             DeleteObject(oldRgn);
01716             DeleteObject(newRgn);
01717             DeleteObject(combRgn);
01718           }
01719         } else  EDIT_PaintText(hwnd, es, dc, x, y, line, 0, ll, FALSE);
01720 }
01721 
01722 
01723 /*********************************************************************
01724  *
01725  *      EDIT_PaintText
01726  *
01727  */
01728 static VOID EDIT_PaintText(HWND hwnd, EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
01729 {
01730         COLORREF BkColor;
01731         COLORREF TextColor;
01732         INT li;
01733 
01734         if (!count)
01735                 return;
01736         BkColor = GetBkColor(dc);
01737         TextColor = GetTextColor(dc);
01738         if (rev)
01739         {
01740                 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
01741                 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
01742         }
01743         li = EDIT_EM_LineIndex(hwnd, es, line);
01744         if (es->style & ES_MULTILINE)
01745         {
01746                 TabbedTextOutA(dc, x, y, es->text + li + col, count,
01747                                es->tabs_count, es->tabs, es->format_rect.left - es->x_offset);
01748         } else
01749         {
01750           LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
01751           POINT pt;
01752 
01753           TextOutA(dc,x,y,text+li+col,count);
01754           if (es->style & ES_PASSWORD)
01755             HeapFree(es->heap, 0, text);
01756         }
01757         if (rev)
01758         {
01759                 SetBkColor(dc, BkColor);
01760                 SetTextColor(dc, TextColor);
01761         }
01762 }
01763 
01764 
01765 /*********************************************************************
01766  *
01767  *      EDIT_SetCaretPos
01768  *
01769  */
01770 static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos,
01771                              BOOL after_wrap)
01772 {
01773         LRESULT res = EDIT_EM_PosFromChar(hwnd,0, es, pos, after_wrap);
01774         INT x = SLOWORD(res);
01775         INT y = SHIWORD(res);
01776 
01777         if(x < es->format_rect.left)
01778                 x = es->format_rect.left;
01779         if(x > es->format_rect.right - 2)
01780                 x = es->format_rect.right - 2;
01781         if(y > es->format_rect.bottom)
01782                 y = es->format_rect.bottom;
01783         if(y < es->format_rect.top)
01784                 y = es->format_rect.top;
01785         SetCaretPos(x, y);
01786         return;
01787 }
01788 
01789 
01790 /*********************************************************************
01791  *
01792  *      EDIT_SetRectNP
01793  *
01794  *      note:   this is not (exactly) the handler called on EM_SETRECTNP
01795  *              it is also used to set the rect of a single line control
01796  *
01797  */
01798 static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT rc)
01799 {
01800         CopyRect(&es->format_rect, rc);
01801         if (es->style & WS_BORDER)
01802         {
01803           INT bw = GetSystemMetrics(SM_CXBORDER)+1,bh = GetSystemMetrics(SM_CYBORDER)+1;
01804 
01805           es->format_rect.left += bw;
01806           es->format_rect.top += bh;
01807           es->format_rect.right -= bw;
01808           es->format_rect.bottom -= bh;
01809         }
01810         es->format_rect.left += es->left_margin;
01811         es->format_rect.right -= es->right_margin;
01812         es->format_rect.right = MAX(es->format_rect.right, es->format_rect.left + es->char_width);
01813         if (es->style & ES_MULTILINE)
01814                 es->format_rect.bottom = es->format_rect.top +
01815                         MAX(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
01816         else
01817                 es->format_rect.bottom = es->format_rect.top + es->line_height;
01818         if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
01819                 EDIT_BuildLineDefs_ML(hwnd,es);
01820         EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
01821 }
01822 
01823 
01824 /*********************************************************************
01825  *
01826  *      EDIT_UnlockBuffer
01827  *
01828  */
01829 static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force)
01830 {
01831         if (!es) {
01832                 //ERR_(edit)("no EDITSTATE ... please report\n");
01833                 return;
01834         }
01835         if (!(es->style & ES_MULTILINE))
01836                 return;
01837         if (!es->lock_count) {
01838                 //ERR_(edit)("lock_count == 0 ... please report\n");
01839                 return;
01840         }
01841         if (!es->text) {
01842                 //ERR_(edit)("es->text == 0 ... please report\n");
01843                 return;
01844         }
01845         if (force || (es->lock_count == 1)) {
01846                 if (es->hloc) {
01847                         LocalUnlock(es->hloc);
01848                         es->text = NULL;
01849                 }
01850         }
01851         es->lock_count--;
01852 }
01853 
01854 
01855 /*********************************************************************
01856  *
01857  *      EDIT_WordBreakProc
01858  *
01859  *      Find the beginning of words.
01860  *      Note:   unlike the specs for a WordBreakProc, this function only
01861  *              allows to be called without linebreaks between s[0] upto
01862  *              s[count - 1].  Remember it is only called
01863  *              internally, so we can decide this for ourselves.
01864  *
01865  */
01866 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
01867 {
01868         INT ret = 0;
01869 
01870         //TRACE_(edit)("s=%p, index=%u, count=%u, action=%d\n",
01871         //             s, index, count, action);
01872 
01873         switch (action) {
01874         case WB_LEFT:
01875                 if (!count)
01876                         break;
01877                 if (index)
01878                         index--;
01879                 if (s[index] == ' ') {
01880                         while (index && (s[index] == ' '))
01881                                 index--;
01882                         if (index) {
01883                                 while (index && (s[index] != ' '))
01884                                         index--;
01885                                 if (s[index] == ' ')
01886                                         index++;
01887                         }
01888                 } else {
01889                         while (index && (s[index] != ' '))
01890                                 index--;
01891                         if (s[index] == ' ')
01892                                 index++;
01893                 }
01894                 ret = index;
01895                 break;
01896         case WB_RIGHT:
01897                 if (!count)
01898                         break;
01899                 if (index)
01900                         index--;
01901                 if (s[index] == ' ')
01902                         while ((index < count) && (s[index] == ' ')) index++;
01903                 else {
01904                         while (s[index] && (s[index] != ' ') && (index < count))
01905                                 index++;
01906                         while ((s[index] == ' ') && (index < count)) index++;
01907                 }
01908                 ret = index;
01909                 break;
01910         case WB_ISDELIMITER:
01911                 ret = (s[index] == ' ');
01912                 break;
01913         default:
01914                 //ERR_(edit)("unknown action code, please report !\n");
01915                 break;
01916         }
01917         return ret;
01918 }
01919 
01920 
01921 /*********************************************************************
01922  *
01923  *      EM_CHARFROMPOS
01924  *
01925  *      returns line number (not index) in high-order word of result.
01926  *      NB : Q137805 is unclear about this. POINT * pointer in lParam apply
01927  *      to Richedit, not to the edit control. Original documentation is valid.
01928  *
01929  */
01930 static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y)
01931 {
01932         POINT pt;
01933         INT index;
01934 
01935         pt.x = x;
01936         pt.y = y;
01937 
01938         if (!PtInRect(&es->format_rect,pt)) return -1;
01939 
01940         index = EDIT_CharFromPos(hwnd, es, x, y, NULL);
01941         return MAKELONG(index, EDIT_EM_LineFromChar(hwnd, es, index));
01942 }
01943 
01944 
01945 /*********************************************************************
01946  *
01947  *      EM_FMTLINES
01948  *
01949  * Enable or disable soft breaks.
01950  */
01951 static BOOL EDIT_EM_FmtLines(HWND hwnd, EDITSTATE *es, BOOL add_eol)
01952 {
01953         es->flags &= ~EF_USE_SOFTBRK;
01954         if (add_eol) {
01955                 es->flags |= EF_USE_SOFTBRK;
01956                 dprintf(("EDIT: EM_FMTLINES: soft break enabled, not implemented\n"));
01957         }
01958         return add_eol;
01959 }
01960 
01961 static INT EDIT_EM_GetFirstVisibleLine(HWND hwnd,EDITSTATE *es)
01962 {
01963   return (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
01964 }
01965 
01966 /*********************************************************************
01967  *
01968  *      EM_GETHANDLE
01969  *
01970  *      Hopefully this won't fire back at us.
01971  *      We always start with a fixed buffer in our own heap.
01972  *      However, with this message a 32 bit application requests
01973  *      a handle to 32 bit moveable local heap memory, where it expects
01974  *      to find the text.
01975  *      It's a pity that from this moment on we have to use this
01976  *      local heap, because applications may rely on the handle
01977  *      in the future.
01978  *
01979  *      In this function we'll try to switch to local heap.
01980  *
01981  */
01982 static HLOCAL EDIT_EM_GetHandle(HWND hwnd, EDITSTATE *es)
01983 {
01984         HLOCAL newBuf;
01985         LPSTR newText;
01986         INT newSize;
01987 
01988         if (!(es->style & ES_MULTILINE))
01989                 return 0;
01990 
01991         if (es->hloc)
01992                 return es->hloc;
01993 
01994         if (!(newBuf = LocalAlloc(LMEM_MOVEABLE, lstrlenA(es->text) + 1))) {
01995                 //ERR_(edit)("could not allocate new 32 bit buffer\n");
01996                 return 0;
01997         }
01998 #ifdef __WIN32OS2__
01999         newSize = LocalSize(newBuf) - 1;
02000 #else
02001         newSize = MIN(LocalSize(newBuf) - 1, es->buffer_limit);
02002 #endif
02003         if (!(newText = (char*)LocalLock(newBuf))) {
02004                 //ERR_(edit)("could not lock new 32 bit buffer\n");
02005                 LocalFree(newBuf);
02006                 return 0;
02007         }
02008         lstrcpyA(newText, es->text);
02009         EDIT_UnlockBuffer(hwnd, es, TRUE);
02010         if (es->text)
02011                 HeapFree(es->heap, 0, es->text);
02012         es->hloc = newBuf;
02013         es->buffer_size = newSize;
02014         es->text = newText;
02015         EDIT_LockBuffer(hwnd, es);
02016         //TRACE_(edit)("switched to 32 bit local heap\n");
02017 
02018         return es->hloc;
02019 }
02020 
02021 static INT EDIT_EM_GetLimitText(HWND hwnd,EDITSTATE *es)
02022 {
02023   return es->buffer_limit;
02024 }
02025 
02026 /*********************************************************************
02027  *
02028  *      EM_GETLINE
02029  *
02030  */
02031 static INT EDIT_EM_GetLine(HWND hwnd, EDITSTATE *es, INT line, LPSTR lpch)
02032 {
02033         LPSTR src;
02034         INT len;
02035         INT i;
02036 
02037         if (!lpch || (*(WORD*)lpch == 0)) return 0;
02038 
02039         if (es->style & ES_MULTILINE) {
02040                 if (line >= es->line_count)
02041                         return 0;
02042         } else
02043                 line = 0;
02044         i = EDIT_EM_LineIndex(hwnd, es, line);
02045         src = es->text + i;
02046         len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(hwnd, es, i));
02047         for (i = 0 ; i < len ; i++) {
02048                 *lpch = *src;
02049                 src++;
02050                 lpch++;
02051         }
02052         //SvL: Terminate string
02053         *lpch = 0;
02054         return (LRESULT)len;
02055 }
02056 
02057 static INT EDIT_EM_GetLineCount(HWND hwnd,EDITSTATE *es)
02058 {
02059   return (es->style & ES_MULTILINE) ? es->line_count : 1;
02060 }
02061 
02062 static LONG EDIT_EM_GetMargins(HWND hwnd,EDITSTATE *es)
02063 {
02064   return MAKELONG(es->left_margin, es->right_margin);
02065 }
02066 
02067 static BOOL EDIT_EM_GetModify(HWND hwnd,EDITSTATE *es)
02068 {
02069   return ((es->flags & EF_MODIFIED) != 0);
02070 }
02071 
02072 static CHAR EDIT_EM_GetPasswordChar(HWND hwnd,EDITSTATE *es)
02073 {
02074   return es->password_char;
02075 }
02076 
02077 static VOID EDIT_EM_GetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
02078 {
02079   if (lprc) CopyRect(lprc,&es->format_rect);
02080 }
02081 
02082 /*********************************************************************
02083  *
02084  *      EM_GETSEL
02085  *
02086  */
02087 static LRESULT EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end)
02088 {
02089         UINT s = es->selection_start;
02090         UINT e = es->selection_end;
02091 
02092         ORDER_UINT(s, e);
02093 
02094         if (start)
02095                 *start = s;
02096         if (end)
02097                 *end = e;
02098         return MAKELONG(s, e);
02099 }
02100 
02101 /*********************************************************************
02102  *
02103  *      EM_GETTHUMB
02104  */
02105 static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es)
02106 {
02107   SCROLLINFO si;
02108 
02109   if (!(es->style & ES_MULTILINE)) return 0;
02110 
02111   si.cbSize = sizeof(si);
02112   si.fMask  = SIF_TRACKPOS;
02113   return GetScrollInfo(hwnd,SB_VERT,&si) ? si.nTrackPos:0;
02114 }
02115 
02116 static PVOID EDIT_EM_GetWordbreakProc(HWND hwnd,EDITSTATE *es)
02117 {
02118   return es->word_break_procA;
02119 }
02120 
02121 /*********************************************************************
02122  *
02123  *      EM_LINEFROMCHAR
02124  *
02125  */
02126 static INT EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index)
02127 {
02128         INT line;
02129         LINEDEF *line_def;
02130 
02131         if (!(es->style & ES_MULTILINE))
02132                 return 0;
02133         if (index > lstrlenA(es->text))
02134                 return es->line_count - 1;
02135         if (index == -1)
02136                 index = MIN(es->selection_start, es->selection_end); //selection_end == caret pos
02137 
02138         line = 0;
02139         line_def = es->first_line_def;
02140         index -= line_def->length;
02141         while ((index >= 0) && line_def->next) {
02142                 line++;
02143                 line_def = line_def->next;
02144                 index -= line_def->length;
02145         }
02146         return line;
02147 }
02148 
02149 
02150 /*********************************************************************
02151  *
02152  *      EM_LINEINDEX
02153  *
02154  */
02155 static INT EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line)
02156 {
02157         INT line_index;
02158         LINEDEF *line_def;
02159 
02160         if (!(es->style & ES_MULTILINE))
02161                 return 0;
02162         if (line >= es->line_count)
02163                 return -1;
02164 
02165         line_index = 0;
02166         line_def = es->first_line_def;
02167         if (line == -1) {
02168                 INT index = es->selection_end - line_def->length;
02169                 while ((index >= 0) && line_def->next) {
02170                         line_index += line_def->length;
02171                         line_def = line_def->next;
02172                         index -= line_def->length;
02173                 }
02174         } else {
02175                 while (line > 0) {
02176                         line_index += line_def->length;
02177                         line_def = line_def->next;
02178                         line--;
02179                 }
02180         }
02181         return line_index;
02182 }
02183 
02184 
02185 /*********************************************************************
02186  *
02187  *      EM_LINELENGTH
02188  *
02189  */
02190 static INT EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index)
02191 {
02192         LINEDEF *line_def;
02193 
02194         if (!(es->style & ES_MULTILINE))
02195                 return lstrlenA(es->text);
02196 
02197         if (index == -1)
02198         {
02199           INT sl = EDIT_EM_LineFromChar(hwnd,es,MIN(es->selection_start,es->selection_end));
02200           INT el = EDIT_EM_LineFromChar(hwnd,es,MAX(es->selection_start,es->selection_end));
02201 
02202           if (sl == el)
02203             return EDIT_EM_LineLength(hwnd,es,sl)+es->selection_start-es->selection_end;
02204           else
02205             return es->selection_start+EDIT_EM_LineLength(hwnd,es,el)-es->selection_end;
02206         }
02207         line_def = es->first_line_def;
02208         index -= line_def->length;
02209         while ((index >= 0) && line_def->next) {
02210                 line_def = line_def->next;
02211                 index -= line_def->length;
02212         }
02213         return line_def->net_length;
02214 }
02215 
02216 static VOID EDIT_UpdateScrollBars(HWND hwnd,EDITSTATE *es,BOOL updateHorz,BOOL updateVert)
02217 {
02218   if (updateHorz && (es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
02219   {
02220     SCROLLINFO si;
02221     INT fw = es->format_rect.right - es->format_rect.left;
02222 
02223     si.cbSize       = sizeof(SCROLLINFO);
02224     si.fMask        = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
02225     si.nMin         = 0;
02226     si.nMax         = es->text_width + fw - 1;
02227     si.nPage        = fw;
02228     si.nPos         = es->x_offset;
02229     SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
02230   }
02231 
02232   if (updateVert && (es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
02233   {
02234     INT vlc = (es->format_rect.bottom-es->format_rect.top)/es->line_height;
02235     SCROLLINFO si;
02236 
02237     si.cbSize       = sizeof(SCROLLINFO);
02238     si.fMask        = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
02239     si.nMin         = 0;
02240     si.nMax         = es->line_count + vlc - 2;
02241     si.nPage        = vlc;
02242     si.nPos         = es->y_offset;
02243     SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
02244   }
02245 }
02246 
02247 
02248 /*********************************************************************
02249  *
02250  *      EM_LINESCROLL
02251  *
02252  *      FIXME: dx is in average character widths
02253  *              However, we assume it is in pixels when we use this
02254  *              function internally
02255  *
02256  */
02257 static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy)
02258 {
02259         INT nyoff;
02260         INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
02261 
02262         if (!(es->style & ES_MULTILINE))
02263                 return FALSE;
02264 
02265         if (-dx > es->x_offset)
02266                 dx = -es->x_offset;
02267         if (dx > es->text_width - es->x_offset)
02268                 dx = es->text_width - es->x_offset;
02269         nyoff = MAX(0, es->y_offset + dy);
02270 
02271         //SvL: If nyoff > nr lines in control -> last line in control
02272         //     window should show last line of control
02273         //     Wine code shows last line at the top of the control
02274         //     (Quake 3 startup edit control shows the problem)
02275         if (nyoff >= es->line_count) {
02276                 if(es->line_count <= vlc) {
02277                         nyoff = es->y_offset;   //no need to scroll
02278                 }
02279                 else    nyoff = es->line_count - vlc - 1;
02280         }
02281         dy = (es->y_offset - nyoff) * es->line_height;
02282         if (dx || dy)
02283         {
02284                 RECT rc1;
02285                 RECT rc;
02286 
02287                 if (dx && !(es->flags & EF_HSCROLL_TRACK))
02288                   EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
02289                 if (dy && !(es->flags & EF_VSCROLL_TRACK))
02290                   EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
02291 
02292                 GetClientRect(hwnd, &rc1);
02293                 IntersectRect(&rc, &rc1, &es->format_rect);
02294 
02295                 ScrollWindowEx(hwnd,-dx,dy,NULL,&rc,(HRGN)NULL,NULL,SW_INVALIDATE);
02296                 es->y_offset = nyoff;
02297                 es->x_offset += dx;
02298                 EDIT_UpdateScrollBars(hwnd,es,dx,dy);
02299         }
02300 
02301         return TRUE;
02302 }
02303 
02304 
02305 /*********************************************************************
02306  *
02307  *      EM_POSFROMCHAR
02308  *
02309  */
02310 static LRESULT EDIT_EM_PosFromChar(HWND hwnd,HDC dc, EDITSTATE *es, INT index, BOOL after_wrap)
02311 {
02312         INT len = lstrlenA(es->text);
02313         INT l;
02314         INT li;
02315         INT x;
02316         INT y = 0;
02317         BOOL createdDC = FALSE;
02318         HFONT old_font = 0;
02319         SIZE size;
02320 
02321         index = MIN(index, len);
02322         if (!dc)
02323         {
02324           dc = GetDC(hwnd);
02325           if (es->font)
02326             old_font = SelectObject(dc, es->font);
02327           createdDC = TRUE;
02328         }
02329         if (es->style & ES_MULTILINE) {
02330                 l = EDIT_EM_LineFromChar(hwnd, es, index);
02331                 y = (l - es->y_offset) * es->line_height;
02332                 li = EDIT_EM_LineIndex(hwnd, es, l);
02333                 if (after_wrap && (li == index) && l) {
02334                         INT l2 = l - 1;
02335                         LINEDEF *line_def = es->first_line_def;
02336                         while (l2) {
02337                                 line_def = line_def->next;
02338                                 l2--;
02339                         }
02340                         if (line_def->ending == END_WRAP) {
02341                                 l--;
02342                                 y -= es->line_height;
02343                                 li = EDIT_EM_LineIndex(hwnd, es, l);
02344                         }
02345                 }
02346                 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
02347                                 es->tabs_count, es->tabs)) - es->x_offset;
02348         } else
02349         {
02350           LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
02351 
02352           GetTextExtentPoint32A(dc,text,index,&size);
02353           x = size.cx;
02354           if (es->x_offset)
02355           {
02356             GetTextExtentPoint32A(dc,text,es->x_offset,&size);
02357             x -= size.cx;
02358           }
02359           y = 0;
02360           if (es->style & ES_PASSWORD)
02361             HeapFree(es->heap, 0 ,text);
02362         }
02363         x += es->format_rect.left;
02364         y += es->format_rect.top;
02365         if (createdDC)
02366         {
02367           if (es->font)
02368             SelectObject(dc, old_font);
02369           ReleaseDC(hwnd, dc);
02370         }
02371         return MAKELONG((INT16)x, (INT16)y);
02372 }
02373 
02374 BOOL EDIT_CheckNumber(CHAR *text)
02375 {
02376   if (!text) return TRUE;
02377 
02378   while (text[0] != 0)
02379   {
02380     if ((BYTE)text[0] < '0' || (BYTE)text[0] > '9') return FALSE;
02381     text++;
02382   }
02383 
02384   return TRUE;
02385 }
02386 
02387 /*********************************************************************
02388  *
02389  *      EM_REPLACESEL
02390  *
02391  */
02392 static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace)
02393 {
02394         INT strl = lstrlenA(lpsz_replace);
02395         INT tl = lstrlenA(es->text);
02396         INT utl;
02397         UINT s;
02398         UINT e;
02399         INT i;
02400         LPSTR p;
02401 
02402         s = es->selection_start;
02403         e = es->selection_end;
02404 
02405         if ((s == e) && !strl)
02406                 return;
02407 
02408         ORDER_UINT(s, e);
02409 
02410         if (!EDIT_MakeFit(hwnd, es, tl - (e - s) + strl))
02411                 return;
02412 
02413         if (e != s) {
02414                 /* there is something to be deleted */
02415                 if (can_undo) {
02416                         utl = lstrlenA(es->undo_text);
02417                         if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
02418                                 /* undo-buffer is extended to the right */
02419                                 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
02420                                 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
02421                         } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
02422                                 /* undo-buffer is extended to the left */
02423                                 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
02424                                 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
02425                                         p[e - s] = p[0];
02426                                 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
02427                                         p[i] = (es->text + s)[i];
02428                                 es->undo_position = s;
02429                         } else {
02430                                 /* new undo-buffer */
02431                                 EDIT_MakeUndoFit(hwnd, es, e - s);
02432                                 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
02433                                 es->undo_position = s;
02434                         }
02435                         /* any deletion makes the old insertion-undo invalid */
02436                         es->undo_insert_count = 0;
02437                 } else
02438                         EDIT_EM_EmptyUndoBuffer(hwnd, es);
02439 
02440                 /* now delete */
02441                 lstrcpyA(es->text + s, es->text + e);
02442         }
02443         if (strl)
02444         {
02445           if ((es->style & ES_NUMBER) && !EDIT_CheckNumber((CHAR*)lpsz_replace))
02446             MessageBeep(MB_ICONEXCLAMATION);
02447           else
02448           {
02449             /* there is an insertion */
02450             if (can_undo) {
02451               if ((s == es->undo_position) ||
02452                   ((es->undo_insert_count) &&
02453                   (s == es->undo_position + es->undo_insert_count)))
02454                   /*
02455                    * insertion is new and at delete position or
02456                    * an extension to either left or right
02457                    */
02458                   es->undo_insert_count += strl;
02459               else {
02460                 /* new insertion undo */
02461                 es->undo_position = s;
02462                 es->undo_insert_count = strl;
02463                 /* new insertion makes old delete-buffer invalid */
02464                 *es->undo_text = '\0';
02465               }
02466             } else
02467               EDIT_EM_EmptyUndoBuffer(hwnd, es);
02468 
02469 
02470             /* now insert */
02471             tl = lstrlenA(es->text);
02472             for (p = es->text + tl ; p >= es->text + s ; p--)
02473             p[strl] = p[0];
02474             for (i = 0 , p = es->text + s ; i < strl ; i++)
02475               p[i] = lpsz_replace[i];
02476 
02477             if (es->style & ES_OEMCONVERT)
02478             {
02479               CHAR *text = (LPSTR)HeapAlloc(es->heap,0,strl);
02480 
02481               CharToOemBuffA(lpsz_replace,text,strl);
02482               OemToCharBuffA(text,p,strl);
02483               HeapFree(es->heap,0,text);
02484             }
02485 
02486             if(es->style & ES_UPPERCASE)
02487               CharUpperBuffA(p, strl);
02488             else if(es->style & ES_LOWERCASE)
02489               CharLowerBuffA(p, strl);
02490 
02491             s += strl;
02492           }
02493         }
02494         /* FIXME: really inefficient */
02495         if (es->style & ES_MULTILINE)
02496                 EDIT_BuildLineDefs_ML(hwnd,es);
02497 
02498         EDIT_EM_SetSel(hwnd, es, s, s, FALSE);
02499 
02500         es->flags |= EF_MODIFIED;
02501         es->flags |= EF_UPDATE;
02502         EDIT_EM_ScrollCaret(hwnd, es);
02503 
02504         /* FIXME: really inefficient */
02505         EDIT_Refresh(hwnd,es,TRUE);
02506 }
02507 
02508 
02509 /*********************************************************************
02510  *
02511  *      EM_SCROLL
02512  *
02513  */
02514 static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action)
02515 {
02516         INT dy;
02517 
02518         if (!(es->style & ES_MULTILINE))
02519                 return (LRESULT)FALSE;
02520 
02521         dy = 0;
02522 
02523         switch (action) {
02524         case SB_LINEUP:
02525                 if (es->y_offset)
02526                         dy = -1;
02527                 break;
02528         case SB_LINEDOWN:
02529                 if (es->y_offset < es->line_count - 1)
02530                         dy = 1;
02531                 break;
02532         case SB_PAGEUP:
02533                 if (es->y_offset)
02534                         dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
02535                 break;
02536         case SB_PAGEDOWN:
02537                 if (es->y_offset < es->line_count - 1)
02538                         dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
02539                 break;
02540         default:
02541                 return (LRESULT)FALSE;
02542         }
02543         if (dy) {
02544                 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
02545                 EDIT_EM_LineScroll(hwnd, es, 0, dy);
02546         }
02547         return MAKELONG((INT16)dy, (BOOL16)TRUE);
02548 }
02549 
02550 
02551 /*********************************************************************
02552  *
02553  *      EM_SCROLLCARET
02554  *
02555  */
02556 static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es)
02557 {
02558         if (es->style & ES_MULTILINE) {
02559                 INT l;
02560                 INT li;
02561                 INT vlc;
02562                 INT ww;
02563                 INT cw = es->char_width;
02564                 INT x;
02565                 INT dy = 0;
02566                 INT dx = 0;
02567 
02568                 l = EDIT_EM_LineFromChar(hwnd, es, es->selection_end);
02569                 li = EDIT_EM_LineIndex(hwnd, es, l);
02570                 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP));
02571                 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
02572                 if (l >= es->y_offset + vlc)
02573                         dy = l - vlc + 1 - es->y_offset;
02574                 if (l < es->y_offset)
02575                         dy = l - es->y_offset;
02576                 ww = es->format_rect.right - es->format_rect.left;
02577                 if (x < es->format_rect.left)
02578                         dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
02579                 if (x > es->format_rect.right)
02580                         dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
02581                 if (dy || dx)
02582                         EDIT_EM_LineScroll(hwnd, es, dx, dy);
02583         } else {
02584                 INT x;
02585                 INT goal;
02586                 INT format_width;
02587 
02588                 if (!(es->style & ES_AUTOHSCROLL))
02589                         return;
02590 
02591                 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
02592                 format_width = es->format_rect.right - es->format_rect.left;
02593                 if (x < es->format_rect.left)
02594                 {
02595                         goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
02596                         do {
02597                                 es->x_offset--;
02598                                 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
02599                         } while ((x < goal) && es->x_offset);
02600                         /* FIXME: use ScrollWindow() somehow to improve performance */
02601                         EDIT_Refresh(hwnd,es,TRUE);
02602                         EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
02603                 } else if (x > es->format_rect.right)
02604                 {
02605                         INT x_last;
02606                         INT len = lstrlenA(es->text);
02607                         goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
02608                         do {
02609                                 es->x_offset++;
02610                                 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
02611                                 x_last = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, len, FALSE));
02612                         } while ((x > goal) && (x_last > es->format_rect.right));
02613                         /* FIXME: use ScrollWindow() somehow to improve performance */
02614                         EDIT_Refresh(hwnd,es,TRUE);
02615                         EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
02616                 }
02617         }
02618 }
02619 
02620 
02621 /*********************************************************************
02622  *
02623  *      EM_SETHANDLE
02624  *
02625  */
02626 static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc)
02627 {
02628         if (!(es->style & ES_MULTILINE))
02629                 return;
02630 
02631         if (!hloc) {
02632                 //WARN_(edit)("called with NULL handle\n");
02633                 return;
02634         }
02635 
02636         EDIT_UnlockBuffer(hwnd, es, TRUE);
02637         /*
02638          *      old buffer is freed by caller, unless
02639          *      it is still in our own heap.  (in that case
02640          *      we free it, correcting the buggy caller.)
02641          */
02642         if (es->text)
02643                 HeapFree(es->heap, 0, es->text);
02644 
02645         es->hloc = hloc;
02646         es->text = NULL;
02647         es->buffer_size = LocalSize(es->hloc) - 1;
02648         EDIT_LockBuffer(hwnd, es);
02649 
02650         if (es->text && (es->text[0] != 0))
02651         {
02652           if (es->style & ES_NUMBER)
02653           {
02654             //CB: todo
02655           }
02656 
02657           if (es->style & ES_OEMCONVERT)
02658           {
02659             INT len = lstrlenA(es->text);
02660             CHAR *text = (LPSTR)HeapAlloc(es->heap,0,len);
02661 
02662             CharToOemBuffA(es->text,text,len);
02663             OemToCharBuffA(text,es->text,len);
02664             HeapFree(es->heap,0,text);
02665           }
02666 
02667           if(es->style & ES_UPPERCASE)
02668             CharUpperA(es->text);
02669           else if(es->style & ES_LOWERCASE)
02670             CharLowerA(es->text);
02671         }
02672 
02673         es->x_offset = es->y_offset = 0;
02674         es->selection_start = es->selection_end = 0;
02675         EDIT_EM_EmptyUndoBuffer(hwnd, es);
02676         es->flags &= ~EF_MODIFIED;
02677         es->flags &= ~EF_UPDATE;
02678         EDIT_BuildLineDefs_ML(hwnd,es);
02679         EDIT_Refresh(hwnd,es,FALSE);
02680         EDIT_EM_ScrollCaret(hwnd, es);
02681         EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
02682 }
02683 
02684 /*********************************************************************
02685  *
02686  *      EM_SETLIMITTEXT
02687  *
02688  *      FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
02689  *      However, the windows version is not complied to yet in all of edit.c
02690  *
02691  */
02692 static void EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit)
02693 {
02694         if (es->style & ES_MULTILINE) {
02695                 if (limit)
02696 #ifdef __WIN32OS2__
02697                         es->buffer_limit = limit;
02698 #else
02699                         es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
02700 #endif
02701                 else
02702                         es->buffer_limit = BUFLIMIT_MULTI;
02703         } else {
02704                 if (limit)
02705 #ifdef __WIN32OS2__
02706                         es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE_NT);
02707 #else
02708                         es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
02709 #endif
02710                 else
02711                         es->buffer_limit = BUFLIMIT_SINGLE;
02712         }
02713 }
02714 
02715 
02716 /*********************************************************************
02717  *
02718  *      EM_SETMARGINS
02719  *
02720  * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
02721  * action wParam despite what the docs say. EC_USEFONTINFO means one third
02722  * of the char's width, according to the new docs.
02723  *
02724  */
02725 static void EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action,
02726                                INT left, INT right)
02727 {
02728         RECT r;
02729         INT oldLeft = es->left_margin,oldRight = es->right_margin;
02730 
02731         if (action & EC_LEFTMARGIN) {
02732                 if (left != EC_USEFONTINFO)
02733                         es->left_margin = left;
02734                 else
02735                         es->left_margin = es->char_width / 3;
02736         }
02737 
02738         if (action & EC_RIGHTMARGIN) {
02739                 if (right != EC_USEFONTINFO)
02740                         es->right_margin = right;
02741                 else
02742                         es->right_margin = es->char_width / 3;
02743         }
02744         //TRACE_(edit)("left=%d, right=%d\n", es->left_margin, es->right_margin);
02745 
02746         if ((oldLeft != es->left_margin) || (oldRight != es->right_margin))
02747         {
02748           GetClientRect(hwnd, &r);
02749           EDIT_SetRectNP(hwnd, es, &r);
02750           if (es->style & ES_MULTILINE)
02751                   EDIT_BuildLineDefs_ML(hwnd,es);
02752 
02753           EDIT_Refresh(hwnd,es,FALSE);
02754           if (es->flags & EF_FOCUSED) {
02755                   DestroyCaret();
02756                   CreateCaret(hwnd, 0, 2, es->line_height);
02757                   EDIT_SetCaretPos(hwnd, es, es->selection_end,
02758                                    es->flags & EF_AFTER_WRAP);
02759                   ShowCaret(hwnd);
02760           }
02761         }
02762 }
02763 
02764 static void EDIT_EM_SetModify(HWND hwnd,EDITSTATE *es,BOOL fModified)
02765 {
02766   if (fModified)
02767     es->flags |= EF_MODIFIED;
02768   else
02769     es->flags &= ~(EF_MODIFIED | EF_UPDATE);  /* reset pending updates */
02770 }
02771 
02772 /*********************************************************************
02773  *
02774  *      EM_SETPASSWORDCHAR
02775  *
02776  */
02777 static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c)
02778 {
02779         if (es->style & ES_MULTILINE)
02780                 return;
02781 
02782         if (es->password_char == c)
02783                 return;
02784 
02785         es->password_char = c;
02786         if (c) {
02787                 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_PASSWORD);
02788                 es->style |= ES_PASSWORD;
02789         } else {
02790                 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_PASSWORD);
02791                 es->style &= ~ES_PASSWORD;
02792         }
02793         EDIT_Refresh(hwnd,es,FALSE);
02794 }
02795 
02796 static BOOL EDIT_EM_SetReadOnly(HWND hwnd,EDITSTATE *es,BOOL fReadOnly)
02797 {
02798   if (fReadOnly)
02799   {
02800     SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_READONLY);
02801     es->style |= ES_READONLY;
02802   } else
02803   {
02804     SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_READONLY);
02805     es->style &= ~ES_READONLY;
02806   }
02807 
02808   return TRUE;
02809 }
02810 
02811 static void EDIT_EM_SetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
02812 {
02813   if ((es->style & ES_MULTILINE) && lprc)
02814   {
02815     EDIT_SetRectNP(hwnd,es,lprc);
02816     EDIT_Refresh(hwnd,es,FALSE);
02817   }
02818 }
02819 
02820 static void EDIT_EM_SetRectNP(HWND hwnd,EDITSTATE *es,LPRECT lprc)
02821 {
02822   if ((es->style & ES_MULTILINE) && lprc)
02823     EDIT_SetRectNP(hwnd,es,lprc);
02824 }
02825 
02826 /*********************************************************************
02827  *
02828  *      EDIT_EM_SetSel
02829  *
02830  *      note:   unlike the specs say: the order of start and end
02831  *              _is_ preserved in Windows.  (i.e. start can be > end)
02832  *              In other words: this handler is OK
02833  *
02834  */
02835 static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
02836 {
02837         UINT old_start = es->selection_start;
02838         UINT old_end = es->selection_end;
02839         UINT len = lstrlenA(es->text);
02840 
02841         if (start == -1) {
02842                 start = es->selection_end;
02843                 end = es->selection_end;
02844         } else {
02845                 start = MIN(start, len);
02846                 end = MIN(end, len);
02847         }
02848         es->selection_start = start;
02849         es->selection_end = end;
02850         if (after_wrap)
02851                 es->flags |= EF_AFTER_WRAP;
02852         else
02853                 es->flags &= ~EF_AFTER_WRAP;
02854         if (es->flags & EF_FOCUSED)
02855                 EDIT_SetCaretPos(hwnd, es, end, after_wrap);
02856 /* This is little  bit more efficient than before, not sure if it can be improved. FIXME? */
02857         ORDER_UINT(start, end);
02858         ORDER_UINT(end, old_end);
02859         ORDER_UINT(start, old_start);
02860         ORDER_UINT(old_start, old_end);
02861         if ((start == old_start) && (end == old_end)) return;
02862         if (end != old_start)
02863         {
02864 /*
02865  * One can also do
02866  *          ORDER_UINT32(end, old_start);
02867  *          EDIT_InvalidateText(wnd, es, start, end);
02868  *          EDIT_InvalidateText(wnd, es, old_start, old_end);
02869  * in place of the following if statement.
02870  */
02871             if (old_start > end )
02872             {
02873                 EDIT_InvalidateText(hwnd, es, start, end);
02874                 EDIT_InvalidateText(hwnd, es, old_start, old_end);
02875             }
02876             else
02877             {
02878                 EDIT_InvalidateText(hwnd, es, start, old_start);
02879                 EDIT_InvalidateText(hwnd, es, end, old_end);
02880             }
02881         }
02882         else EDIT_InvalidateText(hwnd, es, start, old_end);
02883 }
02884 
02885 
02886 /*********************************************************************
02887  *
02888  *      EM_SETTABSTOPS
02889  *
02890  */
02891 static BOOL EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs)
02892 {
02893         if (!(es->style & ES_MULTILINE))
02894                 return FALSE;
02895         if (es->tabs)
02896                 HeapFree(es->heap, 0, es->tabs);
02897         es->tabs_count = count;
02898         if (!count)
02899                 es->tabs = NULL;
02900         else {
02901                 es->tabs = (INT*)HeapAlloc(es->heap, 0, count * sizeof(INT));
02902                 memcpy(es->tabs, tabs, count * sizeof(INT));
02903         }
02904         return TRUE;
02905 }
02906 
02907 
02908 /*********************************************************************
02909  *
02910  *      EM_SETWORDBREAKPROC
02911  *
02912  */
02913 static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
02914 {
02915         if (es->word_break_procA == wbp)
02916                 return;
02917 
02918         es->word_break_procA = wbp;
02919         if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
02920                 EDIT_BuildLineDefs_ML(hwnd,es);
02921                 EDIT_Refresh(hwnd,es,FALSE);
02922         }
02923 }
02924 
02925 
02926 /*********************************************************************
02927  *
02928  *      EM_UNDO / WM_UNDO
02929  *
02930  */
02931 static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es)
02932 {
02933         INT ulength = lstrlenA(es->undo_text);
02934         LPSTR utext = (LPSTR)HeapAlloc(es->heap, 0, ulength + 1);
02935 
02936         lstrcpyA(utext, es->undo_text);
02937 
02938         //TRACE_(edit)("before UNDO:insertion length = %d, deletion buffer = %s\n",
02939         //             es->undo_insert_count, utext);
02940 
02941         EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
02942         EDIT_EM_EmptyUndoBuffer(hwnd, es);
02943         EDIT_EM_ReplaceSel(hwnd, es, TRUE, utext);
02944         EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
02945         HeapFree(es->heap, 0, utext);
02946 
02947         //TRACE_(edit)("after UNDO:insertion length = %d, deletion buffer = %s\n",
02948         //                es->undo_insert_count, es->undo_text);
02949 
02950         if (es->flags & EF_UPDATE) {
02951                 es->flags &= ~EF_UPDATE;
02952                 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
02953         }
02954 
02955         return TRUE;
02956 }
02957 
02958 static LRESULT EDIT_EM_SetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
02959 {
02960   if (wParam == EMSIS_COMPOSITIONSTRING)
02961   {
02962     //CB: todo
02963   }
02964 
02965   return 0;
02966 }
02967 
02968 static LRESULT EDIT_EM_GetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
02969 {
02970   //CB: todo
02971 
02972   return 0; //default
02973 }
02974 
02975 /*********************************************************************
02976  *
02977  *      WM_CHAR
02978  *
02979  */
02980 static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data)
02981 {
02982         BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
02983         switch (c) {
02984         case '\r':
02985             /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
02986             if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
02987             {
02988               MessageBeep(MB_ICONEXCLAMATION);
02989               break;
02990             }
02991         case '\n':
02992                 if (es->style & ES_MULTILINE) {
02993                         if (es->style & ES_READONLY) {
02994                                 EDIT_MoveHome(hwnd, es, FALSE);
02995                                 EDIT_MoveDown_ML(hwnd, es, FALSE);
02996                         } else
02997                         {
02998                                 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\r\n");
02999                                 if (es->flags & EF_UPDATE) {
03000                                         es->flags &= ~EF_UPDATE;
03001                                         EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
03002                                 }
03003                         }
03004                 }
03005                 break;
03006         case '\t':
03007                 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
03008                 {
03009                         EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\t");
03010                         if (es->flags & EF_UPDATE) {
03011                                 es->flags &= ~EF_UPDATE;
03012                                 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
03013                         }
03014                 }
03015                 break;
03016         case VK_BACK:
03017                 if (!(es->style & ES_READONLY) && !control) {
03018                         if (es->selection_start != es->selection_end)
03019                                 EDIT_WM_Clear(hwnd, es);
03020                         else {
03021                                 /* delete character left of caret */
03022                                 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
03023                                 EDIT_MoveBackward(hwnd, es, TRUE);
03024                                 EDIT_WM_Clear(hwnd, es);
03025                         }
03026                 }
03027                 break;
03028 //CB: are these three keys documented or Linux style???
03029         case 0x03: /* ^C */
03030                 SendMessageA(hwnd, WM_COPY, 0, 0);
03031                 break;
03032         case 0x16: /* ^V */
03033                 SendMessageA(hwnd, WM_PASTE, 0, 0);
03034                 break;
03035         case 0x18: /* ^X */
03036                 SendMessageA(hwnd, WM_CUT, 0, 0);
03037                 break;
03038 
03039         default:
03040                 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127))
03041                 {
03042                   char str[2];
03043 
03044                   if (es->style & ES_NUMBER)
03045                   {
03046                         if (((BYTE)c < '0') || ((BYTE)c > '9')) {
03047                                 MessageBeep(MB_ICONEXCLAMATION);
03048                                 return;
03049                         }
03050                   }
03051                   str[0] = c;
03052                   str[1] = '\0';
03053                   EDIT_EM_ReplaceSel(hwnd, es, TRUE, str);
03054                   if (es->flags & EF_UPDATE)
03055                   {
03056                     es->flags &= ~EF_UPDATE;
03057                     EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
03058                   }
03059                 } else MessageBeep(MB_ICONEXCLAMATION);
03060                 break;
03061         }
03062 }
03063 
03064 
03065 /*********************************************************************
03066  *
03067  *      WM_COMMAND
03068  *
03069  */
03070 static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND control)
03071 {
03072         if (code || control)
03073                 return;
03074 
03075         switch (id) {
03076                 case EM_UNDO:
03077                         EDIT_EM_Undo(hwnd, es);
03078                         break;
03079                 case WM_CUT:
03080                         EDIT_WM_Cut(hwnd, es);
03081                         break;
03082                 case WM_COPY:
03083                         EDIT_WM_Copy(hwnd, es);
03084                         break;
03085                 case WM_PASTE:
03086                         EDIT_WM_Paste(hwnd, es);
03087                         break;
03088                 case WM_CLEAR:
03089                         EDIT_WM_Clear(hwnd, es);
03090                         break;
03091                 case EM_SETSEL:
03092                         EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
03093                         EDIT_EM_ScrollCaret(hwnd, es);
03094                         break;
03095                 default:
03096                         //ERR_(edit)("unknown menu item, please report\n");
03097                         break;
03098         }
03099 }
03100 
03101 
03102 /*********************************************************************
03103  *
03104  *      WM_CONTEXTMENU
03105  *
03106  *      Note: the resource files resource/sysres_??.rc cannot define a
03107  *              single popup menu.  Hence we use a (dummy) menubar
03108  *              containing the single popup menu as its first item.
03109  *
03110  *      FIXME: the message identifiers have been chosen arbitrarily,
03111  *              hence we use MF_BYPOSITION.
03112  *              We might as well use the "real" values (anybody knows ?)
03113  *              The menu definition is in resources/sysres_??.rc.
03114  *              Once these are OK, we better use MF_BYCOMMAND here
03115  *              (as we do in EDIT_WM_Command()).
03116  *
03117  */
03118 static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y)
03119 {
03120         HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
03121         HMENU popup = GetSubMenu(menu, 0);
03122         UINT start = es->selection_start;
03123         UINT end = es->selection_end;
03124 
03125         ORDER_UINT(start, end);
03126 
03127         /* undo */
03128         EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(hwnd, es) ? MF_ENABLED : MF_GRAYED));
03129         /* cut */
03130         EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & (ES_PASSWORD | ES_READONLY)) ? MF_ENABLED : MF_GRAYED));
03131         /* copy */
03132         EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
03133         /* paste */
03134         EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
03135         /* delete */
03136         EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
03137         /* select all */
03138         EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != lstrlenA(es->text)) ? MF_ENABLED : MF_GRAYED));
03139 
03140         TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, hwnd, NULL);
03141         DestroyMenu(menu);
03142 }
03143 
03144 
03145 /*********************************************************************
03146  *
03147  *      WM_COPY
03148  *
03149  */
03150 static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es)
03151 {
03152         INT s = es->selection_start;
03153         INT e = es->selection_end;
03154         HGLOBAL hdst;
03155         LPSTR dst;
03156 
03157         if (e == s)
03158                 return;
03159         ORDER_INT(s, e);
03160         hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
03161         dst = (LPSTR)GlobalLock(hdst);
03162         lstrcpynA(dst, es->text + s, e - s + 1);
03163         GlobalUnlock(hdst);
03164         OpenClipboard(hwnd);
03165         EmptyClipboard();
03166         SetClipboardData(CF_TEXT, hdst);
03167         CloseClipboard();
03168 }
03169 
03170 
03171 /*********************************************************************
03172  *
03173  *      WM_CREATE
03174  *
03175  */
03176 static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs)
03177 {
03178        /*
03179         *       To initialize some final structure members, we call some helper
03180         *       functions.  However, since the EDITSTATE is not consistent (i.e.
03181         *       not fully initialized), we should be very careful which
03182         *       functions can be called, and in what order.
03183         */
03184         EDIT_WM_SetFont(hwnd, es, 0, FALSE);
03185         EDIT_EM_EmptyUndoBuffer(hwnd, es);
03186        if (cs->lpszName && *(cs->lpszName) != '\0') {
03187            EDIT_EM_ReplaceSel(hwnd, es, FALSE, cs->lpszName);
03188            /* if we insert text to the editline, the text scrolls out
03189             * of the window, as the caret is placed after the insert
03190             * pos normally; thus we reset es->selection... to 0 and
03191             * update caret
03192             */
03193            es->selection_start = es->selection_end = 0;
03194            EDIT_EM_ScrollCaret(hwnd, es);
03195            if (es->flags & EF_UPDATE) {
03196                 es->flags &= ~EF_UPDATE;
03197                 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
03198            }
03199        }
03200        return 0;
03201 }
03202 
03203 
03204 /*********************************************************************
03205  *
03206  *      WM_DESTROY
03207  *
03208  */
03209 static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es)
03210 {
03211         if (!es) /* Protect against multiple destroy messages */
03212             return;
03213 
03214         if (es->hloc) {
03215                 while (LocalUnlock(es->hloc)) ;
03216                 LocalFree(es->hloc);
03217         }
03218 
03219         HeapDestroy(es->heap);
03220         HeapFree(GetProcessHeap(), 0, es);
03221         SetInfoPtr(hwnd,0);
03222 }
03223 
03224 
03225 /*********************************************************************
03226  *
03227  *      WM_ERASEBKGND
03228  *
03229  */
03230 static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc)
03231 {
03232         HBRUSH brush;
03233         RECT rc;
03234 
03235         HideCaret(hwnd);
03236 
03237         if (!es->bEnableState || (es->style & ES_READONLY))
03238                 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
03239         else
03240                 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, dc);
03241 
03242         if (!brush)
03243                 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
03244 
03245         GetClientRect(hwnd, &rc);
03246         IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
03247         GetClipBox(dc, &rc);
03248         /*
03249          *      FIXME:  specs say that we should UnrealizeObject() the brush,
03250          *              but the specs of UnrealizeObject() say that we shouldn't
03251          *              unrealize a stock object.  The default brush that
03252          *              DefWndProc() returns is ... a stock object.
03253          */
03254         FillRect(dc, &rc, brush);
03255 
03256         ShowCaret(hwnd);
03257 
03258         return -1;
03259 }
03260 
03261 
03262 /*********************************************************************
03263  *
03264  *      WM_GETTEXT
03265  *
03266  */
03267 static INT EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text)
03268 {
03269   INT len;
03270 
03271   if (es->text == NULL)  // the only case of failure i can imagine
03272     return 0;
03273 
03274   len = min(count, lstrlenA(es->text)+1); // determine length
03275   lstrcpynA(text, es->text, len);       // copy as much as possible
03276   return len;
03277 }
03278 
03279 
03280 /*********************************************************************
03281  *
03282  *      EDIT_HScroll_Hack
03283  *
03284  *      16 bit notepad needs this.  Actually it is not _our_ hack,
03285  *      it is notepad's.  Notepad is sending us scrollbar messages with
03286  *      undocumented parameters without us even having a scrollbar ... !?!?
03287  *
03288  */
03289 static LRESULT EDIT_HScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
03290 {
03291         INT dx = 0;
03292         INT fw = es->format_rect.right - es->format_rect.left;
03293         LRESULT ret = 0;
03294 
03295         if (!(es->flags & EF_HSCROLL_HACK)) {
03296                 //ERR_(edit)("hacked WM_HSCROLL handler invoked\n");
03297                 //ERR_(edit)("      if you are _not_ running 16 bit notepad, please report\n");
03298                 //ERR_(edit)("      (this message is only displayed once per edit control)\n");
03299                 es->flags |= EF_HSCROLL_HACK;
03300         }
03301 
03302         switch (action) {
03303         case SB_LINELEFT:
03304                 if (es->x_offset)
03305                         dx = -es->char_width;
03306                 break;
03307         case SB_LINERIGHT:
03308                 if (es->x_offset < es->text_width)
03309                         dx = es->char_width;
03310                 break;
03311         case SB_PAGELEFT:
03312                 if (es->x_offset)
03313                         dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
03314                 break;
03315         case SB_PAGERIGHT:
03316                 if (es->x_offset < es->text_width)
03317                         dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
03318                 break;
03319         case SB_LEFT:
03320                 if (es->x_offset)
03321                         dx = -es->x_offset;
03322                 break;
03323         case SB_RIGHT:
03324                 if (es->x_offset < es->text_width)
03325                         dx = es->text_width - es->x_offset;
03326                 break;
03327         case SB_THUMBTRACK:
03328                 es->flags |= EF_HSCROLL_TRACK;
03329                 dx = pos * es->text_width / 100 - es->x_offset;
03330                 break;
03331         case SB_THUMBPOSITION:
03332                 es->flags &= ~EF_HSCROLL_TRACK;
03333                 if (!(dx = pos * es->text_width / 100 - es->x_offset))
03334                         EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
03335                 break;
03336         case SB_ENDSCROLL:
03337                 break;
03338 
03339         default:
03340                 //ERR_(edit)("undocumented (hacked) WM_HSCROLL parameter, please report\n");
03341                 return 0;
03342         }
03343         if (dx)
03344                 EDIT_EM_LineScroll(hwnd, es, dx, 0);
03345         return ret;
03346 }
03347 
03348 
03349 /*********************************************************************
03350  *
03351  *      WM_HSCROLL
03352  *
03353  */
03354 static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
03355 {
03356         INT dx;
03357         INT fw;
03358 
03359         if (!(es->style & ES_MULTILINE))
03360                 return 0;
03361 
03362         if (!(es->style & ES_AUTOHSCROLL))
03363                 return 0;
03364 
03365         if (!(es->style & WS_HSCROLL))
03366                 return EDIT_HScroll_Hack(hwnd, es, action, pos, scroll_bar);
03367 
03368         dx = 0;
03369         fw = es->format_rect.right - es->format_rect.left;
03370         switch (action) {
03371         case SB_LINELEFT:
03372                 if (es->x_offset)
03373                         dx = -es->char_width;
03374                 break;
03375         case SB_LINERIGHT:
03376                 if (es->x_offset < es->text_width)
03377                         dx = es->char_width;
03378                 break;
03379         case SB_PAGELEFT:
03380                 if (es->x_offset)
03381                         dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
03382                 break;
03383         case SB_PAGERIGHT:
03384                 if (es->x_offset < es->text_width)
03385                         dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
03386                 break;
03387         case SB_LEFT:
03388                 if (es->x_offset)
03389                         dx = -es->x_offset;
03390                 break;
03391         case SB_RIGHT:
03392                 if (es->x_offset < es->text_width)
03393                         dx = es->text_width - es->x_offset;
03394                 break;
03395         case SB_THUMBTRACK:
03396                 es->flags |= EF_HSCROLL_TRACK;
03397                 dx = pos - es->x_offset;
03398                 break;
03399         case SB_THUMBPOSITION:
03400                 es->flags &= ~EF_HSCROLL_TRACK;
03401                 if (!(dx = pos - es->x_offset)) {
03402                         SetScrollPos(hwnd, SB_HORZ, pos, TRUE);
03403                         EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
03404                 }
03405                 break;
03406         case SB_ENDSCROLL:
03407                 break;
03408 
03409         default:
03410                 //ERR_(edit)("undocumented WM_HSCROLL parameter, please report\n");
03411                 return 0;
03412         }
03413         if (dx)
03414                 EDIT_EM_LineScroll(hwnd, es, dx, 0);
03415         return 0;
03416 }
03417 
03418 
03419 /*********************************************************************
03420  *
03421  *      EDIT_CheckCombo
03422  *
03423  */
03424 static BOOL EDIT_CheckCombo(HWND hwnd, EDITSTATE *es, UINT msg, INT key, DWORD key_data)
03425 {
03426    HWND hLBox = es->hwndListBox;
03427    HWND hCombo;
03428    BOOL bDropped;
03429    int  nEUI;
03430 
03431    if (!hLBox)
03432       return FALSE;
03433 
03434    hCombo   = GetParent(hwnd);
03435    bDropped = TRUE;
03436    nEUI     = 0;
03437 
03438    //TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
03439    //                        wnd->hwndSelf, (UINT16)msg, (UINT16)key);
03440 
03441    if (key == VK_UP || key == VK_DOWN)
03442    {
03443       if (SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0))
03444          nEUI = 1;
03445 
03446       if (msg == WM_KEYDOWN || nEUI)
03447           bDropped = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
03448    }
03449 
03450    switch (msg)
03451    {
03452       case WM_KEYDOWN:
03453          if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
03454          {
03455             /* make sure ComboLBox pops up */
03456             SendMessageA(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
03457             key = VK_F4;
03458             nEUI = 2;
03459          }
03460 
03461          SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
03462          break;
03463 
03464       case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
03465          if (nEUI)
03466             SendMessageA(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
03467          else
03468             SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
03469          break;
03470    }
03471 
03472    if(nEUI == 2)
03473       SendMessageA(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
03474 
03475    return TRUE;
03476 }
03477 
03478 /*********************************************************************
03479  *
03480  *      WM_KEYDOWN
03481  *
03482  *      Handling of special keys that don't produce a WM_CHAR
03483  *      (i.e. non-printable keys) & Backspace & Delete
03484  *
03485  */
03486 static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
03487 {
03488         BOOL shift;
03489         BOOL control;
03490 
03491         if (GetKeyState(VK_MENU) & 0x8000)
03492                 return 0;
03493 
03494         shift = GetKeyState(VK_SHIFT) & 0x8000;
03495         control = GetKeyState(VK_CONTROL) & 0x8000;
03496 
03497         switch (key) {
03498         case VK_F4:
03499         case VK_UP:
03500                 if (EDIT_CheckCombo(hwnd,es, WM_KEYDOWN, key, key_data) || key == VK_F4)
03501                         break;
03502                 /* fall through */
03503         case VK_LEFT:
03504                 if ((es->style & ES_MULTILINE) && (key == VK_UP))
03505                         EDIT_MoveUp_ML(hwnd, es, shift);
03506                 else
03507                         if (control)
03508                                 EDIT_MoveWordBackward(hwnd, es, shift);
03509                         else
03510                                 EDIT_MoveBackward(hwnd, es, shift);
03511                 break;
03512         case VK_DOWN:
03513                 if (EDIT_CheckCombo(hwnd,es, WM_KEYDOWN, key, key_data))
03514                         break;
03515                 /* fall through */
03516         case VK_RIGHT:
03517                 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
03518                         EDIT_MoveDown_ML(hwnd, es, shift);
03519                 else if (control)
03520                         EDIT_MoveWordForward(hwnd, es, shift);
03521                 else
03522                         EDIT_MoveForward(hwnd, es, shift);
03523                 break;
03524         case VK_HOME:
03525                 EDIT_MoveHome(hwnd, es, shift);
03526                 break;
03527         case VK_END:
03528                 EDIT_MoveEnd(hwnd, es, shift);
03529                 break;
03530         case VK_PRIOR:
03531                 if (es->style & ES_MULTILINE)
03532                         EDIT_MovePageUp_ML(hwnd, es, shift);
03533                 else
03534                         EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key, key_data);
03535                 break;
03536         case VK_NEXT:
03537                 if (es->style & ES_MULTILINE)
03538                         EDIT_MovePageDown_ML(hwnd, es, shift);
03539                 else
03540                         EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key, key_data);
03541                 break;
03542         case VK_DELETE:
03543                 if (!(es->style & ES_READONLY) && !(shift && control)) {
03544                         if (es->selection_start != es->selection_end) {
03545                                 if (shift)
03546                                         EDIT_WM_Cut(hwnd, es);
03547                                 else
03548                                         EDIT_WM_Clear(hwnd, es);
03549                         } else {
03550                                 if (shift) {
03551                                         /* delete character left of caret */
03552                                         EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
03553                                         EDIT_MoveBackward(hwnd, es, TRUE);
03554                                         EDIT_WM_Clear(hwnd, es);
03555                                 } else if (control) {
03556                                         /* delete to end of line */
03557                                         EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
03558                                         EDIT_MoveEnd(hwnd, es, TRUE);
03559                                         EDIT_WM_Clear(hwnd, es);
03560                                 } else {
03561                                         /* delete character right of caret */
03562                                         EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
03563                                         EDIT_MoveForward(hwnd, es, TRUE);
03564                                         EDIT_WM_Clear(hwnd, es);
03565                                 }
03566                         }
03567                 }
03568                 break;
03569         case VK_INSERT:
03570                 if (shift) {
03571                         if (!(es->style & ES_READONLY))
03572                                 EDIT_WM_Paste(hwnd, es);
03573                 } else if (control)
03574                         EDIT_WM_Copy(hwnd, es);
03575                 break;
03576         case VK_RETURN:
03577             /* If the edit doesn't want the return send a message to the default object */
03578             if(!(es->style & ES_WANTRETURN))
03579             {
03580                 HWND hwndParent = GetParent(hwnd);
03581                 DWORD dw = SendMessageA( hwndParent, DM_GETDEFID, 0, 0 );
03582                 if (HIWORD(dw) == DC_HASDEFID)
03583                 {
03584                     SendMessageA( hwndParent, WM_COMMAND,
03585                                   MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
03586                               (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
03587                 }
03588             }
03589             break;
03590         }
03591         return 0;
03592 }
03593 
03594 
03595 /*********************************************************************
03596  *
03597  *      WM_KILLFOCUS
03598  *
03599  */
03600 static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus)
03601 {
03602         es->flags &= ~EF_FOCUSED;
03603         DestroyCaret();
03604         if(!(es->style & ES_NOHIDESEL))
03605                 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
03606         EDIT_NOTIFY_PARENT(hwnd, EN_KILLFOCUS);
03607         return 0;
03608 }
03609 
03610 
03611 /*********************************************************************
03612  *
03613  *      WM_LBUTTONDBLCLK
03614  *
03615  *      The caret position has been set on the WM_LBUTTONDOWN message
03616  *
03617  */
03618 static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
03619 {
03620         INT s;
03621         INT e = es->selection_end;
03622         INT l;
03623         INT li;
03624         INT ll;
03625 
03626         if (!(es->flags & EF_FOCUSED))
03627                 return 0;
03628 
03629         l = EDIT_EM_LineFromChar(hwnd, es, e);
03630         li = EDIT_EM_LineIndex(hwnd, es, l);
03631         ll = EDIT_EM_LineLength(hwnd, es, e);
03632         s = li + EDIT_CallWordBreakProc (hwnd, es, li, e - li, ll, WB_LEFT);
03633         e = li + EDIT_CallWordBreakProc(hwnd, es, li, e - li, ll, WB_RIGHT);
03634         EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
03635         EDIT_EM_ScrollCaret(hwnd, es);
03636         return 0;
03637 }
03638 
03639 
03640 /*********************************************************************
03641  *
03642  *      WM_LBUTTONDOWN
03643  *
03644  */
03645 static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
03646 {
03647         INT e;
03648         BOOL after_wrap;
03649 
03650         if (!(es->flags & EF_FOCUSED))
03651           SetFocus(hwnd);
03652 
03653         es->bCaptureState = TRUE;
03654         SetCapture(hwnd);
03655         EDIT_ConfinePoint(hwnd, es, &x, &y);
03656         e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
03657         EDIT_EM_SetSel(hwnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
03658         EDIT_EM_ScrollCaret(hwnd, es);
03659         es->region_posx = es->region_posy = 0;
03660         SetTimer(hwnd, 0, 100, NULL);
03661         return 0;
03662 }
03663 
03664 
03665 /*********************************************************************
03666  *
03667  *      WM_LBUTTONUP
03668  *
03669  */
03670 static LRESULT EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
03671 {
03672   if (es->bCaptureState)
03673   {
03674     KillTimer(hwnd,0);
03675     ReleaseCapture();
03676   }
03677   es->bCaptureState = FALSE;
03678 
03679   return 0;
03680 }
03681 
03682 static LRESULT EDIT_WM_CaptureChanged(HWND hwnd,EDITSTATE *es)
03683 {
03684   if (es->bCaptureState) KillTimer(hwnd,0);
03685   es->bCaptureState = FALSE;
03686 
03687   return 0;
03688 }
03689 
03690 /*********************************************************************
03691  *
03692  *      WM_MOUSEMOVE
03693  *
03694  */
03695 static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
03696 {
03697         INT e;
03698         BOOL after_wrap;
03699         INT prex, prey;
03700 
03701         if (!es->bCaptureState) return 0;
03702 
03703         /*
03704          *      FIXME: gotta do some scrolling if outside client
03705          *              area.  Maybe reset the timer ?
03706          */
03707         prex = x; prey = y;
03708         EDIT_ConfinePoint(hwnd, es, &x, &y);
03709         es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
03710         es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
03711         e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
03712         EDIT_EM_SetSel(hwnd, es, es->selection_start, e, after_wrap);
03713 
03714         return 0;
03715 }
03716 
03717 
03718 /*********************************************************************
03719  *
03720  *      WM_NCCREATE
03721  *
03722  */
03723 static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs)
03724 {
03725         EDITSTATE *es;
03726 
03727         if (!(es = (EDITSTATE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
03728                 return FALSE;
03729         SetInfoPtr(hwnd,(DWORD)es);
03730 
03731        /*
03732         *      Note: since the EDITSTATE has not been fully initialized yet,
03733         *            we can't use any API calls that may send
03734         *            WM_XXX messages before WM_NCCREATE is completed.
03735         */
03736 
03737         if (!(es->heap = HeapCreate(0, 0x10000, 0)))
03738                 return FALSE;
03739         es->style = cs->style;
03740 
03741         es->bEnableState = !(cs->style & WS_DISABLED);
03742 
03743         /*
03744          * In Win95 look and feel, the WS_BORDER style is replaced by the
03745          * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
03746          * control a non client area.
03747          */
03748         if (es->style & WS_BORDER)
03749         {
03750           es->style      &= ~WS_BORDER;
03751           SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~WS_BORDER);
03752           SetWindowLongA(hwnd,GWL_EXSTYLE,GetWindowLongA(hwnd,GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
03753         }
03754 
03755         if (es->style & ES_COMBO)
03756            es->hwndListBox = GetDlgItem(cs->hwndParent, ID_CB_LISTBOX);
03757 
03758         if (es->style & ES_MULTILINE) {
03759                 es->buffer_size = BUFSTART_MULTI;
03760                 es->buffer_limit = BUFLIMIT_MULTI;
03761                 if (es->style & WS_VSCROLL)
03762                         es->style |= ES_AUTOVSCROLL;
03763                 if (es->style & WS_HSCROLL)
03764                         es->style |= ES_AUTOHSCROLL;
03765                 es->style &= ~ES_PASSWORD;
03766                 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
03767                         if (es->style & ES_RIGHT)
03768                                 es->style &= ~ES_CENTER;
03769                         es->style &= ~WS_HSCROLL;
03770                         es->style &= ~ES_AUTOHSCROLL;
03771                 }
03772 
03773                 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
03774                 es->style |= ES_AUTOVSCROLL;
03775         } else {
03776                 es->buffer_size = BUFSTART_SINGLE;
03777                 es->buffer_limit = BUFLIMIT_SINGLE;
03778                 es->style &= ~ES_CENTER;
03779                 es->style &= ~ES_RIGHT;
03780                 es->style &= ~WS_HSCROLL;
03781                 es->style &= ~WS_VSCROLL;
03782                 es->style &= ~ES_AUTOVSCROLL;
03783                 es->style &= ~ES_WANTRETURN;
03784                 if (es->style & ES_UPPERCASE) {
03785                         es->style &= ~ES_LOWERCASE;
03786                         es->style &= ~ES_NUMBER;
03787                 } else if (es->style & ES_LOWERCASE)
03788                         es->style &= ~ES_NUMBER;
03789                 if (es->style & ES_PASSWORD)
03790                         es->password_char = '*';
03791 
03792                 /* FIXME: for now, all single line controls are AUTOHSCROLL */
03793                 es->style |= ES_AUTOHSCROLL;
03794         }
03795         if (!(es->text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
03796                 return FALSE;
03797         es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
03798         if (!(es->undo_text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
03799                 return FALSE;
03800         es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
03801         *es->text = '\0';
03802         if (es->style & ES_MULTILINE)
03803                 if (!(es->first_line_def = (LINEDEF*)HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
03804                         return FALSE;
03805         es->line_count = 1;
03806 
03807         return TRUE;
03808 }
03809 
03810 static VOID EDIT_Draw(HWND hwnd,EDITSTATE *es,HDC hdc,BOOL eraseBkGnd)
03811 {
03812   INT i;
03813   HFONT old_font = 0;
03814   RECT rc;
03815   RECT rcLine;
03816   RECT rcRgn;
03817   BOOL rev = es->bEnableState && ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL));
03818 
03819   HideCaret(hwnd);
03820 
03821   if (eraseBkGnd)
03822   {
03823     HBRUSH brush;
03824 
03825     if (!es->bEnableState || (es->style & ES_READONLY))
03826       brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
03827     else
03828       brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, hdc);
03829 
03830     if (!brush)
03831       brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
03832 
03833     GetClientRect(hwnd, &rc);
03834     /*
03835      *      FIXME:  specs say that we should UnrealizeObject() the brush,
03836      *              but the specs of UnrealizeObject() say that we shouldn't
03837      *              unrealize a stock object.  The default brush that
03838      *              DefWndProc() returns is ... a stock object.
03839      */
03840     FillRect(hdc, &rc, brush);
03841   }
03842 
03843   if(es->style & WS_BORDER)
03844   {
03845     GetClientRect(hwnd, &rc);
03846     if(es->style & ES_MULTILINE)
03847     {
03848       if(es->style & WS_HSCROLL) rc.bottom++;
03849       if(es->style & WS_VSCROLL) rc.right++;
03850     }
03851     Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
03852   }
03853 
03854   IntersectClipRect(hdc, es->format_rect.left,
03855                         es->format_rect.top,
03856                         es->format_rect.right,
03857                         es->format_rect.bottom);
03858   if (es->style & ES_MULTILINE)
03859   {
03860     GetClientRect(hwnd, &rc);
03861     IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
03862   }
03863   if (es->font) old_font = SelectObject(hdc, es->font);
03864   if (!es->bEnableState || (es->style & ES_READONLY))
03865     EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
03866   else
03867     EDIT_SEND_CTLCOLOR(hwnd, hdc);
03868   if (!es->bEnableState)
03869     SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
03870   GetClipBox(hdc, &rcRgn);
03871   if (es->style & ES_MULTILINE)
03872   {
03873     INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
03874 
03875     for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++)
03876     {
03877       EDIT_GetLineRect(hwnd,hdc, es, i, 0, -1, &rcLine);
03878       if (IntersectRect(&rc, &rcRgn, &rcLine))
03879         EDIT_PaintLine(hwnd, es, hdc, i, rev);
03880     }
03881   } else
03882   {
03883     EDIT_GetLineRect(hwnd,hdc, es, 0, 0, -1, &rcLine);
03884     if (IntersectRect(&rc, &rcRgn, &rcLine))
03885       EDIT_PaintLine(hwnd, es, hdc, 0, rev);
03886   }
03887   if (es->font) SelectObject(hdc, old_font);
03888   if (es->flags & EF_FOCUSED)
03889     EDIT_SetCaretPos(hwnd, es, es->selection_end,es->flags & EF_AFTER_WRAP);
03890 
03891   ShowCaret(hwnd);
03892 }
03893 
03894 static VOID EDIT_Refresh(HWND hwnd,EDITSTATE *es,BOOL useCache)
03895 {
03896   HDC hdc,hdcCompatible;
03897   HBITMAP bitmap,oldbmp;
03898   RECT rect;
03899 
03900   if (es->flags & EF_UPDATE)
03901   {
03902     //CB: don't reset flag or EN_CHANGE is lost!
03903     //es->flags &= ~EF_UPDATE;
03904     EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
03905   }
03906 
03907   if (!IsWindowVisible(hwnd)) return;
03908 
03909   if (!useCache)
03910   {
03911     InvalidateRect(hwnd,NULL,TRUE);
03912     return;
03913   }
03914 
03915   //CB: original control redraws many times, cache drawing
03916   HideCaret(hwnd);
03917   GetClientRect(hwnd,&rect);
03918   hdc = GetDC(hwnd);
03919   hdcCompatible = CreateCompatibleDC(hdc);
03920   bitmap = CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
03921   oldbmp = SelectObject(hdcCompatible,bitmap);
03922   EDIT_Draw(hwnd,es,hdcCompatible,TRUE);
03923   SelectClipRgn(hdcCompatible,0);
03924   BitBlt(hdc,0,0,rect.right,rect.bottom,hdcCompatible,0,0,SRCCOPY);
03925   SelectObject(hdcCompatible,oldbmp);
03926   DeleteObject(bitmap);
03927   DeleteDC(hdcCompatible);
03928   ReleaseDC(hwnd,hdc);
03929   ShowCaret(hwnd);
03930 }
03931 
03932 /*********************************************************************
03933  *
03934  *      WM_PAINT
03935  *
03936  */
03937 static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam)
03938 {
03939   PAINTSTRUCT ps;
03940   HDC hdc;
03941 
03942   if (!wParam) hdc = BeginPaint(hwnd, &ps);
03943   else hdc = (HDC) wParam;
03944 
03945   EDIT_Draw(hwnd,es,hdc,FALSE);
03946 
03947   if (!wParam) EndPaint(hwnd, &ps);
03948 }
03949 
03950 
03951 /*********************************************************************
03952  *
03953  *      WM_PASTE
03954  *
03955  */
03956 static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es)
03957 {
03958         HGLOBAL hsrc;
03959         LPSTR src;
03960 
03961         OpenClipboard(hwnd);
03962         hsrc = GetClipboardData(CF_TEXT);
03963         if (hsrc) {
03964                 src = (LPSTR)GlobalLock(hsrc);
03965                 EDIT_EM_ReplaceSel(hwnd, es, TRUE, src);
03966                 GlobalUnlock(hsrc);
03967 
03968                 if (es->flags & EF_UPDATE) {
03969                         es->flags &= ~EF_UPDATE;
03970                         EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
03971                 }
03972         }
03973         CloseClipboard();
03974 }
03975 
03976 
03977 /*********************************************************************
03978  *
03979  *      WM_SETFOCUS
03980  *
03981  */
03982 static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus)
03983 {
03984         es->flags |= EF_FOCUSED;
03985         CreateCaret(hwnd, 0, 2, es->line_height);
03986         EDIT_SetCaretPos(hwnd, es, es->selection_end,
03987                          es->flags & EF_AFTER_WRAP);
03988         if(!(es->style & ES_NOHIDESEL))
03989                 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
03990         ShowCaret(hwnd);
03991         EDIT_NOTIFY_PARENT(hwnd, EN_SETFOCUS);
03992 }
03993 
03994 /*********************************************************************
03995  *
03996  *      WM_SETFONT
03997  *
03998  * With Win95 look the margins are set to default font value unless
03999  * the system font (font == 0) is being set, in which case they are left
04000  * unchanged.
04001  *
04002  */
04003 static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw)
04004 {
04005         TEXTMETRICA tm;
04006         HDC dc;
04007         HFONT old_font = 0;
04008         RECT r;
04009 
04010         dc = GetDC(hwnd);
04011         if (font)
04012                 old_font = SelectObject(dc, font);
04013         if (!GetTextMetricsA(dc, &tm))
04014         {
04015           if (font) SelectObject(dc,old_font);
04016           ReleaseDC(hwnd,dc);
04017 
04018           return;
04019         }
04020         es->font = font;
04021         es->line_height = tm.tmHeight;
04022 
04023         es->char_width = tm.tmAveCharWidth;
04024         if (font)
04025                 SelectObject(dc, old_font);
04026         ReleaseDC(hwnd, dc);
04027         if (font)
04028                 EDIT_EM_SetMargins(hwnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
04029                                    EC_USEFONTINFO, EC_USEFONTINFO);
04030         /* Force the recalculation of the format rect for each font change */
04031         GetClientRect(hwnd, &r);
04032         EDIT_SetRectNP(hwnd, es, &r);
04033         if (es->style & ES_MULTILINE)
04034                 EDIT_BuildLineDefs_ML(hwnd,es);
04035 
04036         if (redraw)
04037                 EDIT_Refresh(hwnd,es,FALSE);
04038         if (es->flags & EF_FOCUSED) {
04039                 DestroyCaret();
04040                 CreateCaret(hwnd, 0, 2, es->line_height);
04041                 EDIT_SetCaretPos(hwnd, es, es->selection_end,
04042                                  es->flags & EF_AFTER_WRAP);
04043                 ShowCaret(hwnd);
04044         }
04045 }
04046 
04047 
04048 /*********************************************************************
04049  *
04050  *      WM_SETTEXT
04051  *
04052  * NOTES
04053  *  For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
04054  *  The modified flag is reset. No notifications are sent.
04055  *
04056  *  For single-line controls, reception of WM_SETTEXT triggers:
04057  *  The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
04058  *
04059  */
04060 static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text)
04061 {
04062 #ifdef __WIN32OS2__
04063         //SvL: Acrobat Distiller keeps on sending WM_SETTEXT with the same
04064         //     string in responds to a WM_COMMAND with EN_UPDATE -> stack
04065         //     overflow (TODO: Need to check if this behaviour is correct compared to NT)
04066         if(text && es->text) {
04067                 if(!strcmp(es->text, text)) {
04068                         return;
04069                 }
04070         }
04071 #endif
04072         es->selection_start = 0;
04073         es->selection_end = lstrlenA(es->text);
04074         if (es->flags & EF_FOCUSED)
04075           EDIT_SetCaretPos(hwnd, es, es->selection_end, FALSE);
04076 
04077         if (text) {
04078                 //TRACE_(edit)("\t'%s'\n", text);
04079                 EDIT_EM_ReplaceSel(hwnd, es, FALSE, text);
04080         } else {
04081                 //TRACE_(edit)("\t<NULL>\n");
04082                 EDIT_EM_ReplaceSel(hwnd, es, FALSE, "");
04083         }
04084         es->x_offset = 0;
04085         if (es->style & ES_MULTILINE) {
04086                 es->flags &= ~EF_UPDATE;
04087         } else {
04088                 es->flags |= EF_UPDATE;
04089         }
04090         es->flags &= ~EF_MODIFIED;
04091         EDIT_EM_SetSel(hwnd, es, 0, 0, FALSE);
04092         EDIT_EM_ScrollCaret(hwnd, es);
04093         EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
04094         if (es->flags & EF_UPDATE)
04095         {
04096           EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
04097           /* EN_CHANGE notification need to be sent too
04098              Windows doc says it's only done after the display,
04099              the doc is WRONG. EN_CHANGE notification is sent
04100              while processing WM_SETTEXT */
04101           EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
04102           es->flags &= ~EF_UPDATE;
04103         }
04104 }
04105 
04106 
04107 /*********************************************************************
04108  *
04109  *      WM_SIZE
04110  *
04111  */
04112 static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height)
04113 {
04114         if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
04115                 RECT rc;
04116                 SetRect(&rc, 0, 0, width, height);
04117                 EDIT_SetRectNP(hwnd, es, &rc);
04118                 EDIT_Refresh(hwnd,es,FALSE);
04119         }
04120 }
04121 
04122 
04123 /*********************************************************************
04124  *
04125  *      WM_SYSKEYDOWN
04126  *
04127  */
04128 static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
04129 {
04130         if ((key == VK_BACK) && (key_data & 0x2000)) {
04131                 if (EDIT_EM_CanUndo(hwnd, es))
04132                         EDIT_EM_Undo(hwnd, es);
04133                 return 0;
04134         } else if ((key == VK_UP) || (key == VK_DOWN))
04135         {
04136                 if (EDIT_CheckCombo(hwnd,es, WM_SYSKEYDOWN, key, key_data))
04137                         return 0;
04138          }
04139         return DefWindowProcA(hwnd, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
04140 }
04141 
04142 
04143 /*********************************************************************
04144  *
04145  *      WM_TIMER
04146  *
04147  */
04148 static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
04149 {
04150         if (es->region_posx < 0) {
04151                 EDIT_MoveBackward(hwnd, es, TRUE);
04152         } else if (es->region_posx > 0) {
04153                 EDIT_MoveForward(hwnd, es, TRUE);
04154         }
04155 
04156         if (!(es->style & ES_MULTILINE)) return;
04157 
04158         if (es->region_posy < 0)
04159         {
04160           EDIT_MoveUp_ML(hwnd,es,TRUE);
04161         } else if (es->region_posy > 0)
04162         {
04163           EDIT_MoveDown_ML(hwnd,es,TRUE);
04164         }
04165 }
04166 
04167 
04168 /*********************************************************************
04169  *
04170  *      EDIT_VScroll_Hack
04171  *
04172  *      16 bit notepad needs this.  Actually it is not _our_ hack,
04173  *      it is notepad's.  Notepad is sending us scrollbar messages with
04174  *      undocumented parameters without us even having a scrollbar ... !?!?
04175  *
04176  */
04177 static LRESULT EDIT_VScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
04178 {
04179         INT dy = 0;
04180         LRESULT ret = 0;
04181 
04182         if (!(es->flags & EF_VSCROLL_HACK)) {
04183                 //ERR_(edit)("hacked WM_VSCROLL handler invoked\n");
04184                 //ERR_(edit)("      if you are _not_ running 16 bit notepad, please report\n");
04185                 //ERR_(edit)("      (this message is only displayed once per edit control)\n");
04186                 es->flags |= EF_VSCROLL_HACK;
04187         }
04188 
04189         switch (action) {
04190         case SB_LINEUP:
04191         case SB_LINEDOWN:
04192         case SB_PAGEUP:
04193         case SB_PAGEDOWN:
04194                 EDIT_EM_Scroll(hwnd, es, action);
04195                 return 0;
04196         case SB_TOP:
04197                 dy = -es->y_offset;
04198                 break;
04199         case SB_BOTTOM:
04200                 dy = es->line_count - 1 - es->y_offset;
04201                 break;
04202         case SB_THUMBTRACK:
04203                 es->flags |= EF_VSCROLL_TRACK;
04204                 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
04205                 break;
04206         case SB_THUMBPOSITION:
04207                 es->flags &= ~EF_VSCROLL_TRACK;
04208                 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
04209                         EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
04210                 break;
04211         case SB_ENDSCROLL:
04212                 break;
04213 
04214         default:
04215                 //ERR_(edit)("undocumented (hacked) WM_VSCROLL parameter, please report\n");
04216                 return 0;
04217         }
04218         if (dy)
04219                 EDIT_EM_LineScroll(hwnd, es, 0, dy);
04220         return ret;
04221 }
04222 
04223 
04224 /*********************************************************************
04225  *
04226  *      WM_VSCROLL
04227  *
04228  */
04229 static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
04230 {
04231         INT dy;
04232 
04233         if (!(es->style & ES_MULTILINE))
04234                 return 0;
04235 
04236         if (!(es->style & ES_AUTOVSCROLL))
04237                 return 0;
04238 
04239         if (!(es->style & WS_VSCROLL))
04240                 return EDIT_VScroll_Hack(hwnd, es, action, pos, scroll_bar);
04241 
04242         dy = 0;
04243         switch (action) {
04244         case SB_LINEUP:
04245         case SB_LINEDOWN:
04246         case SB_PAGEUP:
04247         case SB_PAGEDOWN:
04248                 EDIT_EM_Scroll(hwnd, es, action);
04249                 return 0;
04250 
04251         case SB_TOP:
04252                 dy = -es->y_offset;
04253                 break;
04254         case SB_BOTTOM:
04255                 dy = es->line_count - 1 - es->y_offset;
04256                 break;
04257         case SB_THUMBTRACK:
04258                 es->flags |= EF_VSCROLL_TRACK;
04259                 dy = pos - es->y_offset;
04260                 break;
04261         case SB_THUMBPOSITION:
04262                 es->flags &= ~EF_VSCROLL_TRACK;
04263                 if (!(dy = pos - es->y_offset)) {
04264                         SetScrollPos(hwnd, SB_VERT, pos, TRUE);
04265                         EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
04266                 }
04267                 break;
04268         case SB_ENDSCROLL:
04269                 break;
04270 
04271         default:
04272                 //ERR_(edit)("undocumented WM_VSCROLL action %d, please report\n",
04273                 //        action);
04274                 return 0;
04275         }
04276         if (dy)
04277                 EDIT_EM_LineScroll(hwnd, es, 0, dy);
04278         return 0;
04279 }
04280 
04281 static LRESULT EDIT_WM_MouseWheel(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
04282 {
04283   short gcWheelDelta = 0;
04284   UINT pulScrollLines = 3;
04285   SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
04286 
04287   if (wParam & (MK_SHIFT | MK_CONTROL))
04288     return DefWindowProcA(hwnd,WM_MOUSEWHEEL, wParam, lParam);
04289   gcWheelDelta -= (short) HIWORD(wParam);
04290   if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
04291   {
04292     int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
04293 
04294     cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
04295     return EDIT_EM_LineScroll(hwnd, es, 0, cLineScroll);
04296   }
04297 
04298   return 0;
04299 }
04300 
04301 BOOL EDIT_Register()
04302 {
04303     WNDCLASSA wndClass;
04304 
04305 //SvL: Don't check this now
04306 //    if (GlobalFindAtomA(EDITCLASSNAME)) return FALSE;
04307 
04308     ZeroMemory(&wndClass,sizeof(WNDCLASSA));
04309     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
04310     wndClass.lpfnWndProc   = (WNDPROC)EditWndProc;
04311     wndClass.cbClsExtra    = 0;
04312     wndClass.cbWndExtra    = sizeof(VOID*);
04313     wndClass.hCursor       = LoadCursorA(0,IDC_IBEAMA);
04314     wndClass.hbrBackground = (HBRUSH)0;
04315     wndClass.lpszClassName = EDITCLASSNAME;
04316 
04317     return RegisterClassA(&wndClass);
04318 }
04319 
04320 BOOL EDIT_Unregister()
04321 {
04322     if (GlobalFindAtomA(EDITCLASSNAME))
04323         return UnregisterClassA(EDITCLASSNAME,(HINSTANCE)NULL);
04324     else return FALSE;
04325 }

Generated on Wed Jan 23 23:17:32 2002 for ODIN-user32 by doxygen1.2.11.1 written by Dimitri van Heesch, © 1997-2001