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