Release 940614
[wine] / controls / edit.c
1 /*
2  * Edit control
3  *
4  * Copyright  David W. Metcalfe, 1994
5  *
6  * Release 1, April 1994
7  */
8
9 static char Copyright[] = "Copyright  David W. Metcalfe, 1994";
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include <windows.h>
14 #include "win.h"
15 #include "class.h"
16 #include "user.h"
17
18 #define DEBUG_EDIT /* */
19
20 #define NOTIFY_PARENT(hWndCntrl, wNotifyCode) \
21         SendMessage(GetParent(hWndCntrl), WM_COMMAND, \
22                  GetDlgCtrlID(hWndCntrl), MAKELPARAM(hWndCntrl, wNotifyCode));
23
24 #define MAXTEXTLEN 32000   /* maximum text buffer length */
25 #define EDITLEN     1024   /* starting length for multi-line control */
26 #define ENTRYLEN     256   /* starting length for single line control */
27 #define GROWLENGTH    64   /* buffers grow by this much */
28
29 #define HSCROLLDIM (ClientWidth(wndPtr) / 3)
30                            /* "line" dimension for horizontal scroll */
31
32 #define EDIT_HEAP_ALLOC(size)          USER_HEAP_ALLOC(GMEM_MOVEABLE,size)
33 #define EDIT_HEAP_REALLOC(handle,size) USER_HEAP_REALLOC(handle,size,\
34                                                          GMEM_MOVEABLE)
35 #define EDIT_HEAP_ADDR(handle)         USER_HEAP_ADDR(handle)
36 #define EDIT_HEAP_FREE(handle)         USER_HEAP_FREE(handle)
37
38 typedef struct
39 {
40     int wlines;              /* number of lines of text */
41     int wtop;                /* top line that is displayed */
42     int wleft;               /* left pixel that is displayed */
43     unsigned int textlen;    /* text buffer length */
44     int textwidth;           /* width of longest line in pixels */
45     RECT fmtrc;              /* rectangle in which to format text */
46     int txtht;               /* height of text line in pixels */
47     HANDLE hText;            /* handle to text buffer */
48     HANDLE hCharWidths;      /* widths of chars in font */
49     HANDLE hTextPtrs;        /* list of line offsets */
50     HANDLE hBlankLine;       /* to fill blank lines quickly */
51     int CurrCol;             /* current column */
52     int CurrLine;            /* current line */
53     int WndCol;              /* current window column */
54     int WndRow;              /* current window row */
55     BOOL TextChanged;        /* TRUE if text has changed */
56     BOOL PaintBkgd;          /* paint control background */
57     unsigned int MaxTextLen; /* maximum text buffer length */
58     int SelBegLine;          /* beginning line of selection */
59     int SelBegCol;           /* beginning column of selection */
60     int SelEndLine;          /* ending line of selection */
61     int SelEndCol;           /* ending column of selection */
62 } EDITSTATE;
63
64
65 #define ClientWidth(wndPtr) (wndPtr->rectClient.right - \
66                              wndPtr->rectClient.left)
67 #define ClientHeight(wndPtr, es) ((wndPtr->rectClient.bottom - \
68                                    wndPtr->rectClient.top) / es->txtht)
69 #define EditBufLen(wndPtr) (wndPtr->dwStyle & ES_MULTILINE \
70                             ? EDITLEN : ENTRYLEN)
71 #define CurrChar (EDIT_TextLine(hwnd, es->CurrLine) + es->CurrCol)
72 #define SelMarked(es) (es->SelBegLine != -1 && es->SelBegCol != -1 && \
73                        es->SelEndLine != -1 && es->SelEndCol != -1)
74
75 /* macros to access window styles */
76 #define IsAutoVScroll() (wndPtr->dwStyle & ES_AUTOVSCROLL)
77 #define IsAutoHScroll() (wndPtr->dwStyle & ES_AUTOHSCROLL)
78 #define IsMultiLine() (wndPtr->dwStyle & ES_MULTILINE)
79 #define IsVScrollBar() (wndPtr->dwStyle & WS_VSCROLL)
80 #define IsHScrollBar() (wndPtr->dwStyle & WS_HSCROLL)
81
82 /* internal variables */
83 static BOOL TextMarking;         /* TRUE if text marking in progress */
84 static BOOL ButtonDown;          /* TRUE if left mouse button down */
85 static int ButtonRow;              /* row in text buffer when button pressed */
86 static int ButtonCol;              /* col in text buffer when button pressed */
87 static BOOL Print = FALSE;
88
89
90 LONG EditWndProc(HWND hWnd, WORD uMsg, WORD wParam, LONG lParam);
91 long EDIT_CreateMsg(HWND hwnd, LONG lParam);
92 void EDIT_ClearTextPointers(HWND hwnd);
93 void EDIT_BuildTextPointers(HWND hwnd);
94 void EDIT_ModTextPointers(HWND hwnd, int lineno, int var);
95 void EDIT_PaintMsg(HWND hwnd);
96 HANDLE EDIT_GetTextLine(HWND hwnd, int selection);
97 char *EDIT_TextLine(HWND hwnd, int sel);
98 int EDIT_LineLength(EDITSTATE *es, char *str, int len);
99 void EDIT_WriteTextLine(HWND hwnd, RECT *rc, int y);
100 void EDIT_WriteText(HWND hwnd, char *lp, int off, int len, int row, 
101                     int col, RECT *rc, BOOL blank, BOOL reverse);
102 HANDLE EDIT_GetStr(EDITSTATE *es, char *lp, int off, int len, int *diff);
103 void EDIT_CharMsg(HWND hwnd, WORD wParam);
104 void EDIT_KeyTyped(HWND hwnd, short ch);
105 int EDIT_CharWidth(EDITSTATE *es, short ch);
106 void EDIT_Forward(HWND hwnd);
107 void EDIT_Downward(HWND hwnd);
108 void EDIT_Upward(HWND hwnd);
109 void EDIT_Backward(HWND hwnd);
110 void EDIT_End(HWND hwnd);
111 void EDIT_Home(HWND hwnd);
112 void EDIT_StickEnd(HWND hwnd);
113 void EDIT_KeyDownMsg(HWND hwnd, WORD wParam);
114 void EDIT_KeyHScroll(HWND hwnd, WORD opt);
115 void EDIT_KeyVScrollLine(HWND hwnd, WORD opt);
116 void EDIT_KeyVScrollPage(HWND hwnd, WORD opt);
117 void EDIT_KeyVScrollDoc(HWND hwnd, WORD opt);
118 int EDIT_ComputeVScrollPos(HWND hwnd);
119 int EDIT_ComputeHScrollPos(HWND hwnd);
120 void EDIT_DelKey(HWND hwnd);
121 void EDIT_VScrollMsg(HWND hwnd, WORD wParam, LONG lParam);
122 void EDIT_VScrollLine(HWND hwnd, WORD opt);
123 void EDIT_VScrollPage(HWND hwnd, WORD opt);
124 void EDIT_HScrollMsg(HWND hwnd, WORD wParam, LONG lParam);
125 void EDIT_SizeMsg(HWND hwnd, WORD wParam, LONG lParam);
126 void EDIT_LButtonDownMsg(HWND hwnd, WORD wParam, LONG lParam);
127 void EDIT_MouseMoveMsg(HWND hwnd, WORD wParam, LONG lParam);
128 int EDIT_PixelToChar(HWND hwnd, int row, int *pixel);
129 LONG EDIT_SetTextMsg(HWND hwnd, LONG lParam);
130 void EDIT_ClearText(HWND hwnd);
131 void EDIT_SetSelMsg(HWND hwnd, LONG lParam);
132 void EDIT_GetLineCol(HWND hwnd, int off, int *line, int *col);
133 void EDIT_DeleteSel(HWND hwnd);
134 void EDIT_ClearSel(HWND hwnd);
135 int EDIT_TextLineNumber(HWND hwnd, char *lp);
136 void EDIT_SetAnchor(HWND hwnd, int row, int col);
137 void EDIT_ExtendSel(HWND hwnd, int x, int y);
138 void EDIT_StopMarking(HWND hwnd);
139 LONG EDIT_GetLineMsg(HWND hwnd, WORD wParam, LONG lParam);
140 LONG EDIT_GetSelMsg(HWND hwnd);
141 LONG EDIT_LineFromCharMsg(HWND hwnd, WORD wParam);
142 LONG EDIT_LineIndexMsg(HWND hwnd, WORD wParam);
143 void swap(int *a, int *b);
144
145
146 LONG EditWndProc(HWND hwnd, WORD uMsg, WORD wParam, LONG lParam)
147 {
148     LONG lResult = 0L;
149     HDC hdc;
150     char *textPtr;
151     int len;
152     WND *wndPtr = WIN_FindWndPtr(hwnd);
153     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
154
155     switch (uMsg) {
156     case EM_CANUNDO:
157         /* cannot process undo message */
158         lResult = 0L;
159         break;
160         
161     case EM_FMTLINES:
162         printf("edit: cannot process EM_FMTLINES message\n");
163         lResult = 0L;
164         break;
165
166     case EM_GETFIRSTVISIBLELINE:
167         lResult = es->wtop;
168         break;
169
170     case EM_GETHANDLE:
171         printf("edit: cannot process EM_GETHANDLE message\n");
172         break;
173
174     case EM_GETLINE:
175         if (IsMultiLine())
176             lResult = EDIT_GetLineMsg(hwnd, wParam, lParam);
177         else
178             lResult = 0L;
179         break;
180
181     case EM_GETLINECOUNT:
182         if (IsMultiLine())
183             lResult = es->wlines;
184         else
185             lResult = 0L;
186         break;
187
188     case EM_GETMODIFY:
189         lResult = es->TextChanged;
190         break;
191
192     case EM_GETPASSWORDCHAR:
193         printf("edit: cannot process EM_GETPASSWORDCHAR message\n");
194         break;
195
196     case EM_GETRECT:
197         GetWindowRect(hwnd, (LPRECT)lParam);
198         break;
199
200     case EM_GETSEL:
201         lResult = EDIT_GetSelMsg(hwnd);
202         break;
203
204     case EM_GETWORDBREAKPROC:
205         printf("edit: cannot process EM_GETWORDBREAKPROC message\n");
206         break;
207
208     case EM_LIMITTEXT:
209         es->MaxTextLen = wParam;
210         break;
211
212     case EM_LINEFROMCHAR:
213         lResult = EDIT_LineFromCharMsg(hwnd, wParam);
214         break;
215
216     case EM_LINEINDEX:
217         if (IsMultiLine())
218             lResult = EDIT_LineIndexMsg(hwnd, wParam);
219         else
220             lResult = 0L;
221         break;
222
223     case EM_LINELENGTH:
224         printf("edit: cannot process EM_LINELENGTH message\n");
225         break;
226
227     case EM_LINESCROLL:
228         printf("edit: cannot process EM_LINESCROLL message\n");
229         break;
230
231     case EM_REPLACESEL:
232         printf("edit: cannot process EM_REPLACESEL message\n");
233         break;
234
235     case EM_SETHANDLE:
236         printf("edit: cannot process EM_SETHANDLE message\n");
237         break;
238
239     case EM_SETMODIFY:
240         es->TextChanged = wParam;
241         break;
242
243     case EM_SETPASSWORDCHAR:
244         printf("edit: cannot process EM_SETPASSWORDCHAR message\n");
245         break;
246
247     case EM_SETREADONLY:
248         printf("edit: cannot process EM_SETREADONLY message\n");
249         break;
250
251     case EM_SETRECT:
252     case EM_SETRECTNP:
253         printf("edit: cannot process EM_SETRECT(NP) message\n");
254         break;
255
256     case EM_SETSEL:
257         HideCaret(hwnd);
258         EDIT_SetSelMsg(hwnd, lParam);
259         SetCaretPos(es->WndCol, es->WndRow * es->txtht);
260         ShowCaret(hwnd);
261         break;
262
263     case EM_SETTABSTOPS:
264         printf("edit: cannot process EM_SETTABSTOPS message\n");
265         break;
266
267     case EM_SETWORDBREAKPROC:
268         printf("edit: cannot process EM_SETWORDBREAKPROC message\n");
269         break;
270
271     case WM_CHAR:
272         EDIT_CharMsg(hwnd, wParam);
273         break;
274
275     case WM_CREATE:
276         lResult = EDIT_CreateMsg(hwnd, lParam);
277         break;
278
279     case WM_DESTROY:
280         EDIT_HEAP_FREE(es->hTextPtrs);
281         EDIT_HEAP_FREE(es->hCharWidths);
282         EDIT_HEAP_FREE((HANDLE)(*(wndPtr->wExtra)));
283         break;
284
285     case WM_ENABLE:
286         InvalidateRect(hwnd, NULL, FALSE);
287         break;
288
289     case WM_GETTEXT:
290         textPtr = (LPSTR)EDIT_HEAP_ADDR(es->hText);
291         if ((int)wParam > (len = strlen(textPtr)))
292         {
293             strcpy((char *)lParam, textPtr);
294             lResult = (DWORD)len;
295         }
296         else
297             lResult = 0L;
298         break;
299
300     case WM_GETTEXTLENGTH:
301         textPtr = (LPSTR)EDIT_HEAP_ADDR(es->hText);
302         lResult = (DWORD)strlen(textPtr);
303         break;
304
305     case WM_HSCROLL:
306         EDIT_HScrollMsg(hwnd, wParam, lParam);
307         break;
308
309     case WM_KEYDOWN:
310         EDIT_KeyDownMsg(hwnd, wParam);
311         break;
312
313     case WM_KILLFOCUS:
314         DestroyCaret();
315         NOTIFY_PARENT(hwnd, EN_KILLFOCUS);
316         break;
317
318     case WM_LBUTTONDOWN:
319         HideCaret(hwnd);
320         SetFocus(hwnd);
321         EDIT_LButtonDownMsg(hwnd, wParam, lParam);
322         SetCaretPos(es->WndCol, es->WndRow * es->txtht);
323         ShowCaret(hwnd);
324         break;
325
326     case WM_LBUTTONUP:
327         ButtonDown = FALSE;
328         if (TextMarking)
329             EDIT_StopMarking(hwnd);
330         break;
331
332     case WM_MOUSEMOVE:
333         HideCaret(hwnd);
334         EDIT_MouseMoveMsg(hwnd, wParam, lParam);
335         SetCaretPos(es->WndCol, es->WndRow * es->txtht);
336         ShowCaret(hwnd);
337         break;
338
339     case WM_MOVE:
340         lResult = 0;
341         break;
342
343     case WM_PAINT:
344         EDIT_PaintMsg(hwnd);
345         break;
346
347     case WM_SETFOCUS:
348         es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
349         CreateCaret(hwnd, 0, 2, es->txtht);
350         SetCaretPos(es->WndCol, es->WndRow * es->txtht);
351         ShowCaret(hwnd);
352         NOTIFY_PARENT(hwnd, EN_SETFOCUS);
353         break;
354
355     case WM_SETFONT:
356         break;
357
358     case WM_SETTEXT:
359         EDIT_SetTextMsg(hwnd, lParam);
360         break;
361
362     case WM_SIZE:
363         EDIT_SizeMsg(hwnd, wParam, lParam);
364         lResult = 0;
365         break;
366
367     case WM_VSCROLL:
368         EDIT_VScrollMsg(hwnd, wParam, lParam);
369         break;
370
371     default:
372         lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
373         break;
374     }
375
376     GlobalUnlock(hwnd);
377     return lResult;
378 }
379
380
381 /*********************************************************************
382  *  WM_CREATE message function
383  */
384
385 long EDIT_CreateMsg(HWND hwnd, LONG lParam)
386 {
387     HDC hdc;
388     WND *wndPtr = WIN_FindWndPtr(hwnd);
389     EDITSTATE *es;
390     CLASS *classPtr;
391     short *charWidths;
392     TEXTMETRIC tm;
393     char *text;
394     unsigned int *textPtrs;
395
396     /* allocate space for state variable structure */
397     (HANDLE)(*(wndPtr->wExtra)) = 
398         EDIT_HEAP_ALLOC(sizeof(EDITSTATE));
399     es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
400     es->hTextPtrs = EDIT_HEAP_ALLOC(sizeof(int));
401     textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
402     es->hCharWidths = EDIT_HEAP_ALLOC(256 * sizeof(short));
403
404     /* initialize state variable structure */
405     /* --- char width array */
406     hdc = GetDC(hwnd);
407     charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
408     memset(charWidths, 0, 256 * sizeof(short));
409     GetCharWidth(hdc, 0, 255, charWidths);
410
411     /* --- text buffer */
412     es->MaxTextLen = MAXTEXTLEN + 1;
413     if (!(wndPtr->hText))
414     {
415         es->textlen = EditBufLen(wndPtr);
416         es->hText = EDIT_HEAP_ALLOC(EditBufLen(wndPtr) + 2);
417         text = (LPSTR)EDIT_HEAP_ADDR(es->hText);
418         memset(text, 0, es->textlen + 2);
419         EDIT_ClearTextPointers(hwnd);
420     }
421     else
422     {
423         es->hText = wndPtr->hText;
424         wndPtr->hText = 0;
425         es->textlen = GetWindowTextLength(hwnd) + 1;
426         EDIT_BuildTextPointers(hwnd);
427     }
428
429     /* --- other structure variables */
430     GetTextMetrics(hdc, &tm);
431     es->txtht = tm.tmHeight + tm.tmExternalLeading;
432     es->wlines = 0;
433     es->wtop = es->wleft = 0;
434     es->CurrCol = es->CurrLine = 0;
435     es->WndCol = es->WndRow = 0;
436     es->TextChanged = FALSE;
437     es->textwidth = 0;
438     es->SelBegLine = es->SelBegCol = -1;
439     es->SelEndLine = es->SelEndCol = -1;
440
441     /* allocate space for a line full of blanks to speed up */
442     /* line filling */
443     es->hBlankLine = EDIT_HEAP_ALLOC((ClientWidth(wndPtr) / 
444                                       charWidths[32]) + 2); 
445     text = EDIT_HEAP_ADDR(es->hBlankLine);
446     memset(text, ' ', (ClientWidth(wndPtr) / charWidths[32]) + 2);
447
448     /* set up text cursor for edit class */
449     CLASS_FindClassByName("EDIT", &classPtr);
450     classPtr->wc.hCursor = LoadCursor(0, IDC_IBEAM);
451
452     /* paint background on first WM_PAINT */
453     es->PaintBkgd = TRUE;
454
455     ReleaseDC(hwnd, hdc);
456     return 0L;
457 }
458
459
460 /*********************************************************************
461  *  EDIT_ClearTextPointers
462  *
463  *  Clear and initialize text line pointer array.
464  */
465
466 void EDIT_ClearTextPointers(HWND hwnd)
467 {
468     unsigned int *textPtrs;
469     WND *wndPtr = WIN_FindWndPtr(hwnd);
470     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
471     
472     es->hTextPtrs = EDIT_HEAP_REALLOC(es->hTextPtrs, sizeof(int));
473     textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
474     *textPtrs = 0;
475 }
476
477
478 /*********************************************************************
479  *  EDIT_BuildTextPointers
480  *
481  *  Build array of pointers to text lines.
482  */
483
484 #define INITLINES 100
485
486 void EDIT_BuildTextPointers(HWND hwnd)
487 {
488     WND *wndPtr = WIN_FindWndPtr(hwnd);
489     char *text, *cp;
490     int incrs = INITLINES;
491     unsigned int off, len, temp;
492     EDITSTATE *es;
493     unsigned int *textPtrs;
494     short *charWidths;
495
496     es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
497     text = (char *)EDIT_HEAP_ADDR(es->hText);
498     textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
499     charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
500
501     es->textwidth = es->wlines = 0;
502     cp = text;
503
504     /* advance through text buffer */
505     while (*cp)
506     {
507         /* increase size of text pointer array */ 
508         if (incrs == INITLINES)
509         {
510             incrs = 0;
511             es->hTextPtrs = EDIT_HEAP_REALLOC(es->hTextPtrs,
512                               (es->wlines + INITLINES) * sizeof(int));
513             textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
514         }
515         off = (unsigned int)(cp - text);     /* offset of beginning of line */
516         *(textPtrs + es->wlines) = off;
517         es->wlines++;
518         incrs++;
519         len = 0;
520         
521         /* advance through current line */
522         while (*cp && *cp != '\n')
523         {
524             len += charWidths[*cp];          /* width of line in pixels */
525             cp++;
526         }
527         es->textwidth = max(es->textwidth, len);
528         if (*cp)
529             cp++;                            /* skip '\n' */
530     }
531     off = (unsigned int)(cp - text);     /* offset of beginning of line */
532     *(textPtrs + es->wlines) = off;
533 }
534
535
536 /*********************************************************************
537  *  EDIT_ModTextPointers
538  *
539  *  Modify text pointers from a specified position.
540  */
541
542 void EDIT_ModTextPointers(HWND hwnd, int lineno, int var)
543 {
544     WND *wndPtr = WIN_FindWndPtr(hwnd);
545     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
546     unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
547
548     while (lineno < es->wlines)
549         *(textPtrs + lineno++) += var;
550 }
551
552
553 /*********************************************************************
554  *  WM_PAINT message function
555  */
556
557 void EDIT_PaintMsg(HWND hwnd)
558 {
559     PAINTSTRUCT ps;
560     HDC hdc;
561     int y;
562     RECT rc;
563     WND *wndPtr = WIN_FindWndPtr(hwnd);
564     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
565
566     hdc = BeginPaint(hwnd, &ps);
567     rc = ps.rcPaint;
568
569 #ifdef DEBUG_EDIT
570     printf("WM_PAINT: rc=(%d,%d), (%d,%d)\n", rc.left, rc.top, 
571            rc.right, rc.bottom);
572 #endif
573
574     if (es->PaintBkgd)
575         FillWindow(GetParent(hwnd), hwnd, hdc, CTLCOLOR_EDIT);
576
577     for (y = (rc.top / es->txtht); y <= (rc.bottom / es->txtht); y++)
578     {
579         if (y < es->wlines - es->wtop)
580             EDIT_WriteTextLine(hwnd, &rc, y + es->wtop);
581     }
582
583     EndPaint(hwnd, &ps);
584 }
585
586
587 /*********************************************************************
588  *  EDIT_GetTextLine
589  *
590  *  Get a copy of the text in the specified line.
591  */
592
593 HANDLE EDIT_GetTextLine(HWND hwnd, int selection)
594 {
595     char *line;
596     HANDLE hLine;
597     int len = 0;
598     char *cp, *cp1;
599
600 #ifdef DEBUG_EDIT
601     printf("GetTextLine %d\n", selection);
602 #endif
603     cp = cp1 = EDIT_TextLine(hwnd, selection);
604     /* advance through line */
605     while (*cp && *cp != '\n')
606     {
607         len++;
608         cp++;
609     }
610
611     /* store selected line and return handle */
612     hLine = EDIT_HEAP_ALLOC(len + 6);
613     line = (char *)EDIT_HEAP_ADDR(hLine);
614     memmove(line, cp1, len);
615     line[len] = '\0';
616     return hLine;
617 }
618
619
620 /*********************************************************************
621  *  EDIT_TextLine
622  *
623  *  Return a pointer to the text in the specified line.
624  */
625
626 char *EDIT_TextLine(HWND hwnd, int sel)
627 {
628     WND *wndPtr = WIN_FindWndPtr(hwnd);
629     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
630     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
631     unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
632
633     return (text + *(textPtrs + sel));
634 }
635     
636
637 /*********************************************************************
638  *  EDIT_LineLength
639  *
640  *  Return length of line _str_ of length _len_ characters in pixels.
641  */
642
643 int EDIT_LineLength(EDITSTATE *es, char *str, int len)
644 {
645     int i, plen = 0;
646     short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
647
648     for (i = 0; i < len; i++)
649         plen += charWidths[*(str + i)];
650
651 #ifdef DEBUG_EDIT
652     printf("EDIT_LineLength: returning %d\n", plen);
653 #endif
654     return plen;
655 }
656
657
658 /*********************************************************************
659  *  EDIT_WriteTextLine
660  *
661  *  Write the line of text at offset _y_ in text buffer to a window.
662  */
663
664 void EDIT_WriteTextLine(HWND hwnd, RECT *rect, int y)
665 {
666     int len = 0;
667     unsigned char line[200];
668     HANDLE hLine;
669     unsigned char *lp;
670     int lnlen, lnlen1;
671     int col, off = 0;
672     int sbl, sel, sbc, sec;
673     RECT rc;
674
675     BOOL trunc = FALSE;
676     WND *wndPtr = WIN_FindWndPtr(hwnd);
677     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
678
679     /* initialize rectangle if NULL, else copy */
680     if (rect)
681         CopyRect(&rc, rect);
682     else
683         GetClientRect(hwnd, &rc);
684
685 #ifdef DEBUG_EDIT
686     printf("WriteTextLine %d\n", y);
687 #endif
688
689     /* make sure y is inside the window */
690     if (y < es->wtop || y > (es->wtop + ClientHeight(wndPtr, es)))
691     {
692 #ifdef DEBUG_EDIT
693         printf("EDIT_WriteTextLine: y (%d) is not a displayed line\n", y);
694 #endif
695         return;
696     }
697
698     /* make sure rectangle is within window */
699     if (rc.left >= ClientWidth(wndPtr) - 1)
700     {
701 #ifdef DEBUG_EDIT
702         printf("EDIT_WriteTextLine: rc.left (%d) is greater than right edge\n",
703                rc.left);
704 #endif
705         return;
706     }
707     if (rc.right <= 0)
708     {
709 #ifdef DEBUG_EDIT
710         printf("EDIT_WriteTextLine: rc.right (%d) is less than left edge\n",
711                rc.right);
712 #endif
713         return;
714     }
715     if (y - es->wtop < (rc.top / es->txtht) || 
716         y - es->wtop > (rc.bottom / es->txtht))
717     {
718 #ifdef DEBUG_EDIT
719         printf("EDIT_WriteTextLine: y (%d) is outside window\n", y);
720 #endif
721         return;
722     }
723
724     /* get the text and length of line */
725     if ((hLine = EDIT_GetTextLine(hwnd, y)) == 0)
726         return;
727     lp = (unsigned char *)EDIT_HEAP_ADDR(hLine);
728     lnlen = EDIT_LineLength(es, lp, strlen(lp));
729     lnlen1 = lnlen;
730
731     /* build the line to display */
732     if (lnlen < es->wleft)
733         lnlen = 0;
734     else
735         off += es->wleft;
736
737     if (lnlen > rc.left)
738     {
739         off += rc.left;
740         lnlen = lnlen1 - off;
741         len = min(lnlen, rc.right - rc.left);
742     }
743
744     if (SelMarked(es))
745     {
746         sbl = es->SelBegLine;
747         sel = es->SelEndLine;
748         sbc = es->SelBegCol;
749         sec = es->SelEndCol;
750
751         /* put lowest marker first */
752         if (sbl > sel)
753         {
754             swap(&sbl, &sel);
755             swap(&sbc, &sec);
756         }
757         if (sbl == sel && sbc > sec)
758             swap(&sbc, &sec);
759
760         if (y < sbl || y > sel)
761             EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, rc.left, &rc,
762                            TRUE, FALSE);
763         else if (y > sbl && y < sel)
764             EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, rc.left, &rc,
765                            TRUE, TRUE);
766         else if (y == sbl)
767         {
768             col = EDIT_LineLength(es, lp, sbc);
769             if (col > (es->wleft + rc.left))
770             {
771                 len = min(col - off, rc.right - off);
772                 EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, 
773                                rc.left, &rc, FALSE, FALSE);
774                 off = col;
775             }
776             if (y == sel)
777             {
778                 col = EDIT_LineLength(es, lp, sec);
779                 if (col < (es->wleft + rc.right))
780                 {
781                     len = min(col - off, rc.right - off);
782                     EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
783                                    off - es->wleft, &rc, FALSE, TRUE);
784                     off = col;
785                     len = min(lnlen - off, rc.right - off);
786                     EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
787                                    off - es->wleft, &rc, TRUE, FALSE);
788                 }
789                 else
790                 {
791                     len = min(lnlen - off, rc.right - off);
792                     EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
793                                    off - es->wleft, &rc, TRUE, TRUE);
794                 }
795             }
796             else
797             {
798                 len = min(lnlen - off, rc.right - off);
799                 if (col < (es->wleft + rc.right))
800                     EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
801                                    off - es->wleft, &rc, TRUE, TRUE);
802             }
803         }
804         else if (y == sel)
805         {
806             col = EDIT_LineLength(es, lp, sec);
807             if (col < (es->wleft + rc.right))
808             {
809                 len = min(col - off, rc.right - off);
810                 EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
811                                off - es->wleft, &rc, FALSE, TRUE);
812                 off = col;
813                 len = min(lnlen - off, rc.right - off);
814                 EDIT_WriteText(hwnd, lp, off, len, y - es->wtop,
815                                off - es->wleft, &rc, TRUE, FALSE);
816             }
817         }
818     }
819     else
820         EDIT_WriteText(hwnd, lp, off, len, y - es->wtop, rc.left, &rc,
821                        TRUE, FALSE);
822               
823     EDIT_HEAP_FREE(hLine);
824 }
825
826
827 /*********************************************************************
828  *  EDIT_WriteText
829  *
830  *  Write text to a window
831  *     lp  - text line
832  *     off - offset in text line (in pixels)
833  *     len - length from off (in pixels)
834  *     row - line in window
835  *     col - column in window
836  *     rc  - rectangle in which to display line
837  *     blank - blank remainder of line?
838  *     reverse - reverse color of line?
839  */
840
841 void EDIT_WriteText(HWND hwnd, char *lp, int off, int len, int row, 
842                     int col, RECT *rc, BOOL blank, BOOL reverse)
843 {
844     HDC hdc;
845     HANDLE hStr;
846     char *str, *blanks;
847     int diff, num_spaces;
848     HRGN hrgnClip;
849     COLORREF oldTextColor, oldBkgdColor;
850     WND *wndPtr = WIN_FindWndPtr(hwnd);
851     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
852     short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
853
854 #ifdef DEBUG_EDIT
855     printf("EDIT_WriteText lp=%s, off=%d, len=%d, row=%d, col=%d, reverse=%d\n", lp, off, len, row, col, reverse);
856 #endif
857
858     hdc = GetDC(hwnd);
859     hStr = EDIT_GetStr(es, lp, off, len, &diff);
860     str = (char *)EDIT_HEAP_ADDR(hStr);
861     hrgnClip = CreateRectRgnIndirect(rc);
862     SelectClipRgn(hdc, hrgnClip);
863
864     SendMessage(GetParent(hwnd), WM_CTLCOLOR, (WORD)hdc,
865                 MAKELPARAM(hwnd, CTLCOLOR_EDIT));
866
867     if (reverse)
868     {
869         oldBkgdColor = GetBkColor(hdc);
870         oldTextColor = GetTextColor(hdc);
871         SetBkColor(hdc, oldTextColor);
872         SetTextColor(hdc, oldBkgdColor);
873     }
874
875     TextOut(hdc, col - diff, row * es->txtht, str, strlen(str));
876
877     if (reverse)
878     {
879         SetBkColor(hdc, oldBkgdColor);
880         SetTextColor(hdc, oldTextColor);
881     }
882
883     /* blank out remainder of line if appropriate */
884     if (blank)
885     {
886         if ((rc->right - col) > len)
887         {
888             blanks = EDIT_HEAP_ADDR(es->hBlankLine);
889             num_spaces = (rc->right - col - len) / charWidths[32];
890             TextOut(hdc, col + len, row * es->txtht, blanks, num_spaces);
891         }
892     }
893
894     EDIT_HEAP_FREE(hStr);
895     ReleaseDC(hwnd, hdc);
896 }
897
898
899 /*********************************************************************
900  *  EDIT_GetStr
901  *
902  *  Return sub-string starting at pixel _off_ of length _len_ pixels.
903  *  If _off_ is part way through a character, the negative offset of
904  *  the beginning of the character is returned in _diff_, else _diff_ 
905  *  will be zero.
906  */
907
908 HANDLE EDIT_GetStr(EDITSTATE *es, char *lp, int off, int len, int *diff)
909 {
910     HANDLE hStr;
911     char *str;
912     int ch = 0, i = 0, j, tmp;
913     int ch1;
914     short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
915
916 #ifdef DEBUG_EDIT
917     printf("EDIT_GetStr %s %d %d\n", lp, off, len);
918 #endif
919
920     while (i < off)
921     {
922         i += charWidths[*(lp + ch)];
923         ch++;
924     }
925
926     /* if stepped past _off_, go back a character */
927     if (i - off)
928         i -= charWidths[*(lp + --ch)];
929     *diff = off - i;
930     ch1 = ch;
931
932     while (i < len + off)
933     {
934         i += charWidths[*(lp + ch)];
935         ch++;
936     }
937     
938     hStr = EDIT_HEAP_ALLOC(ch - ch1 + 3);
939     str = (char *)EDIT_HEAP_ADDR(hStr);
940     for (i = ch1, j = 0; i < ch; i++, j++)
941         str[j] = lp[i];
942     str[++j] = '\0';
943 #ifdef DEBUG_EDIT
944     printf("EDIT_GetStr: returning %s\n", str);
945 #endif
946     return hStr;
947 }
948
949
950 /*********************************************************************
951  *  WM_CHAR message function
952  */
953
954 void EDIT_CharMsg(HWND hwnd, WORD wParam)
955 {
956     WND *wndPtr = WIN_FindWndPtr(hwnd);
957     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
958
959 #ifdef DEBUG_EDIT
960     printf("EDIT_CharMsg: wParam=%c\n", (char)wParam);
961 #endif
962
963     switch (wParam)
964     {
965     case '\r':
966     case '\n':
967         if (!IsMultiLine())
968             break;
969         wParam = '\n';
970         EDIT_KeyTyped(hwnd, wParam);
971         break;
972
973     default:
974         if (wParam >= 20 && wParam <= 126)
975             EDIT_KeyTyped(hwnd, wParam);
976         break;
977     }
978 }
979
980
981 /*********************************************************************
982  *  EDIT_KeyTyped
983  *
984  *  Process keystrokes that produce displayable characters.
985  */
986
987 void EDIT_KeyTyped(HWND hwnd, short ch)
988 {
989     WND *wndPtr = WIN_FindWndPtr(hwnd);
990     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
991     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
992     char *currchar = CurrChar;
993     RECT rc;
994     BOOL FullPaint = FALSE;
995
996 #ifdef DEBUG_EDIT
997     printf("EDIT_KeyTyped: ch=%c\n", (char)ch);
998 #endif
999
1000     /* delete selected text (if any) */
1001     if (SelMarked(es))
1002         EDIT_DeleteSel(hwnd);
1003
1004     /* test for typing at end of maximum buffer size */
1005     if (currchar == text + es->MaxTextLen)
1006     {
1007         NOTIFY_PARENT(hwnd, EN_ERRSPACE);
1008         return;
1009     }
1010
1011     if (*currchar == '\0')
1012     {
1013         /* insert a newline at end of text */
1014         *currchar = '\n';
1015         *(currchar + 1) = '\0';
1016         EDIT_BuildTextPointers(hwnd);
1017     }
1018
1019     /* insert the typed character */
1020     if (text[es->textlen - 1] != '\0')
1021     {
1022         /* current text buffer is full */
1023         if (es->textlen == es->MaxTextLen)
1024         {
1025             /* text buffer is at maximum size */
1026             NOTIFY_PARENT(hwnd, EN_ERRSPACE);
1027             return;
1028         }
1029
1030         /* increase the text buffer size */
1031         es->textlen += GROWLENGTH;
1032         /* but not above maximum size */
1033         if (es->textlen > es->MaxTextLen)
1034             es->textlen = es->MaxTextLen;
1035         es->hText = EDIT_HEAP_REALLOC(es->hText, es->textlen + 2);
1036         if (!es->hText)
1037             NOTIFY_PARENT(hwnd, EN_ERRSPACE);
1038         text = (char *)EDIT_HEAP_ADDR(es->hText);
1039         text[es->textlen - 1] = '\0';
1040         currchar = CurrChar;
1041     }
1042     /* make space for new character and put char in buffer */
1043     memmove(currchar + 1, currchar, strlen(currchar) + 1);
1044     *currchar = ch;
1045     EDIT_ModTextPointers(hwnd, es->CurrLine + 1, 1);
1046     es->TextChanged = TRUE;
1047     NOTIFY_PARENT(hwnd, EN_UPDATE);
1048
1049     /* re-adjust textwidth, if necessary, and redraw line */
1050     HideCaret(hwnd);
1051     if (IsMultiLine() && es->wlines > 1)
1052     {
1053         es->textwidth = max(es->textwidth,
1054                     EDIT_LineLength(es, EDIT_TextLine(hwnd, es->CurrLine),
1055                     (int)(EDIT_TextLine(hwnd, es->CurrLine + 1) -
1056                           EDIT_TextLine(hwnd, es->CurrLine))));
1057     }
1058     else
1059         es->textwidth = max(es->textwidth,
1060                             EDIT_LineLength(es, text, strlen(text)));
1061     EDIT_WriteTextLine(hwnd, NULL, es->wtop + es->WndRow);
1062
1063     if (ch == '\n')
1064     {
1065         if (es->wleft > 0)
1066             FullPaint = TRUE;
1067         es->wleft = 0;
1068         EDIT_BuildTextPointers(hwnd);
1069         EDIT_End(hwnd);
1070         EDIT_Forward(hwnd);
1071
1072         /* invalidate rest of window */
1073         GetClientRect(hwnd, &rc);
1074         if (!FullPaint)
1075             rc.top = es->WndRow * es->txtht;
1076         InvalidateRect(hwnd, &rc, FALSE);
1077
1078         SetCaretPos(es->WndCol, es->WndRow * es->txtht);
1079         ShowCaret(hwnd);
1080         UpdateWindow(hwnd);
1081         NOTIFY_PARENT(hwnd, EN_CHANGE);
1082         return;
1083     }
1084
1085     /* test end of window */
1086     if (es->WndCol >= ClientWidth(wndPtr) - EDIT_CharWidth(es, ch))
1087     {
1088         /* TODO:- Word wrap to be handled here */
1089
1090 /*      if (!(currchar == text + es->MaxTextLen - 2)) */
1091             EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
1092     }
1093     es->WndCol += EDIT_CharWidth(es, ch);
1094     es->CurrCol++;
1095     SetCaretPos(es->WndCol, es->WndRow * es->txtht);
1096     ShowCaret(hwnd);
1097     NOTIFY_PARENT(hwnd, EN_CHANGE);
1098 }
1099
1100
1101 /*********************************************************************
1102  *  EDIT_CharWidth
1103  *
1104  *  Return the width of the given character in pixels.
1105  */
1106
1107 int EDIT_CharWidth(EDITSTATE *es, short ch)
1108 {
1109     short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
1110
1111     return (charWidths[ch]);
1112 }
1113
1114
1115 /*********************************************************************
1116  *  EDIT_Forward
1117  *
1118  *  Cursor right key: move right one character position.
1119  */
1120
1121 void EDIT_Forward(HWND hwnd)
1122 {
1123     WND *wndPtr = WIN_FindWndPtr(hwnd);
1124     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1125     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
1126     char *cc = CurrChar + 1;
1127
1128     if (*cc == '\0')
1129         return;
1130
1131     if (*CurrChar == '\n')
1132     {
1133         EDIT_Home(hwnd);
1134         EDIT_Downward(hwnd);
1135     }
1136     else
1137     {
1138         es->WndCol += EDIT_CharWidth(es, *CurrChar);
1139         es->CurrCol++;
1140         if (es->WndCol >= ClientWidth(wndPtr))
1141             EDIT_KeyHScroll(hwnd, SB_LINEDOWN);
1142     }
1143 }
1144
1145
1146
1147 /*********************************************************************
1148  *  EDIT_Downward
1149  *
1150  *  Cursor down key: move down one line.
1151  */
1152
1153 void EDIT_Downward(HWND hwnd)
1154 {
1155     WND *wndPtr = WIN_FindWndPtr(hwnd);
1156     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1157
1158 #ifdef DEBUG_EDIT
1159     printf("EDIT_Downward: WndRow=%d, wtop=%d, wlines=%d\n", es->WndRow, es->wtop, es->wlines);
1160 #endif
1161
1162     if (IsMultiLine() && (es->WndRow + es->wtop + 1 < es->wlines))
1163     {
1164         es->CurrLine++;
1165         if (es->WndRow == ClientHeight(wndPtr, es) - 1)
1166         {
1167             es->WndRow++;
1168             EDIT_KeyVScrollLine(hwnd, SB_LINEDOWN);
1169         }
1170         else
1171             es->WndRow++;
1172         EDIT_StickEnd(hwnd);
1173     }
1174 }
1175
1176
1177 /*********************************************************************
1178  *  EDIT_Upward
1179  *
1180  *  Cursor up key: move up one line.
1181  */
1182
1183 void EDIT_Upward(HWND hwnd)
1184 {
1185     WND *wndPtr = WIN_FindWndPtr(hwnd);
1186     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1187
1188     if (IsMultiLine() && es->CurrLine != 0)
1189     {
1190         --es->CurrLine;
1191         if (es->WndRow == 0)
1192         {
1193             --es->WndRow;
1194             EDIT_KeyVScrollLine(hwnd, SB_LINEUP);
1195         }
1196         else
1197             --es->WndRow;
1198         EDIT_StickEnd(hwnd);
1199     }
1200 }
1201
1202
1203 /*********************************************************************
1204  *  EDIT_Backward
1205  *
1206  *  Cursor left key: move left one character position.
1207  */
1208
1209 void EDIT_Backward(HWND hwnd)
1210 {
1211     WND *wndPtr = WIN_FindWndPtr(hwnd);
1212     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1213     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
1214
1215     if (es->CurrCol)
1216     {
1217         --es->CurrCol;
1218         es->WndCol -= EDIT_CharWidth(es, *CurrChar);
1219         if (es->WndCol < 0)
1220             EDIT_KeyHScroll(hwnd, SB_LINEUP);
1221     }
1222     else if (IsMultiLine() && es->CurrLine != 0)
1223     {
1224         EDIT_Upward(hwnd);
1225         EDIT_End(hwnd);
1226     }
1227 }
1228
1229
1230 /*********************************************************************
1231  *  EDIT_End
1232  *
1233  *  End key: move to end of line.
1234  */
1235
1236 void EDIT_End(HWND hwnd)
1237 {
1238     RECT rc;
1239     WND *wndPtr = WIN_FindWndPtr(hwnd);
1240     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1241     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
1242
1243     while (*CurrChar && *CurrChar != '\n')
1244     {
1245         es->WndCol += EDIT_CharWidth(es, *CurrChar);
1246         es->CurrCol++;
1247     }
1248
1249     if (es->WndCol >= ClientWidth(wndPtr))
1250     {
1251         es->wleft = es->WndCol - ClientWidth(wndPtr) + HSCROLLDIM;
1252         es->WndCol -= es->wleft;
1253         InvalidateRect(hwnd, NULL, FALSE);
1254         UpdateWindow(hwnd);
1255     }
1256 }
1257
1258
1259 /*********************************************************************
1260  *  EDIT_Home
1261  *
1262  *  Home key: move to beginning of line.
1263  */
1264
1265 void EDIT_Home(HWND hwnd)
1266 {
1267     RECT rc;
1268     WND *wndPtr = WIN_FindWndPtr(hwnd);
1269     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1270
1271     es->CurrCol = es->WndCol = 0;
1272     if (es->wleft != 0)
1273     {
1274         es->wleft = 0;
1275         InvalidateRect(hwnd, NULL, FALSE);
1276         UpdateWindow(hwnd);
1277     }
1278 }
1279
1280
1281 /*********************************************************************
1282  *  EDIT_StickEnd
1283  *
1284  *  Stick the cursor to the end of the line.
1285  */
1286
1287 void EDIT_StickEnd(HWND hwnd)
1288 {
1289     WND *wndPtr = WIN_FindWndPtr(hwnd);
1290     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1291     char currpel;
1292
1293     char *cp = EDIT_TextLine(hwnd, es->CurrLine);
1294     char *cp1 = strchr(cp, '\n');
1295     int len = cp1 ? (int)(cp1 - cp) : 0;
1296
1297     es->CurrCol = min(len, es->CurrCol);
1298     es->WndCol = min(EDIT_LineLength(es, cp, len) - es->wleft, es->WndCol);
1299     currpel = EDIT_LineLength(es, cp, es->CurrCol);
1300
1301     if (es->wleft > currpel)
1302     {
1303         es->wleft = max(0, currpel - 20);
1304         es->WndCol = currpel - es->wleft;
1305         UpdateWindow(hwnd);
1306     }
1307     else if (currpel - es->wleft >= ClientWidth(wndPtr))
1308     {
1309         es->wleft = currpel - (ClientWidth(wndPtr) - 5);
1310         es->WndCol = currpel - es->wleft;
1311         UpdateWindow(hwnd);
1312     }
1313 }
1314
1315
1316 /*********************************************************************
1317  *  WM_KEYDOWN message function
1318  */
1319
1320 void EDIT_KeyDownMsg(HWND hwnd, WORD wParam)
1321 {
1322     WND *wndPtr = WIN_FindWndPtr(hwnd);
1323     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1324
1325 #ifdef DEBUG_EDIT
1326     printf("EDIT_KeyDownMsg: key=%x\n", wParam);
1327 #endif
1328
1329     HideCaret(hwnd);
1330     switch (wParam)
1331     {
1332     case VK_UP:
1333         if (SelMarked(es))
1334             EDIT_ClearSel(hwnd);
1335         if (IsMultiLine())
1336             EDIT_Upward(hwnd);
1337         else
1338             EDIT_Backward(hwnd);
1339         break;
1340
1341     case VK_DOWN:
1342         if (SelMarked(es))
1343             EDIT_ClearSel(hwnd);
1344         if (IsMultiLine())
1345             EDIT_Downward(hwnd);
1346         else
1347             EDIT_Forward(hwnd);
1348         break;
1349
1350     case VK_RIGHT:
1351         if (SelMarked(es))
1352             EDIT_ClearSel(hwnd);
1353         EDIT_Forward(hwnd);
1354         break;
1355
1356     case VK_LEFT:
1357         if (SelMarked(es))
1358             EDIT_ClearSel(hwnd);
1359         EDIT_Backward(hwnd);
1360         break;
1361
1362     case VK_HOME:
1363         if (SelMarked(es))
1364             EDIT_ClearSel(hwnd);
1365         EDIT_Home(hwnd);
1366         break;
1367
1368     case VK_END:
1369         if (SelMarked(es))
1370             EDIT_ClearSel(hwnd);
1371         EDIT_End(hwnd);
1372         break;
1373
1374     case VK_PRIOR:
1375         if (IsMultiLine())
1376         {
1377             if (SelMarked(es))
1378                 EDIT_ClearSel(hwnd);
1379             EDIT_KeyVScrollPage(hwnd, SB_PAGEUP);
1380         }
1381         break;
1382
1383     case VK_NEXT:
1384         if (IsMultiLine())
1385         {
1386             if (SelMarked(es))
1387                 EDIT_ClearSel(hwnd);
1388             EDIT_KeyVScrollPage(hwnd, SB_PAGEDOWN);
1389         }
1390         break;
1391
1392     case VK_BACK:
1393         if (SelMarked(es))
1394             EDIT_DeleteSel(hwnd);
1395         else
1396         {
1397             if (es->CurrCol == 0 && es->CurrLine == 0)
1398                 break;
1399             EDIT_Backward(hwnd);
1400             EDIT_DelKey(hwnd);
1401         }
1402         break;
1403
1404     case VK_DELETE:
1405         if (SelMarked(es))
1406             EDIT_DeleteSel(hwnd);
1407         else
1408             EDIT_DelKey(hwnd);
1409         break;
1410     }
1411
1412     SetCaretPos(es->WndCol, es->WndRow * es->txtht);
1413     ShowCaret(hwnd);
1414 }
1415
1416
1417 /*********************************************************************
1418  *  EDIT_KeyHScroll
1419  *
1420  *  Scroll text horizontally using cursor keys.
1421  */
1422
1423 void EDIT_KeyHScroll(HWND hwnd, WORD opt)
1424 {
1425     RECT rc;
1426     int hscrollpos;
1427     WND *wndPtr = WIN_FindWndPtr(hwnd);
1428     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1429
1430     if (opt == SB_LINEDOWN)
1431     {
1432         es->wleft += HSCROLLDIM;
1433         es->WndCol -= HSCROLLDIM;
1434     }
1435     else
1436     {
1437         if (es->wleft == 0)
1438             return;
1439         if (es->wleft - HSCROLLDIM < 0)
1440         {
1441             es->WndCol += es->wleft;
1442             es->wleft = 0;
1443         }           
1444         else
1445         {
1446             es->wleft -= HSCROLLDIM;
1447             es->WndCol += HSCROLLDIM;
1448         }
1449     }
1450
1451     InvalidateRect(hwnd, NULL, FALSE);
1452     UpdateWindow(hwnd);
1453
1454     if (IsHScrollBar())
1455     {
1456         hscrollpos = EDIT_ComputeHScrollPos(hwnd);
1457         SetScrollPos(hwnd, SB_HORZ, hscrollpos, TRUE);
1458     }
1459 }
1460
1461
1462 /*********************************************************************
1463  *  EDIT_KeyVScrollLine
1464  *
1465  *  Scroll text vertically by one line using keyboard.
1466  */
1467
1468 void EDIT_KeyVScrollLine(HWND hwnd, WORD opt)
1469 {
1470     RECT rc;
1471     int y, vscrollpos;
1472     WND *wndPtr = WIN_FindWndPtr(hwnd);
1473     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1474
1475     if (!IsMultiLine())
1476         return;
1477
1478     if (opt == SB_LINEDOWN)
1479     {
1480         /* move down one line */
1481         if (es->wtop + ClientHeight(wndPtr, es) >= es->wlines)
1482             return;
1483         es->wtop++;
1484     }
1485     else
1486     {
1487         /* move up one line */
1488         if (es->wtop == 0)
1489             return;
1490         --es->wtop;
1491     }
1492
1493     if (IsWindowVisible(hwnd))
1494     {
1495         /* adjust client bottom to nearest whole line */
1496         GetClientRect(hwnd, &rc);
1497         rc.bottom = (rc.bottom / es->txtht) * es->txtht;
1498
1499         if (opt == SB_LINEUP)
1500         {
1501             /* move up one line (scroll window down) */
1502             ScrollWindow(hwnd, 0, es->txtht, &rc, &rc);
1503             /* write top line */
1504             EDIT_WriteTextLine(hwnd, NULL, es->wtop);
1505             es->WndRow++;
1506         }
1507         else
1508         {
1509             /* move down one line (scroll window up) */
1510             ScrollWindow(hwnd, 0, -(es->txtht), &rc, &rc);
1511             /* write bottom line */
1512             y = (((rc.bottom - rc.top) / es->txtht) - 1);
1513             EDIT_WriteTextLine(hwnd, NULL, es->wtop + y);
1514             --es->WndRow;
1515         }
1516     }
1517
1518     /* reset the vertical scroll bar */
1519     if (IsVScrollBar())
1520     {
1521         vscrollpos = EDIT_ComputeVScrollPos(hwnd);
1522         SetScrollPos(hwnd, SB_VERT, vscrollpos, TRUE);
1523     }
1524 }
1525
1526
1527 /*********************************************************************
1528  *  EDIT_KeyVScrollPage
1529  *
1530  *  Scroll text vertically by one page using keyboard.
1531  */
1532
1533 void EDIT_KeyVScrollPage(HWND hwnd, WORD opt)
1534 {
1535     RECT rc;
1536     int vscrollpos;
1537     WND *wndPtr = WIN_FindWndPtr(hwnd);
1538     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1539
1540     if (IsMultiLine())
1541     {
1542         if (opt == SB_PAGEUP)
1543         {
1544             if (es->wtop)
1545                 es->wtop -= ClientHeight(wndPtr, es);
1546         }
1547         else
1548         {
1549             if (es->wtop + ClientHeight(wndPtr, es) < es->wlines)
1550             {
1551                 es->wtop += ClientHeight(wndPtr, es);
1552                 if (es->wtop > es->wlines - ClientHeight(wndPtr, es))
1553                     es->wtop = es->wlines - ClientHeight(wndPtr, es);
1554             }
1555         }
1556         if (es->wtop < 0)
1557             es->wtop = 0;
1558
1559         es->CurrLine = es->wtop + es->WndRow;
1560         EDIT_StickEnd(hwnd);
1561         InvalidateRect(hwnd, NULL, TRUE);
1562         UpdateWindow(hwnd);
1563
1564         /* reset the vertical scroll bar */
1565         if (IsVScrollBar())
1566         {
1567             vscrollpos = EDIT_ComputeVScrollPos(hwnd);
1568             SetScrollPos(hwnd, SB_VERT, vscrollpos, TRUE);
1569         }
1570     }
1571 }
1572
1573
1574 /*********************************************************************
1575  *  EDIT_KeyVScrollDoc
1576  *
1577  *  Scroll text to top and bottom of document using keyboard.
1578  */
1579
1580 void EDIT_KeyVScrollDoc(HWND hwnd, WORD opt)
1581 {
1582     RECT rc;
1583     int vscrollpos;
1584     WND *wndPtr = WIN_FindWndPtr(hwnd);
1585     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1586
1587     if (!IsMultiLine())
1588         return;
1589
1590     if (opt == SB_TOP)
1591         es->wtop = es->wleft = 0;
1592     else if (es->wtop + ClientHeight(wndPtr, es) < es->wlines)
1593     {
1594         es->wtop = es->wlines - ClientHeight(wndPtr, es);
1595         es->wleft = 0;
1596     }
1597
1598     es->CurrLine = es->wlines;
1599     es->WndRow = es->wlines - es->wtop;
1600     EDIT_End(hwnd);
1601     InvalidateRect(hwnd, NULL, TRUE);
1602     UpdateWindow(hwnd);
1603
1604     /* reset the vertical scroll bar */
1605     if (IsVScrollBar())
1606     {
1607         vscrollpos = EDIT_ComputeVScrollPos(hwnd);
1608         SetScrollPos(hwnd, SB_VERT, vscrollpos, TRUE);
1609     }
1610 }
1611
1612
1613 /*********************************************************************
1614  *  EDIT_ComputeVScrollPos
1615  *
1616  *  Compute the vertical scroll bar position from the window
1617  *  position and text width.
1618  */
1619
1620 int EDIT_ComputeVScrollPos(HWND hwnd)
1621 {
1622     int vscrollpos;
1623     short minpos, maxpos;
1624     WND *wndPtr = WIN_FindWndPtr(hwnd);
1625     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1626
1627     GetScrollRange(hwnd, SB_VERT, &minpos, &maxpos);
1628
1629     if (es->wlines > ClientHeight(wndPtr, es))
1630         vscrollpos = (double)(es->wtop) / (double)(es->wlines - 
1631                      ClientHeight(wndPtr, es)) * (maxpos - minpos);
1632     else
1633         vscrollpos = minpos;
1634
1635     return vscrollpos;
1636 }
1637     
1638
1639 /*********************************************************************
1640  *  EDIT_ComputeHScrollPos
1641  *
1642  *  Compute the horizontal scroll bar position from the window
1643  *  position and text width.
1644  */
1645
1646 int EDIT_ComputeHScrollPos(HWND hwnd)
1647 {
1648     int hscrollpos;
1649     short minpos, maxpos;
1650     WND *wndPtr = WIN_FindWndPtr(hwnd);
1651     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1652
1653     GetScrollRange(hwnd, SB_HORZ, &minpos, &maxpos);
1654
1655     if (es->textwidth > ClientWidth(wndPtr))
1656         hscrollpos = (double)(es->wleft) / (double)(es->textwidth - 
1657                      ClientWidth(wndPtr)) * (maxpos - minpos);
1658     else
1659         hscrollpos = minpos;
1660
1661     return hscrollpos;
1662 }
1663
1664
1665 /*********************************************************************
1666  *  EDIT_DelKey
1667  *
1668  *  Delete character to right of cursor.
1669  */
1670
1671 void EDIT_DelKey(HWND hwnd)
1672 {
1673     RECT rc;
1674     WND *wndPtr = WIN_FindWndPtr(hwnd);
1675     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1676     char *currchar = CurrChar;
1677     BOOL repaint = *currchar == '\n';
1678
1679     if (IsMultiLine() && *currchar == '\n' && *(currchar + 1) == '\0')
1680         return;
1681     strcpy(currchar, currchar + 1);
1682     NOTIFY_PARENT(hwnd, EN_UPDATE);
1683     
1684     if (repaint)
1685     {
1686         EDIT_BuildTextPointers(hwnd);
1687         GetClientRect(hwnd, &rc);
1688         rc.top = es->WndRow * es->txtht;
1689         InvalidateRect(hwnd, &rc, FALSE);
1690         UpdateWindow(hwnd);
1691     }
1692     else
1693     {
1694         EDIT_ModTextPointers(hwnd, es->CurrLine + 1, -1);
1695         EDIT_WriteTextLine(hwnd, NULL, es->WndRow + es->wtop);
1696     }
1697
1698     es->TextChanged = TRUE;
1699     NOTIFY_PARENT(hwnd, EN_CHANGE);
1700 }
1701
1702 /*********************************************************************
1703  *  WM_VSCROLL message function
1704  */
1705
1706 void EDIT_VScrollMsg(HWND hwnd, WORD wParam, LONG lParam)
1707 {
1708     WND *wndPtr = WIN_FindWndPtr(hwnd);
1709     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1710
1711     if (IsMultiLine())
1712     {
1713         HideCaret(hwnd);
1714
1715         switch (wParam)
1716         {
1717         case SB_LINEUP:
1718         case SB_LINEDOWN:
1719             EDIT_VScrollLine(hwnd, wParam);
1720             break;
1721
1722         case SB_PAGEUP:
1723         case SB_PAGEDOWN:
1724             EDIT_VScrollPage(hwnd, wParam);
1725             break;
1726         }
1727     }
1728     
1729     SetCaretPos(es->WndCol, es->WndRow);
1730     ShowCaret(hwnd);
1731 }
1732
1733
1734 /*********************************************************************
1735  *  EDIT_VScrollLine
1736  *
1737  *  Scroll text vertically by one line using scrollbars.
1738  */
1739
1740 void EDIT_VScrollLine(HWND hwnd, WORD opt)
1741 {
1742     RECT rc;
1743     int y;
1744     WND *wndPtr = WIN_FindWndPtr(hwnd);
1745     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1746
1747 #ifdef DEBUG_EDIT
1748     printf("EDIT_VScrollLine: direction=%d\n", opt);
1749 #endif
1750
1751     if (opt == SB_LINEDOWN)
1752     {
1753         /* move down one line */
1754         if (es->wtop + ClientHeight(wndPtr, es) >= es->wlines)
1755             return;
1756         es->wtop++;
1757         printf("Scroll line down: wtop=%d\n", es->wtop);
1758     }
1759     else
1760     {
1761         /* move up one line */
1762         if (es->wtop == 0)
1763             return;
1764         --es->wtop;
1765         printf("Scroll line up: wtop=%d\n", es->wtop);
1766     }
1767
1768     if (IsWindowVisible(hwnd))
1769     {
1770         /* adjust client bottom to nearest whole line */
1771         GetClientRect(hwnd, &rc);
1772         rc.bottom = (rc.bottom / es->txtht) * es->txtht;
1773
1774         if (opt == SB_LINEUP)
1775         {
1776             /* move up one line (scroll window down) */
1777             ScrollWindow(hwnd, 0, es->txtht, &rc, &rc);
1778             /* write top line */
1779             EDIT_WriteTextLine(hwnd, NULL, es->wtop);
1780             es->WndRow++;
1781         }
1782         else
1783         {
1784             /* move down one line (scroll window up) */
1785             ScrollWindow(hwnd, 0, -(es->txtht), &rc, &rc);
1786             /* write bottom line */
1787             y = ((rc.bottom - rc.top / es->txtht) - 1);
1788             EDIT_WriteTextLine(hwnd, NULL, es->wtop + y);
1789             --es->WndRow;
1790         }
1791     }
1792 }
1793
1794
1795 /*********************************************************************
1796  *  EDIT_VScrollPage
1797  *
1798  *  Scroll text vertically by one page using keyboard.
1799  */
1800
1801 void EDIT_VScrollPage(HWND hwnd, WORD opt)
1802 {
1803     RECT rc;
1804     int vscrollpos;
1805     WND *wndPtr = WIN_FindWndPtr(hwnd);
1806     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1807
1808     if (opt == SB_PAGEUP)
1809     {
1810         if (es->wtop)
1811             es->wtop -= ClientHeight(wndPtr, es);
1812     }
1813     else
1814     {
1815         if (es->wtop + ClientHeight(wndPtr, es) < es->wlines)
1816         {
1817             es->wtop += ClientHeight(wndPtr, es);
1818             if (es->wtop > es->wlines - ClientHeight(wndPtr, es))
1819                 es->wtop = es->wlines - ClientHeight(wndPtr, es);
1820         }
1821     }
1822     if (es->wtop < 0)
1823         es->wtop = 0;
1824     
1825     InvalidateRect(hwnd, NULL, TRUE);
1826     UpdateWindow(hwnd);
1827
1828     /* reset the vertical scroll bar */
1829     if (IsVScrollBar())
1830     {
1831         vscrollpos = EDIT_ComputeVScrollPos(hwnd);
1832         SetScrollPos(hwnd, SB_VERT, vscrollpos, TRUE);
1833     }
1834 }
1835
1836
1837 /*********************************************************************
1838  *  WM_HSCROLL message function
1839  */
1840
1841 void EDIT_HScrollMsg(HWND hwnd, WORD wParam, LONG lParam)
1842 {
1843     WND *wndPtr = WIN_FindWndPtr(hwnd);
1844     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1845
1846     switch (wParam)
1847     {
1848     case SB_LINEUP:
1849     case SB_LINEDOWN:
1850         HideCaret(hwnd);
1851
1852         SetCaretPos(es->WndCol, es->WndRow * es->txtht);
1853         ShowCaret(hwnd);
1854         break;
1855     }
1856 }
1857
1858
1859 /*********************************************************************
1860  *  WM_SIZE message function
1861  */
1862
1863 void EDIT_SizeMsg(HWND hwnd, WORD wParam, LONG lParam)
1864 {
1865     RECT rc;
1866     WND *wndPtr = WIN_FindWndPtr(hwnd);
1867     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1868
1869     if (wParam != SIZE_MAXIMIZED && wParam != SIZE_RESTORED) return;
1870
1871     InvalidateRect(hwnd, NULL, TRUE);
1872     es->PaintBkgd = TRUE;
1873     UpdateWindow(hwnd);
1874 }
1875
1876
1877 /*********************************************************************
1878  *  WM_LBUTTONDOWN message function
1879  */
1880
1881 void EDIT_LButtonDownMsg(HWND hwnd, WORD wParam, LONG lParam)
1882 {
1883     char *cp, *cp1;
1884     int len;
1885     BOOL end = FALSE;
1886     WND *wndPtr = WIN_FindWndPtr(hwnd);
1887     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1888
1889     if (SelMarked(es))
1890         EDIT_ClearSel(hwnd);
1891
1892     es->WndRow = HIWORD(lParam) / es->txtht;
1893     if (es->WndRow > es->wlines - es->wtop - 1)
1894     {
1895         if (es->wlines)
1896             es->WndRow = es->wlines - es->wtop - 1;
1897         else
1898             es->WndRow = 0;
1899         end = TRUE;
1900     }
1901     es->CurrLine = es->wtop + es->WndRow;
1902
1903     cp = EDIT_TextLine(hwnd, es->CurrLine);
1904     cp1 = strchr(cp, '\n');
1905     len = cp1 ? (int)(cp1 - cp) : 0;
1906
1907     es->WndCol = LOWORD(lParam);
1908     if (es->WndCol > EDIT_LineLength(es, cp, len) - es->wleft || end)
1909         es->WndCol = EDIT_LineLength(es, cp, len) - es->wleft;
1910     es->CurrCol = EDIT_PixelToChar(hwnd, es->CurrLine, &(es->WndCol));
1911
1912     ButtonDown = TRUE;
1913     ButtonRow = es->CurrLine;
1914     ButtonCol = es->CurrCol;
1915 }
1916
1917
1918 /*********************************************************************
1919  *  WM_MOUSEMOVE message function
1920  */
1921
1922 void EDIT_MouseMoveMsg(HWND hwnd, WORD wParam, LONG lParam)
1923 {
1924     if (wParam != MK_LBUTTON)
1925         return;
1926
1927     if (ButtonDown)
1928     {
1929         EDIT_SetAnchor(hwnd, ButtonRow, ButtonCol);
1930         TextMarking = TRUE;
1931         ButtonDown = FALSE;
1932     }
1933
1934     if (TextMarking)
1935         EDIT_ExtendSel(hwnd, LOWORD(lParam), HIWORD(lParam));
1936 }
1937
1938
1939 /*********************************************************************
1940  *  EDIT_PixelToChar
1941  *
1942  *  Convert a pixel offset in the given row to a character offset,
1943  *  adjusting the pixel offset to the nearest whole character if
1944  *  necessary.
1945  */
1946
1947 int EDIT_PixelToChar(HWND hwnd, int row, int *pixel)
1948 {
1949     int ch = 0, i = 0;
1950     char *text;
1951     WND *wndPtr = WIN_FindWndPtr(hwnd);
1952     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1953     short *charWidths = (short *)EDIT_HEAP_ADDR(es->hCharWidths);
1954
1955 #ifdef DEBUG_EDIT
1956     printf("EDIT_PixelToChar: row=%d, pixel=%d\n", row, *pixel);
1957 #endif
1958
1959     text = EDIT_TextLine(hwnd, row);
1960     while (i < *pixel)
1961     {
1962         i += charWidths[*(text + ch)];
1963         ch++;
1964     }
1965
1966     /* if stepped past _pixel_, go back a character */
1967     if (i - *pixel)
1968         i -= charWidths[*(text + ch)];
1969     *pixel = i;
1970     return ch;
1971 }
1972
1973
1974 /*********************************************************************
1975  *  WM_SETTEXT message function
1976  */
1977
1978 LONG EDIT_SetTextMsg(HWND hwnd, LONG lParam)
1979 {
1980     int len;
1981     char *text;
1982     RECT rc;
1983     WND *wndPtr = WIN_FindWndPtr(hwnd);
1984     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
1985
1986     if (strlen((char *)lParam) <= es->MaxTextLen)
1987     {
1988         len = strlen((char *)lParam);
1989         EDIT_ClearText(hwnd);
1990         es->textlen = len;
1991         es->hText = EDIT_HEAP_REALLOC(es->hText, len + 3);
1992         text = EDIT_HEAP_ADDR(es->hText);
1993         strcpy(text, (char *)lParam);
1994 /*      text[len] = '\n'; */ /* Removed by Bob Amstadt */
1995         text[len + 1] = '\0';
1996         text[len + 2] = '\0';
1997         EDIT_BuildTextPointers(hwnd);
1998         InvalidateRect(hwnd, NULL, TRUE);
1999         es->PaintBkgd = TRUE;
2000         es->TextChanged = TRUE;
2001         return 0L;
2002     }
2003     else
2004         return EN_ERRSPACE;
2005 }
2006
2007
2008 /*********************************************************************
2009  *  EDIT_ClearText
2010  *
2011  *  Clear text from text buffer.
2012  */
2013
2014 void EDIT_ClearText(HWND hwnd)
2015 {
2016     WND *wndPtr = WIN_FindWndPtr(hwnd);
2017     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2018     unsigned int blen = EditBufLen(wndPtr) + 2;
2019     char *text;
2020
2021     es->hText = EDIT_HEAP_REALLOC(es->hText, blen);
2022     text = EDIT_HEAP_ADDR(es->hText);
2023     memset(text, 0, blen);
2024     es->textlen = 0;
2025     es->wlines = 0;
2026     es->CurrLine = es->CurrCol = 0;
2027     es->WndRow = es->WndCol = 0;
2028     es->wleft = es->wtop = 0;
2029     es->textwidth = 0;
2030     es->TextChanged = FALSE;
2031     EDIT_ClearTextPointers(hwnd);
2032 }
2033
2034
2035 /*********************************************************************
2036  *  EM_SETSEL message function
2037  */
2038
2039 void EDIT_SetSelMsg(HWND hwnd, LONG lParam)
2040 {
2041     int so, eo;
2042     WND *wndPtr = WIN_FindWndPtr(hwnd);
2043     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2044
2045     so = LOWORD(lParam);
2046     eo = HIWORD(lParam);
2047     if (so > eo)
2048         swap(&so, &eo);
2049
2050     EDIT_GetLineCol(hwnd, so, &(es->SelBegLine), &(es->SelBegCol));
2051     EDIT_GetLineCol(hwnd, eo, &(es->SelEndLine), &(es->SelEndCol));
2052
2053     es->CurrLine = es->SelEndLine;
2054     es->CurrCol = es->SelEndCol;
2055     es->WndRow = es->SelEndLine - es->wtop;
2056     if (es->WndRow < 0)
2057     {
2058         es->wtop = es->SelEndLine;
2059         es->WndRow = 0;
2060     }
2061     es->WndCol = EDIT_LineLength(es, EDIT_TextLine(hwnd, es->SelEndLine), 
2062                                  es->SelEndCol) - es->wleft;
2063
2064     InvalidateRect(hwnd, NULL, TRUE);
2065     UpdateWindow(hwnd);
2066 }
2067
2068
2069 /*********************************************************************
2070  *  EDIT_GetLineCol
2071  *
2072  *  Return line and column in text buffer from character offset.
2073  */
2074
2075 void EDIT_GetLineCol(HWND hwnd, int off, int *line, int *col)
2076 {
2077     int lineno;
2078     char *cp, *cp1;
2079     WND *wndPtr = WIN_FindWndPtr(hwnd);
2080     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2081     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
2082     unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
2083
2084     if (off > strlen(text)) off = strlen(text);
2085     cp1 = text;
2086     for (lineno = 0; lineno < es->wlines; lineno++)
2087     {
2088         cp = text + *(textPtrs + lineno);
2089         if (off == (int)(cp - text))
2090         {
2091             *line = lineno;
2092             *col = 0;
2093             return;
2094         }
2095         if (off < (int)(cp - text))
2096             break;
2097         cp1 = cp;
2098     }
2099     *line = lineno - 1;
2100     *col = off - (int)(cp1 - text);
2101     if (*(text + *col) == '\0')
2102         (*col)--;
2103 }
2104
2105
2106 /*********************************************************************
2107  *  EDIT_DeleteSel
2108  *
2109  *  Delete the current selected text (if any)
2110  */
2111
2112 void EDIT_DeleteSel(HWND hwnd)
2113 {
2114     char *bbl, *bel;
2115     int len;
2116     WND *wndPtr = WIN_FindWndPtr(hwnd);
2117     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2118     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
2119
2120     if (SelMarked(es))
2121     {
2122         bbl = EDIT_TextLine(hwnd, es->SelBegLine) + es->SelBegCol;
2123         bel = EDIT_TextLine(hwnd, es->SelEndLine) + es->SelEndCol;
2124         len = (int)(bel - bbl);
2125         es->TextChanged = TRUE;
2126         strcpy(bbl, bel);
2127
2128         es->CurrLine = es->SelBegLine;
2129         es->CurrCol = es->SelBegCol;
2130         es->WndRow = es->SelBegLine - es->wtop;
2131         if (es->WndRow < 0)
2132         {
2133             es->wtop = es->SelBegLine;
2134             es->WndRow = 0;
2135         }
2136         es->WndCol = EDIT_LineLength(es, bbl - es->SelBegCol, 
2137                                      es->SelBegCol) - es->wleft;
2138
2139         EDIT_BuildTextPointers(hwnd);
2140         es->PaintBkgd = TRUE;
2141         EDIT_ClearSel(hwnd);
2142     }
2143 }
2144     
2145
2146 /*********************************************************************
2147  *  EDIT_ClearSel
2148  *
2149  *  Clear the current selection.
2150  */
2151
2152 void EDIT_ClearSel(HWND hwnd)
2153 {
2154     WND *wndPtr = WIN_FindWndPtr(hwnd);
2155     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2156
2157     es->SelBegLine = es->SelBegCol = -1;
2158     es->SelEndLine = es->SelEndCol = -1;
2159
2160     InvalidateRect(hwnd, NULL, TRUE);
2161     UpdateWindow(hwnd);
2162 }
2163
2164
2165 /*********************************************************************
2166  *  EDIT_TextLineNumber
2167  *
2168  *  Return the line number in the text buffer of the supplied
2169  *  character pointer.
2170  */
2171
2172 int EDIT_TextLineNumber(HWND hwnd, char *lp)
2173 {
2174     int lineno;
2175     char *cp;
2176     WND *wndPtr = WIN_FindWndPtr(hwnd);
2177     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2178     char *text = (char *)EDIT_HEAP_ADDR(es->hText);
2179     unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
2180
2181     for (lineno = 0; lineno < es->wlines; lineno++)
2182     {
2183         cp = text + *(textPtrs + lineno);
2184         if (cp == lp)
2185             return lineno;
2186         if (cp > lp)
2187             break;
2188     }
2189     return lineno - 1;
2190 }
2191
2192
2193 /*********************************************************************
2194  *  EDIT_SetAnchor
2195  *
2196  *  Set down anchor for text marking.
2197  */
2198
2199 void EDIT_SetAnchor(HWND hwnd, int row, int col)
2200 {
2201     WND *wndPtr = WIN_FindWndPtr(hwnd);
2202     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2203
2204     EDIT_ClearSel(hwnd);
2205     es->SelBegLine = es->SelEndLine = row;
2206     es->SelBegCol = es->SelEndCol = col;
2207     InvalidateRect(hwnd, NULL, FALSE);
2208     UpdateWindow(hwnd);
2209 }
2210
2211
2212 /*********************************************************************
2213  *  EDIT_ExtendSel
2214  *
2215  *  Extend selection to the given screen co-ordinates.
2216  */
2217
2218 void EDIT_ExtendSel(HWND hwnd, int x, int y)
2219 {
2220     int bbl, bel;
2221     int ptop, pbot;
2222     char *cp, *cp1;
2223     int len;
2224     BOOL end = FALSE;
2225     WND *wndPtr = WIN_FindWndPtr(hwnd);
2226     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2227
2228 #ifdef DEBUG_EDIT
2229     printf("EDIT_ExtendSel: x=%d, y=%d\n", x, y);
2230 #endif
2231
2232     ptop = min(es->SelBegLine, es->SelEndLine);
2233     pbot = max(es->SelBegLine, es->SelEndLine);
2234     cp = EDIT_TextLine(hwnd, es->wtop + y / es->txtht);
2235     cp1 = strchr(cp, '\n');
2236     len = cp1 ? (int)(cp1 - cp) : 0;
2237
2238     es->WndRow = y / es->txtht;
2239     if (es->WndRow > es->wlines - es->wtop - 1)
2240     {
2241         if (es->wlines)
2242             es->WndRow = es->wlines - es->wtop - 1;
2243         else
2244             es->WndRow = 0;
2245         end = TRUE;
2246     }
2247     es->CurrLine = es->wtop + es->WndRow;
2248     es->SelEndLine = es->CurrLine;
2249
2250     es->WndCol = x;
2251     if (es->WndCol > EDIT_LineLength(es, cp, len) - es->wleft || end)
2252         es->WndCol = EDIT_LineLength(es, cp, len) - es->wleft;
2253     es->CurrCol = EDIT_PixelToChar(hwnd, es->CurrLine, &(es->WndCol));
2254     es->SelEndCol = es->CurrCol;
2255
2256     bbl = min(es->SelBegLine, es->SelEndLine);
2257     bel = max(es->SelBegLine, es->SelEndLine);
2258     while (ptop < bbl)
2259     {
2260         EDIT_WriteTextLine(hwnd, NULL, ptop);
2261         ptop++;
2262     }
2263     for (y = bbl; y <= bel; y++)
2264         EDIT_WriteTextLine(hwnd, NULL, y);
2265     while (pbot > bel)
2266     {
2267         EDIT_WriteTextLine(hwnd, NULL, pbot);
2268         --pbot;
2269     }
2270 }
2271
2272
2273 /*********************************************************************
2274  *  EDIT_StopMarking
2275  *
2276  *  Stop text marking (selection).
2277  */
2278
2279 void EDIT_StopMarking(HWND hwnd)
2280 {
2281     WND *wndPtr = WIN_FindWndPtr(hwnd);
2282     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2283
2284     TextMarking = FALSE;
2285     if (es->SelBegLine > es->SelEndLine)
2286     {
2287         swap(&(es->SelBegLine), &(es->SelEndLine));
2288         swap(&(es->SelBegCol), &(es->SelEndCol));
2289     }
2290     if (es->SelBegLine == es->SelEndLine && es->SelBegCol > es->SelEndCol)
2291         swap(&(es->SelBegCol), &(es->SelEndCol));
2292 }
2293
2294
2295 /*********************************************************************
2296  *  EM_GETLINE message function
2297  */
2298
2299 LONG EDIT_GetLineMsg(HWND hwnd, WORD wParam, LONG lParam)
2300 {
2301     char *cp, *cp1;
2302     int len;
2303     char *buffer = (char *)lParam;
2304     WND *wndPtr = WIN_FindWndPtr(hwnd);
2305     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2306
2307     cp = EDIT_TextLine(hwnd, wParam);
2308     cp1 = EDIT_TextLine(hwnd, wParam + 1);
2309     len = min((int)(cp1 - cp), (WORD)(*buffer));
2310     strncpy(buffer, cp, len);
2311
2312     return (LONG)len;
2313 }
2314
2315
2316 /*********************************************************************
2317  *  EM_GETSEL message function
2318  */
2319
2320 LONG EDIT_GetSelMsg(HWND hwnd)
2321 {
2322     int so, eo;
2323     WND *wndPtr = WIN_FindWndPtr(hwnd);
2324     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2325     unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
2326
2327     so = *(textPtrs + es->SelBegLine) + es->SelBegCol;
2328     eo = *(textPtrs + es->SelEndLine) + es->SelEndCol;
2329
2330     return MAKELONG(so, eo);
2331 }
2332
2333
2334 /*********************************************************************
2335  *  EM_LINEFROMCHAR message function
2336  */
2337
2338 LONG EDIT_LineFromCharMsg(HWND hwnd, WORD wParam)
2339 {
2340     int row, col;
2341     WND *wndPtr = WIN_FindWndPtr(hwnd);
2342     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2343
2344     if (wParam == (WORD)-1)
2345         return (LONG)(es->SelBegLine);
2346     else
2347         EDIT_GetLineCol(hwnd, wParam, &row, &col);
2348
2349     return (LONG)row;
2350 }
2351
2352
2353 /*********************************************************************
2354  *  EM_LINEINDEX message function
2355  */
2356
2357 LONG EDIT_LineIndexMsg(HWND hwnd, WORD wParam)
2358 {
2359     WND *wndPtr = WIN_FindWndPtr(hwnd);
2360     EDITSTATE *es = (EDITSTATE *)EDIT_HEAP_ADDR((HANDLE)(*(wndPtr->wExtra)));
2361     unsigned int *textPtrs = (unsigned int *)EDIT_HEAP_ADDR(es->hTextPtrs);
2362
2363     if (wParam == (WORD)-1)
2364         wParam = es->CurrLine;
2365
2366     return (LONG)(*(textPtrs + wParam));
2367 }
2368
2369
2370 /*********************************************************************
2371  *  Utility functions
2372  */
2373
2374 void swap(int *a, int *b)
2375 {
2376     int x;
2377
2378     x = *a;
2379     *a = *b;
2380     *b = x;
2381 }
2382