1 /* Copyright (C) 2001-2012 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 
18 /* Microsoft Windows text window for Ghostscript. */
19 
20 #include <stdlib.h>
21 #include <string.h> 	/* use only far items */
22 #include <wchar.h>
23 #include <ctype.h>
24 
25 #define STRICT
26 #include <windowsx.h>
27 #include "dwtext.h"
28 #include <commdlg.h>
29 #include <shellapi.h>
30 
31 #ifdef WINDOWS_NO_UNICODE
32 #define CHARSIZE 1
33 #else
34 #define CHARSIZE 2
35 #ifndef WM_UNICHAR
36 #define WM_UNICHAR 0x109
37 #endif
38 #endif
39 
40 /* Define  min and max, but make sure to use the identical definition */
41 /* to the one that all the compilers seem to have.... */
42 #ifndef min
43 #  define min(a, b) (((a) < (b)) ? (a) : (b))
44 #endif
45 #ifndef max
46 #  define max(a, b) (((a) > (b)) ? (a) : (b))
47 #endif
48 
49 #ifndef EOF
50 #define EOF (-1)
51 #endif
52 
53 /* sysmenu */
54 #define M_COPY_CLIP 1
55 #define M_PASTE_CLIP 2
56 
57 LRESULT CALLBACK WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
58 static void text_error(char *message);
59 static void text_new_line(TW *tw);
60 static void text_update_text(TW *tw, int count);
61 static void text_drag_drop(TW *tw, HDROP hdrop);
62 static void text_copy_to_clipboard(TW *tw);
63 static void text_paste_from_clipboard(TW *tw);
64 
65 #ifdef WINDOWS_NO_UNICODE
66 static const char* TextWinClassName = "rjlTextWinClass";
67 #else
68 static const wchar_t* TextWinClassName = L"rjlTextWinClass";
69 #endif
70 static const POINT TextWinMinSize = {16, 4};
71 
72 #if !defined(WINDOWS_NO_UNICODE) && defined(_MSC_VER) && _MSC_VER < 1400
73 /*
74  * 'wmemset()' is documented for both Visual Studio 6.0 and .NET 2003, but
75  * a bug in the shipped "wctype.h" makes it available only for C++ programs.
76  * As this is an inline function, it's not found in the libs.
77  */
wmemset(wchar_t * buf,wchar_t w,int count)78 static wchar_t *wmemset(wchar_t *buf, wchar_t w, int count) {
79     while (count > 0)
80         buf[--count] = w;
81     return buf;
82 }
83 #endif
84 
85 static void
text_error(char * message)86 text_error(char *message)
87 {
88     MessageBoxA((HWND)NULL,message,(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
89 }
90 
91 static void
UpdateScrollBarX(TW * tw)92 UpdateScrollBarX(TW *tw)
93 {
94     SCROLLINFO si;
95     si.cbSize = sizeof(si);
96     si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
97     si.nMin = 0;
98     si.nTrackPos = 0;
99     si.nPage = tw->ClientSize.x;
100     si.nMax = tw->CharSize.x*tw->ScreenSize.x-1;
101     si.nPos = tw->ScrollPos.x;
102     SetScrollInfo(tw->hwnd, SB_HORZ, &si, TRUE);
103 }
104 
105 static void
UpdateScrollBarY(TW * tw)106 UpdateScrollBarY(TW *tw)
107 {
108     SCROLLINFO si;
109     si.cbSize = sizeof(si);
110     si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
111     si.nMin = 0;
112     si.nTrackPos = 0;
113     si.nPage = tw->ClientSize.y;
114     si.nMax = tw->CharSize.y*tw->ScreenSize.y-1;
115     si.nPos = tw->ScrollPos.y;
116     SetScrollInfo(tw->hwnd, SB_VERT, &si, TRUE);
117 }
118 
119 /* Bring Cursor into text window */
120 void
text_to_cursor(TW * tw)121 text_to_cursor(TW *tw)
122 {
123 int nXinc=0;
124 int nYinc=0;
125 int cxCursor;
126 int cyCursor;
127         cyCursor = tw->CursorPos.y * tw->CharSize.y;
128         if ( (cyCursor + tw->CharSize.y > tw->ScrollPos.y + tw->ClientSize.y)
129 /*	  || (cyCursor < tw->ScrollPos.y) ) { */
130 /* change to scroll to end of window instead of just making visible */
131 /* so that ALL of error message can be seen */
132           || (cyCursor < tw->ScrollPos.y+tw->ClientSize.y) ) {
133                 nYinc = max(0, cyCursor + tw->CharSize.y
134                         - tw->ClientSize.y) - tw->ScrollPos.y;
135                 nYinc = min(nYinc, tw->ScrollMax.y - tw->ScrollPos.y);
136         }
137         cxCursor = tw->CursorPos.x * tw->CharSize.x;
138         if ( (cxCursor + tw->CharSize.x > tw->ScrollPos.x + tw->ClientSize.x)
139           || (cxCursor < tw->ScrollPos.x) ) {
140                 nXinc = max(0, cxCursor + tw->CharSize.x
141                         - tw->ClientSize.x/2) - tw->ScrollPos.x;
142                 nXinc = min(nXinc, tw->ScrollMax.x - tw->ScrollPos.x);
143         }
144         if (nYinc || nXinc) {
145                 tw->ScrollPos.y += nYinc;
146                 tw->ScrollPos.x += nXinc;
147                 ScrollWindow(tw->hwnd,-nXinc,-nYinc,NULL,NULL);
148                 UpdateScrollBarX(tw);
149                 UpdateScrollBarY(tw);
150                 UpdateWindow(tw->hwnd);
151         }
152 }
153 
154 static void
text_new_line(TW * tw)155 text_new_line(TW *tw)
156 {
157         tw->CursorPos.x = 0;
158         tw->CursorPos.y++;
159         if (tw->CursorPos.y >= tw->ScreenSize.y) {
160             int i =  tw->ScreenSize.x * (tw->ScreenSize.y - 1);
161                 memmove(tw->ScreenBuffer, tw->ScreenBuffer+tw->ScreenSize.x,
162                         i * CHARSIZE);
163 #ifdef WINDOWS_NO_UNICODE
164                 memset(tw->ScreenBuffer + i, ' ', tw->ScreenSize.x);
165 #else
166                 wmemset(tw->ScreenBuffer + i, ' ', tw->ScreenSize.x);
167 #endif
168                 tw->CursorPos.y--;
169                 ScrollWindow(tw->hwnd,0,-tw->CharSize.y,NULL,NULL);
170                 UpdateWindow(tw->hwnd);
171         }
172         if (tw->CursorFlag)
173                 text_to_cursor(tw);
174 
175 /*	TextMessage(); */
176 }
177 
178 /* Update count characters in window at cursor position */
179 /* Updates cursor position */
180 static void
text_update_text(TW * tw,int count)181 text_update_text(TW *tw, int count)
182 {
183 HDC hdc;
184 int xpos, ypos;
185         xpos = tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x;
186         ypos = tw->CursorPos.y*tw->CharSize.y - tw->ScrollPos.y;
187         hdc = GetDC(tw->hwnd);
188         SelectFont(hdc, tw->hfont);
189 #ifdef WINDOWS_NO_UNICODE
190         TextOut(hdc,xpos,ypos,
191                 (LPSTR)(tw->ScreenBuffer + tw->CursorPos.y*tw->ScreenSize.x
192                 + tw->CursorPos.x), count);
193 #else
194         TextOutW(hdc,xpos,ypos,
195                  (LPCWSTR)(tw->ScreenBuffer +
196                            tw->CursorPos.y*tw->ScreenSize.x +
197                            tw->CursorPos.x),
198                  count);
199 #endif
200         (void)ReleaseDC(tw->hwnd,hdc);
201         tw->CursorPos.x += count;
202         if (tw->CursorPos.x >= tw->ScreenSize.x)
203                 text_new_line(tw);
204 }
205 
206 void
text_size(TW * tw,int width,int height)207 text_size(TW *tw, int width, int height)
208 {
209     tw->ScreenSize.x =  max(width, TextWinMinSize.x);
210     tw->ScreenSize.y =  max(height, TextWinMinSize.y);
211 }
212 
213 void
text_font(TW * tw,const char * name,int size)214 text_font(TW *tw, const char *name, int size)
215 {
216     /* make a new font */
217     LOGFONTA lf;
218     TEXTMETRIC tm;
219     LPSTR p;
220     HDC hdc;
221 
222     /* reject inappropriate arguments */
223     if (name == NULL)
224         return;
225     if (size < 4)
226         return;
227 
228     /* set new name and size */
229     free(tw->fontname);
230     tw->fontname = (char *)malloc(strlen(name)+1);
231     if (tw->fontname == NULL)
232         return;
233     strcpy(tw->fontname, name);
234     tw->fontsize = size;
235 
236     /* if window not open, hwnd == 0 == HWND_DESKTOP */
237     hdc = GetDC(tw->hwnd);
238     memset(&lf, 0, sizeof(LOGFONTA));
239     strncpy(lf.lfFaceName,tw->fontname,LF_FACESIZE);
240     lf.lfHeight = -MulDiv(tw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
241     lf.lfPitchAndFamily = FIXED_PITCH;
242     lf.lfCharSet = DEFAULT_CHARSET;
243     if ( (p = strstr(tw->fontname," Italic")) != (LPSTR)NULL ) {
244         lf.lfFaceName[ (unsigned int)(p-tw->fontname) ] = '\0';
245         lf.lfItalic = TRUE;
246     }
247     if ( (p = strstr(tw->fontname," Bold")) != (LPSTR)NULL ) {
248         lf.lfFaceName[ (unsigned int)(p-tw->fontname) ] = '\0';
249         lf.lfWeight = FW_BOLD;
250     }
251     if (tw->hfont)
252         DeleteFont(tw->hfont);
253 
254     tw->hfont = CreateFontIndirectA((LOGFONTA FAR *)&lf);
255 
256     /* get text size */
257     SelectFont(hdc, tw->hfont);
258     GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
259     tw->CharSize.y = tm.tmHeight;
260     tw->CharSize.x = tm.tmAveCharWidth;
261     tw->CharAscent = tm.tmAscent;
262     if (tw->bFocus)
263         CreateCaret(tw->hwnd, 0, tw->CharSize.x, 2+tw->CaretHeight);
264     ReleaseDC(tw->hwnd, hdc);
265 
266     /* redraw window if necessary */
267     if (tw->hwnd != HWND_DESKTOP) {
268         /* INCOMPLETE */
269     }
270 }
271 
272 /* Set drag strings */
273 void
text_drag(TW * tw,const char * pre,const char * post)274 text_drag(TW *tw, const char *pre, const char *post)
275 {
276     /* remove old strings */
277     free((char *)tw->DragPre);
278     tw->DragPre = NULL;
279     free((char *)tw->DragPost);
280     tw->DragPost = NULL;
281 
282     /* add new strings */
283     tw->DragPre = malloc(strlen(pre)+1);
284     if (tw->DragPre)
285         strcpy(tw->DragPre, pre);
286     tw->DragPost = malloc(strlen(post)+1);
287     if (tw->DragPost)
288         strcpy(tw->DragPost, post);
289 }
290 
291 /* Set the window position and size */
292 void
text_setpos(TW * tw,int x,int y,int cx,int cy)293 text_setpos(TW *tw, int x, int y, int cx, int cy)
294 {
295     tw->x = x;
296     tw->y = y;
297     tw->cx = cx;
298     tw->cy = cy;
299 }
300 
301 /* Get the window position and size */
text_getpos(TW * tw,int * px,int * py,int * pcx,int * pcy)302 int text_getpos(TW *tw, int *px, int *py, int *pcx, int *pcy)
303 {
304     *px = tw->x;
305     *py = tw->y;
306     *pcx = tw->cx;
307     *pcy = tw->cy;
308     return 0;
309 }
310 
311 /* Allocate new text window */
312 TW *
text_new(void)313 text_new(void)
314 {
315     TW *tw;
316     tw = (TW *)malloc(sizeof(TW));
317     if (tw == NULL)
318         return NULL;
319     /* make sure everything is null */
320     memset(tw, 0, sizeof(TW));
321 
322     /* set some defaults */
323     text_font(tw, "Courier New", 10);
324     text_size(tw, 80, 24);
325     tw->KeyBufSize = 2048;
326     tw->CursorFlag = 1;	/* scroll to cursor after \n or \r */
327     tw->hwnd = HWND_DESKTOP;
328 
329     tw->line_end = 0;
330     tw->line_start = 0;
331     tw->line_complete = FALSE;
332     tw->line_eof = FALSE;
333 
334     tw->x = CW_USEDEFAULT;
335     tw->y = CW_USEDEFAULT;
336     tw->cx = CW_USEDEFAULT;
337     tw->cy = CW_USEDEFAULT;
338 #ifndef WINDOWS_NO_UNICODE
339     tw->utf8shift = 0;
340 #endif
341     return tw;
342 }
343 
344 /* Destroy window and deallocate text window structure */
345 void
text_destroy(TW * tw)346 text_destroy(TW *tw)
347 {
348     if (tw->hwnd)
349         DestroyWindow(tw->hwnd);
350     tw->hwnd = HWND_DESKTOP;
351 
352     if (tw->hfont)
353         DeleteFont(tw->hfont);
354     tw->hfont = NULL;
355 
356     free((char *)tw->KeyBuf);
357     tw->KeyBuf = NULL;
358 
359     free((char *)tw->ScreenBuffer);
360     tw->ScreenBuffer = NULL;
361 
362     free((char *)tw->DragPre);
363     tw->DragPre = NULL;
364 
365     free((char *)tw->DragPost);
366     tw->DragPost = NULL;
367 
368     free((char *)tw->fontname);
369     tw->fontname = NULL;
370 
371 #ifndef WINDOWS_NO_UNICODE
372     free((char *)tw->TitleW);
373     tw->TitleW = NULL;
374 #endif
375 }
376 
377 /* register the window class */
378 int
text_register_class(TW * tw,HICON hicon)379 text_register_class(TW *tw, HICON hicon)
380 {
381 #ifdef WINDOWS_NO_UNICODE
382     WNDCLASS wndclass;
383 #else
384     WNDCLASSW wndclass;
385 #endif
386     HINSTANCE hInstance = GetModuleHandle(NULL);
387     tw->hIcon = hicon;
388 
389     /* register window class */
390     wndclass.style = CS_HREDRAW | CS_VREDRAW;
391     wndclass.lpfnWndProc = WndTextProc;
392     wndclass.cbClsExtra = 0;
393     wndclass.cbWndExtra = sizeof(void *);
394     wndclass.hInstance = hInstance;
395     wndclass.hIcon = tw->hIcon ? tw->hIcon : LoadIcon(NULL, IDI_APPLICATION);
396     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
397     wndclass.hbrBackground = GetStockBrush(WHITE_BRUSH);
398     wndclass.lpszMenuName = NULL;
399     wndclass.lpszClassName = TextWinClassName;
400 #ifdef WINDOWS_NO_UNICODE
401     return RegisterClass(&wndclass);
402 #else
403     return RegisterClassW(&wndclass);
404 #endif
405 }
406 
407 /* Show the window */
text_create(TW * tw,const char * app_name,int show_cmd)408 int text_create(TW *tw, const char *app_name, int show_cmd)
409 {
410     HMENU sysmenu;
411     HINSTANCE hInstance = GetModuleHandle(NULL);
412 #ifndef WINDOWS_NO_UNICODE
413     wchar_t *app_nameW, *d;
414     const char *s;
415 
416     app_nameW = malloc(strlen(app_name)*2+2);
417     if (app_nameW == NULL) {
418         text_error("Out of memory");
419         return 1;
420     }
421     d = app_nameW;
422     s = app_name;
423     while ((*d++ = (wchar_t)(unsigned char)(*s++)) != 0);
424     tw->TitleW = app_nameW;
425 #endif
426 
427     tw->Title = app_name;
428     tw->nCmdShow = show_cmd;
429     tw->quitnow = FALSE;
430 
431     /* make sure we have some sensible defaults */
432     if (tw->KeyBufSize < 256)
433         tw->KeyBufSize = 256;
434 
435     tw->CursorPos.x = tw->CursorPos.y = 0;
436     tw->bFocus = FALSE;
437     tw->bGetCh = FALSE;
438     tw->CaretHeight = 0;
439 
440     /* allocate buffers */
441     tw->KeyBufIn = tw->KeyBufOut = tw->KeyBuf = malloc(tw->KeyBufSize);
442     if (tw->KeyBuf == NULL) {
443         text_error("Out of memory");
444         return 1;
445     }
446     tw->ScreenBuffer = malloc(tw->ScreenSize.x * tw->ScreenSize.y * CHARSIZE);
447     if (tw->ScreenBuffer == NULL) {
448         text_error("Out of memory");
449         return 1;
450     }
451 #ifdef WINDOWS_NO_UNICODE
452     memset(tw->ScreenBuffer, ' ', tw->ScreenSize.x * tw->ScreenSize.y);
453 #else
454     wmemset(tw->ScreenBuffer, ' ', tw->ScreenSize.x * tw->ScreenSize.y);
455 #endif
456 
457 #ifdef WINDOWS_NO_UNICODE
458     tw->hwnd = CreateWindow(TextWinClassName, tw->Title,
459                   WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
460                   tw->x, tw->y, tw->cx, tw->cy,
461                   NULL, NULL, hInstance, tw);
462 #else
463     tw->hwnd = CreateWindowW(TextWinClassName, app_nameW,
464                   WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
465                   tw->x, tw->y, tw->cx, tw->cy,
466                   NULL, NULL, hInstance, tw);
467 #endif
468 
469     if (tw->hwnd == NULL) {
470         MessageBoxA((HWND)NULL,"Couldn't open text window",(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
471         return 1;
472     }
473 
474     ShowWindow(tw->hwnd, tw->nCmdShow);
475     sysmenu = GetSystemMenu(tw->hwnd,0);	/* get the sysmenu */
476     AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
477     AppendMenuA(sysmenu, MF_STRING, M_COPY_CLIP, "Copy to Clip&board");
478     AppendMenuA(sysmenu, MF_STRING, M_PASTE_CLIP, "&Paste");
479 
480     return 0;
481 }
482 
483 int
text_putch(TW * tw,int ch)484 text_putch(TW *tw, int ch)
485 {
486 int pos;
487 int n;
488 #ifndef WINDOWS_NO_UNICODE
489 int shift = tw->utf8shift;
490     tw->utf8shift=0;
491 #endif
492     if (tw->quitnow)
493         return ch;	/* don't write error message as we shut down */
494     switch(ch) {
495         case '\r':
496                 tw->CursorPos.x = 0;
497                 if (tw->CursorFlag)
498                     text_to_cursor(tw);
499                 break;
500         case '\n':
501                 text_new_line(tw);
502                 break;
503         case 7:
504                 MessageBeep(-1);
505                 if (tw->CursorFlag)
506                     text_to_cursor(tw);
507                 break;
508         case '\t':
509                 {
510                     for (n = 8 - (tw->CursorPos.x % 8); n>0; n-- )
511                             text_putch(tw, ' ');
512                 }
513                 break;
514         case 0x08:
515         case 0x7f:
516                 tw->CursorPos.x--;
517                 if (tw->CursorPos.x < 0) {
518                     tw->CursorPos.x = tw->ScreenSize.x - 1;
519                     tw->CursorPos.y--;
520                 }
521                 if (tw->CursorPos.y < 0)
522                     tw->CursorPos.y = 0;
523                 break;
524         default:
525                 pos = tw->CursorPos.y*tw->ScreenSize.x + tw->CursorPos.x;
526 #ifndef WINDOWS_NO_UNICODE
527                 /* Are we continuing a unicode char? */
528                 if ((ch & 0xC0) == 0x80) {
529                     tw->ScreenBuffer[pos] |= (ch & 0x3F)<<shift;
530                     if (shift > 0)
531                         tw->utf8shift = shift-6;
532                     else
533                         text_update_text(tw, 1); /* Only update when complete */
534                 } else if (ch >= 0xe0) { /* 2 more to come */
535                     tw->ScreenBuffer[pos] = (ch & 0x0f)<<12;
536                     tw->utf8shift = 6;
537                 } else if (ch >= 0xC0) { /* 1 more to come */
538                     tw->ScreenBuffer[pos] = (ch & 0x01f)<<6;
539                     tw->utf8shift = 0;
540                 }
541                 else
542 #endif
543                 {
544                     tw->ScreenBuffer[pos] = ch;
545                     text_update_text(tw, 1);
546                 }
547     }
548     return ch;
549 }
550 
551 void
text_write_buf(TW * tw,const char * str,int cnt)552 text_write_buf(TW *tw, const char *str, int cnt)
553 {
554 #ifdef WINDOWS_NO_UNICODE
555 BYTE *p;
556 #else
557 wchar_t *p;
558 #endif
559 int count, limit;
560 int n;
561     if (tw->quitnow)
562         return;		/* don't write error message as we shut down */
563     while (cnt>0) {
564         p = tw->ScreenBuffer + tw->CursorPos.y*tw->ScreenSize.x + tw->CursorPos.x;
565         limit = tw->ScreenSize.x - tw->CursorPos.x;
566         for (count=0; (count < limit) && (cnt>0) &&
567             (
568 #ifdef WINDOWS_NO_UNICODE
569              isprint((unsigned char)(*str))
570 #else
571              ((*str >= 32) && (*str <= 0x7F))
572 #endif
573              || *str=='\t'); count++) {
574             if (*str=='\t') {
575                 for (n = 8 - ((tw->CursorPos.x+count) % 8); (count < limit) & (n>0); n--, count++ )
576                     *p++ = ' ';
577                 str++;
578                 count--;
579             }
580             else {
581                 *p++ = *str++;
582             }
583             cnt--;
584         }
585         if (count>0) {
586             text_update_text(tw, count);
587         }
588         if (cnt > 0) {
589             if (*str=='\n') {
590                 text_new_line(tw);
591                 str++;
592                 cnt--;
593             }
594             else if (!
595 #ifdef WINDOWS_NO_UNICODE
596                 isprint((unsigned char)(*str))
597 #else
598                 ((*str >= 32) && (*str <= 0x7f))
599 #endif
600                 && *str!='\t') {
601                 text_putch(tw, *(const unsigned char *)str++);
602                 cnt--;
603             }
604         }
605     }
606 }
607 
608 /* Put string to window */
609 void
text_puts(TW * tw,const char * str)610 text_puts(TW *tw, const char *str)
611 {
612     text_write_buf(tw, str, strlen(str));
613 }
614 
615 /* TRUE if key hit, FALSE if no key */
616 int
text_kbhit(TW * tw)617 text_kbhit(TW *tw)
618 {
619     return (tw->KeyBufIn != tw->KeyBufOut);
620 }
621 
622 /* get character from keyboard, no echo */
623 /* need to add extended codes */
624 int
text_getch(TW * tw)625 text_getch(TW *tw)
626 {
627     MSG msg;
628     int ch;
629     text_to_cursor(tw);
630     tw->bGetCh = TRUE;
631     if (tw->bFocus) {
632         SetCaretPos(tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x,
633             tw->CursorPos.y*tw->CharSize.y + tw->CharAscent
634             - tw->CaretHeight - tw->ScrollPos.y);
635         ShowCaret(tw->hwnd);
636     }
637 
638 #ifdef WINDOWS_NO_UNICODE
639     while (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_NOREMOVE)) {
640         if (GetMessage(&msg, (HWND)NULL, 0, 0)) {
641             TranslateMessage(&msg);
642             DispatchMessage(&msg);
643         }
644     }
645 #else
646     while (PeekMessageW(&msg, (HWND)NULL, 0, 0, PM_NOREMOVE)) {
647         if (GetMessageW(&msg, (HWND)NULL, 0, 0)) {
648             TranslateMessage(&msg);
649             DispatchMessageW(&msg);
650         }
651     }
652 #endif
653 
654     if (tw->quitnow)
655        return EOF;	/* window closed */
656 
657     while (!text_kbhit(tw)) {
658         if (!tw->quitnow) {
659 #ifdef WINDOWS_NO_UNICODE
660             if (GetMessage(&msg, (HWND)NULL, 0, 0)) {
661                 TranslateMessage(&msg);
662                 DispatchMessage(&msg);
663             }
664 #else
665             if (GetMessageW(&msg, (HWND)NULL, 0, 0)) {
666                 TranslateMessage(&msg);
667                 DispatchMessageW(&msg);
668             }
669 #endif
670         }
671         else
672            return EOF;	/* window closed */
673     }
674 
675     ch = *tw->KeyBufOut++;
676     if (ch=='\r')
677         ch = '\n';
678     if (tw->KeyBufOut - tw->KeyBuf >= tw->KeyBufSize)
679         tw->KeyBufOut = tw->KeyBuf;		/* wrap around */
680     if (tw->bFocus)
681         HideCaret(tw->hwnd);
682     tw->bGetCh = FALSE;
683     return ch;
684 }
685 
686 /* Read line from keyboard using buffered input
687  * Return at most 'len' characters
688  * Does NOT add null terminating character
689  * This is NOT the same as fgets()
690  * Do not mix this with calls to text_getch()
691  */
692 int
text_read_line(TW * tw,char * line,int len)693 text_read_line(TW *tw, char *line, int len)
694 {
695 int ch;
696     if (tw->line_eof)
697         return 0;
698 
699     while (!tw->line_complete) {
700         /* we have not yet collected a full line */
701         ch = text_getch(tw);
702         switch(ch) {
703             case EOF:
704             case 26:	/* ^Z == EOF */
705                 tw->line_eof = TRUE;
706                 tw->line_complete = TRUE;
707                 break;
708             case '\b':	/* ^H */
709             case 0x7f:  /* DEL */
710                 if (tw->line_end) {
711                     text_putch(tw, '\b');
712                     text_putch(tw, ' ');
713                     text_putch(tw, '\b');
714 #ifndef WINDOWS_NO_UNICODE
715                     while ((tw->line_end) &&
716                            ((tw->line_buf[tw->line_end-1] & 0xC0) == 0x80)) {
717                         /* It's a UTF-8 continuation char. */
718                         --(tw->line_end);
719                     }
720                     if (tw->line_end == 0)
721                         break;
722 #endif
723                     --(tw->line_end);
724                 }
725                 break;
726             case 21:	/* ^U */
727                 while (tw->line_end) {
728                     text_putch(tw, '\b');
729                     text_putch(tw, ' ');
730                     text_putch(tw, '\b');
731                     --(tw->line_end);
732                 }
733                 break;
734             case '\r':
735             case '\n':
736                 tw->line_complete = TRUE;
737                 /* fall through */
738             default:
739                 tw->line_buf[tw->line_end++] = ch;
740                 text_putch(tw, ch);
741                 break;
742         }
743         if (tw->line_end >= sizeof(tw->line_buf))
744             tw->line_complete = TRUE;
745     }
746 
747     if (tw->quitnow)
748         return -1;
749 
750     if (tw->line_complete) {
751         /* We either filled the buffer or got CR, LF or EOF */
752         int count = min(len, tw->line_end - tw->line_start);
753         memcpy(line, tw->line_buf + tw->line_start, count);
754         tw->line_start += count;
755         if (tw->line_start == tw->line_end) {
756             tw->line_start = tw->line_end = 0;
757             tw->line_complete = FALSE;
758         }
759         return count;
760     }
761 
762     return 0;
763 }
764 
765 /* Read a string from the keyboard, of up to len characters */
766 /* (not including trailing NULL) */
767 int
text_gets(TW * tw,char * line,int len)768 text_gets(TW *tw, char *line, int len)
769 {
770 LPSTR dest = line;
771 LPSTR limit = dest + len;  /* don't leave room for '\0' */
772 int ch;
773     do {
774         if (dest >= limit)
775             break;
776         ch = text_getch(tw);
777         switch(ch) {
778             case 26:	/* ^Z == EOF */
779                 return 0;
780             case '\b':	/* ^H */
781             case 0x7f:  /* DEL */
782                 if (dest > line) {
783                     text_putch(tw, '\b');
784                     text_putch(tw, ' ');
785                     text_putch(tw, '\b');
786 #ifndef WINDOWS_NO_UNICODE
787                     while ((dest > line) &&
788                            ((dest[-1] & 0xC0) == 0x80)) {
789                         /* It's a UTF-8 continuation char. */
790                         --(dest);
791                     }
792                     if (dest == line)
793                         break;
794 #endif
795                     --dest;
796                 }
797                 break;
798             case 21:	/* ^U */
799                 while (dest > line) {
800                     text_putch(tw, '\b');
801                     text_putch(tw, ' ');
802                     text_putch(tw, '\b');
803                     --dest;
804                 }
805                 break;
806             default:
807                 *dest++ = ch;
808                 text_putch(tw, ch);
809                 break;
810         }
811     } while (ch != '\n');
812 
813     *dest = '\0';
814     return (dest-line);
815 }
816 
817 /* Windows 3.1 drag-drop feature */
818 void
text_drag_drop(TW * tw,HDROP hdrop)819 text_drag_drop(TW *tw, HDROP hdrop)
820 {
821     TCHAR *szFile;
822     int i, cFiles;
823     unsigned int Len, error;
824     const char *p;
825     const TCHAR *t;
826     if ( (tw->DragPre==NULL) || (tw->DragPost==NULL) )
827             return;
828 
829     cFiles = DragQueryFile(hdrop, (UINT)(-1), (LPTSTR)NULL, 0);
830     for (i=0; i<cFiles; i++) {
831         Len = DragQueryFile(hdrop, i, NULL, 0);
832         szFile = (TCHAR *)malloc((Len+1)*sizeof(TCHAR));
833         if (szFile != 0) {
834             error = DragQueryFile(hdrop, i, szFile, Len+1);
835             if (error != 0) {
836                 for (p=tw->DragPre; *p; p++)
837                     SendMessage(tw->hwnd,WM_CHAR,*p,1L);
838                 for (t=szFile; *t; t++) {
839                     if (*t == '\\')
840                         SendMessage(tw->hwnd,WM_CHAR,'/',1L);
841                     else
842                         SendMessage(tw->hwnd,WM_CHAR,*t,1L);
843                 }
844                 for (p=tw->DragPost; *p; p++)
845                     SendMessage(tw->hwnd,WM_CHAR,*p,1L);
846             }
847             free(szFile);
848         }
849     }
850     DragFinish(hdrop);
851 }
852 
853 void
text_copy_to_clipboard(TW * tw)854 text_copy_to_clipboard(TW *tw)
855 {
856     int size, count;
857     HGLOBAL hGMem;
858 #ifdef WINDOWS_NO_UNICODE
859     LPSTR cbuf, cp;
860     HDC hdc;
861     TEXTMETRIC tm;
862 #else
863     LPWSTR cbuf, cp;
864 #endif
865     UINT type;
866     int i;
867 
868     size = tw->ScreenSize.y * (tw->ScreenSize.x + 2) + 1;
869     size *= CHARSIZE;
870     hGMem = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)size);
871 #ifdef WINDOWS_NO_UNICODE
872     cbuf = cp = (LPSTR)GlobalLock(hGMem);
873 #else
874     cbuf = cp = (LPWSTR)GlobalLock(hGMem);
875 #endif
876     if (cp == NULL)
877         return;
878 
879     for (i=0; i<tw->ScreenSize.y; i++) {
880         count = tw->ScreenSize.x;
881         memcpy(cp, tw->ScreenBuffer + tw->ScreenSize.x*i, count*CHARSIZE);
882         /* remove trailing spaces */
883         for (count=count-1; count>=0; count--) {
884                 if (cp[count]!=' ')
885                         break;
886                 cp[count] = '\0';
887         }
888         cp[++count] = '\r';
889         cp[++count] = '\n';
890         cp[++count] = '\0';
891         cp += count;
892     }
893     /* Now remove completely empty trailing lines */
894     while (cp >= cbuf+4) {
895         if ((cp[-3] != '\n') || (cp[-4] != '\r'))
896             break;
897         cp -= 2;
898         *cp = '\0';
899     }
900 #ifdef WINDOWS_NO_UNICODE
901     size = strlen(cbuf) + 1;
902 #else
903     size = CHARSIZE*(wcslen(cbuf) + 1);
904 #endif
905     GlobalUnlock(hGMem);
906     hGMem = GlobalReAlloc(hGMem, (DWORD)size, GHND | GMEM_SHARE);
907 #ifdef WINDOWS_NO_UNICODE
908     /* find out what type to put into clipboard */
909     hdc = GetDC(tw->hwnd);
910     SelectFont(hdc, tw->hfont);
911     GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
912     if (tm.tmCharSet == OEM_CHARSET)
913         type = CF_OEMTEXT;
914     else
915         type = CF_TEXT;
916     ReleaseDC(tw->hwnd, hdc);
917 #else
918     type = CF_UNICODETEXT;
919 #endif
920     /* give buffer to clipboard */
921     OpenClipboard(tw->hwnd);
922     EmptyClipboard();
923     SetClipboardData(type, hGMem);
924     CloseClipboard();
925 }
926 
927 void
text_paste_from_clipboard(TW * tw)928 text_paste_from_clipboard(TW *tw)
929 {
930     HGLOBAL hClipMemory;
931 #ifdef WINDOWS_NO_UNICODE
932     BYTE *p;
933 #else
934     wchar_t *p;
935 #endif
936     long count;
937     OpenClipboard(tw->hwnd);
938 #ifdef WINDOWS_NO_UNICODE
939     if (IsClipboardFormatAvailable(CF_TEXT)) {
940         hClipMemory = GetClipboardData(CF_TEXT);
941 #else
942     if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
943         hClipMemory = GetClipboardData(CF_UNICODETEXT);
944 #endif
945         p = GlobalLock(hClipMemory);
946         while (*p) {
947             /* transfer to keyboard circular buffer */
948             count = tw->KeyBufIn - tw->KeyBufOut;
949             if (count < 0)
950                 count += tw->KeyBufSize;
951 #ifndef WINDOWS_NO_UNICODE
952             /* The clipboard contains unicode, but we put it into the key
953              * buffer as if it was typing utf8 */
954             if (*p >= 0x800) {
955                 if (count < tw->KeyBufSize-3) {
956                     *tw->KeyBufIn++ = 0xE0 | (*p>>12);
957                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
958                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
959                     *tw->KeyBufIn++ = 0x80 | ((*p>>6) & 0x3F);
960                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
961                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
962                     *tw->KeyBufIn++ = 0x80 | (*p & 0x3f);
963                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
964                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
965                 }
966             } else if (*p >= 0x80) {
967                 if (count < tw->KeyBufSize-2) {
968                     *tw->KeyBufIn++ = 0xC0 | (*p>>6);
969                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
970                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
971                     *tw->KeyBufIn++ = 0x80 | (*p & 0x3f);
972                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
973                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
974                 }
975             } else
976 #endif
977             if (count < tw->KeyBufSize-1) {
978                 *tw->KeyBufIn++ = *p;
979                 if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
980                     tw->KeyBufIn = tw->KeyBuf; /* wrap around */
981             }
982             p++;
983         }
984         GlobalUnlock(hClipMemory);
985     }
986     CloseClipboard();
987 }
988 
989 /* text window */
990 LRESULT CALLBACK
991 WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
992 {
993     HDC hdc;
994     PAINTSTRUCT ps;
995     RECT rect;
996     int nYinc, nXinc;
997     TW *tw;
998     if (message == WM_CREATE) {
999         /* Object is stored in window extra data.
1000          * Nothing must try to use the object before WM_CREATE
1001          * initializes it here.
1002          */
1003         tw = (TW *)(((CREATESTRUCT FAR *)lParam)->lpCreateParams);
1004         SetWindowLong(hwnd, 0, (LONG)tw);
1005     }
1006     tw = (TW *)GetWindowLong(hwnd, 0);
1007 
1008     switch(message) {
1009         case WM_SYSCOMMAND:
1010             switch(LOWORD(wParam)) {
1011                 case M_COPY_CLIP:
1012                     text_copy_to_clipboard(tw);
1013                     return 0;
1014                 case M_PASTE_CLIP:
1015                     text_paste_from_clipboard(tw);
1016                     return 0;
1017             }
1018             break;
1019         case WM_SETFOCUS:
1020             tw->bFocus = TRUE;
1021             CreateCaret(hwnd, 0, tw->CharSize.x, 2+tw->CaretHeight);
1022             SetCaretPos(tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x,
1023                     tw->CursorPos.y*tw->CharSize.y + tw->CharAscent
1024                      - tw->CaretHeight - tw->ScrollPos.y);
1025             if (tw->bGetCh)
1026                     ShowCaret(hwnd);
1027             break;
1028         case WM_KILLFOCUS:
1029             DestroyCaret();
1030             tw->bFocus = FALSE;
1031             break;
1032         case WM_MOVE:
1033             if (!IsIconic(hwnd) && !IsZoomed(hwnd)) {
1034                 GetWindowRect(hwnd, &rect);
1035                 tw->x = rect.left;
1036                 tw->y = rect.top;
1037             }
1038             break;
1039         case WM_SIZE:
1040             if (wParam == SIZE_MINIMIZED)
1041                     return(0);
1042 
1043             /* remember current window size */
1044             if (wParam != SIZE_MAXIMIZED) {
1045                 GetWindowRect(hwnd, &rect);
1046                 tw->cx = rect.right - rect.left;
1047                 tw->cy = rect.bottom - rect.top;
1048                 tw->x = rect.left;
1049                 tw->y = rect.top;
1050             }
1051 
1052             tw->ClientSize.y = HIWORD(lParam);
1053             tw->ClientSize.x = LOWORD(lParam);
1054 
1055             tw->ScrollMax.y = max(0, tw->CharSize.y*tw->ScreenSize.y - tw->ClientSize.y);
1056             tw->ScrollPos.y = min(tw->ScrollPos.y, tw->ScrollMax.y);
1057 
1058             UpdateScrollBarY(tw);
1059 
1060             tw->ScrollMax.x = max(0, tw->CharSize.x*tw->ScreenSize.x - tw->ClientSize.x);
1061             tw->ScrollPos.x = min(tw->ScrollPos.x, tw->ScrollMax.x);
1062 
1063             UpdateScrollBarX(tw);
1064 
1065             if (tw->bFocus && tw->bGetCh) {
1066                 SetCaretPos(tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x,
1067                             tw->CursorPos.y*tw->CharSize.y + tw->CharAscent
1068                             - tw->CaretHeight - tw->ScrollPos.y);
1069                 ShowCaret(hwnd);
1070             }
1071             return(0);
1072         case WM_VSCROLL:
1073             switch(LOWORD(wParam)) {
1074                 case SB_TOP:
1075                     nYinc = -tw->ScrollPos.y;
1076                     break;
1077                 case SB_BOTTOM:
1078                     nYinc = tw->ScrollMax.y - tw->ScrollPos.y;
1079                     break;
1080                 case SB_LINEUP:
1081                     nYinc = -tw->CharSize.y;
1082                     break;
1083                 case SB_LINEDOWN:
1084                     nYinc = tw->CharSize.y;
1085                     break;
1086                 case SB_PAGEUP:
1087                     nYinc = min(-1,-tw->ClientSize.y);
1088                     break;
1089                 case SB_PAGEDOWN:
1090                     nYinc = max(1,tw->ClientSize.y);
1091                     break;
1092                 case SB_THUMBPOSITION:
1093                     nYinc = HIWORD(wParam) - tw->ScrollPos.y;
1094                     break;
1095                 default:
1096                     nYinc = 0;
1097             }
1098             if ( (nYinc = max(-tw->ScrollPos.y,
1099                     min(nYinc, tw->ScrollMax.y - tw->ScrollPos.y)))
1100                     != 0 ) {
1101                     tw->ScrollPos.y += nYinc;
1102                     ScrollWindow(hwnd,0,-nYinc,NULL,NULL);
1103                     UpdateScrollBarY(tw);
1104                     UpdateWindow(hwnd);
1105             }
1106             return(0);
1107         case WM_HSCROLL:
1108             switch(LOWORD(wParam)) {
1109                 case SB_LINEUP:
1110                     nXinc = -tw->CharSize.x;
1111                     break;
1112                 case SB_LINEDOWN:
1113                     nXinc = tw->CharSize.x;
1114                     break;
1115                 case SB_PAGEUP:
1116                     nXinc = min(-1,-tw->ClientSize.x);
1117                     break;
1118                 case SB_PAGEDOWN:
1119                     nXinc = max(1,tw->ClientSize.x);
1120                     break;
1121                 case SB_THUMBPOSITION:
1122                     nXinc = HIWORD(wParam) - tw->ScrollPos.x;
1123                     break;
1124                 default:
1125                     nXinc = 0;
1126             }
1127             if ( (nXinc = max(-tw->ScrollPos.x,
1128                     min(nXinc, tw->ScrollMax.x - tw->ScrollPos.x)))
1129                     != 0 ) {
1130                     tw->ScrollPos.x += nXinc;
1131                     ScrollWindow(hwnd,-nXinc,0,NULL,NULL);
1132                     UpdateScrollBarX(tw);
1133                     UpdateWindow(hwnd);
1134             }
1135             return(0);
1136         case WM_KEYDOWN:
1137             switch(wParam) {
1138               case VK_HOME:
1139                       SendMessage(hwnd, WM_VSCROLL, SB_TOP, (LPARAM)0);
1140                       break;
1141               case VK_END:
1142                       SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, (LPARAM)0);
1143                       break;
1144               case VK_PRIOR:
1145                       SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, (LPARAM)0);
1146                       break;
1147               case VK_NEXT:
1148                       SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, (LPARAM)0);
1149                       break;
1150               case VK_UP:
1151                       SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, (LPARAM)0);
1152                       break;
1153               case VK_DOWN:
1154                       SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, (LPARAM)0);
1155                       break;
1156               case VK_LEFT:
1157                       SendMessage(hwnd, WM_HSCROLL, SB_LINEUP, (LPARAM)0);
1158                       break;
1159               case VK_RIGHT:
1160                       SendMessage(hwnd, WM_HSCROLL, SB_LINEDOWN, (LPARAM)0);
1161                       break;
1162             }
1163             break;
1164 #ifdef WM_UNICHAR
1165         case WM_UNICHAR: /* Belt and braces */
1166 #endif
1167         case WM_CHAR:
1168             { /* We get unicode in, but we put it into the buffer as if the
1169                * user was typing UTF8.
1170                */
1171                 long count = tw->KeyBufIn - tw->KeyBufOut;
1172                 if (count < 0) count += tw->KeyBufSize;
1173 #ifndef WINDOWS_NO_UNICODE
1174                 if (wParam >= 0x800) {
1175                     if (count >= tw->KeyBufSize-3)
1176                         return 0; /* Silently drop the chars! */
1177                     *tw->KeyBufIn++ = 0xE0 | (wParam>>12);
1178                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
1179                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
1180                     *tw->KeyBufIn++ = 0x80 | ((wParam>>6) & 0x3F);
1181                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
1182                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
1183                     *tw->KeyBufIn++ = 0x80 | (wParam & 0x3f);
1184                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
1185                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
1186                 } else if (wParam >= 0x80) {
1187                     if (count >= tw->KeyBufSize-2)
1188                         return 0; /* Silently drop the chars! */
1189                     *tw->KeyBufIn++ = 0xC0 | (wParam>>6);
1190                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
1191                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
1192                     *tw->KeyBufIn++ = 0x80 | (wParam & 0x3f);
1193                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
1194                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
1195                 } else
1196 #endif
1197                 {
1198                     if (count >= tw->KeyBufSize-1)
1199                         return 0; /* Silently drop the char! */
1200                     *tw->KeyBufIn++ = wParam;
1201                     if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
1202                         tw->KeyBufIn = tw->KeyBuf; /* wrap around */
1203                 }
1204             }
1205             return(0);
1206         case WM_PAINT:
1207             {
1208             POINT source, width, dest;
1209             hdc = BeginPaint(hwnd, &ps);
1210             SelectFont(hdc, tw->hfont);
1211             SetMapMode(hdc, MM_TEXT);
1212             SetBkMode(hdc,OPAQUE);
1213             GetClientRect(hwnd, &rect);
1214             source.x = (rect.left + tw->ScrollPos.x) / tw->CharSize.x; /* source */
1215             source.y = (rect.top + tw->ScrollPos.y) / tw->CharSize.y;
1216             dest.x = source.x * tw->CharSize.x - tw->ScrollPos.x; /* destination */
1217             dest.y = source.y * tw->CharSize.y - tw->ScrollPos.y;
1218             width.x = ((rect.right  + tw->ScrollPos.x + tw->CharSize.x - 1) / tw->CharSize.x) - source.x; /* width */
1219             width.y = ((rect.bottom + tw->ScrollPos.y + tw->CharSize.y - 1) / tw->CharSize.y) - source.y;
1220             if (source.x < 0)
1221                     source.x = 0;
1222             if (source.y < 0)
1223                     source.y = 0;
1224             if (source.x+width.x > tw->ScreenSize.x)
1225                     width.x = tw->ScreenSize.x - source.x;
1226             if (source.y+width.y > tw->ScreenSize.y)
1227                     width.y = tw->ScreenSize.y - source.y;
1228             /* for each line */
1229             while (width.y>0) {
1230 #ifdef WINDOWS_NO_UNICODE
1231                     TextOut(hdc,dest.x,dest.y,
1232                         (LPSTR)(tw->ScreenBuffer + source.y*tw->ScreenSize.x + source.x),
1233                         width.x);
1234 #else
1235                     TextOutW(hdc,dest.x,dest.y,
1236                         (LPCWSTR)(tw->ScreenBuffer + source.y*tw->ScreenSize.x + source.x),
1237                         width.x);
1238 #endif
1239                     dest.y += tw->CharSize.y;
1240                     source.y++;
1241                     width.y--;
1242             }
1243             EndPaint(hwnd, &ps);
1244             return 0;
1245             }
1246         case WM_DROPFILES:
1247             text_drag_drop(tw, (HDROP)wParam);
1248             break;
1249         case WM_CREATE:
1250             {
1251             RECT crect, wrect;
1252             int cx, cy;
1253 
1254             tw->hwnd = hwnd;
1255 
1256             /* make window no larger than screen buffer */
1257             GetWindowRect(hwnd, &wrect);
1258             GetClientRect(hwnd, &crect);
1259             cx = min(tw->CharSize.x*tw->ScreenSize.x, crect.right);
1260             cy = min(tw->CharSize.y*tw->ScreenSize.y, crect.bottom);
1261             MoveWindow(hwnd, wrect.left, wrect.top,
1262                  wrect.right-wrect.left + (cx - crect.right),
1263                  wrect.bottom-wrect.top + (cy - crect.bottom),
1264                      TRUE);
1265 
1266             if ( (tw->DragPre!=(LPSTR)NULL) && (tw->DragPost!=(LPSTR)NULL) )
1267                 DragAcceptFiles(hwnd, TRUE);
1268             }
1269             break;
1270         case WM_CLOSE:
1271             /* Tell user that we heard them */
1272             if (!tw->quitnow) {
1273                 TCHAR title[256];
1274                 int count = GetWindowText(hwnd, title,
1275                                           sizeof(title)/sizeof(TCHAR)-11);
1276 #ifdef WINDOWS_NO_UNICODE
1277                 lstrcpyA(title+count, " - closing");
1278 #else
1279                 lstrcpyW(title+count, L" - closing");
1280 #endif
1281                 SetWindowText(hwnd, title);
1282             }
1283             tw->quitnow = TRUE;
1284             /* wait until Ghostscript exits before destroying window */
1285             return 0;
1286         case WM_DESTROY:
1287             DragAcceptFiles(hwnd, FALSE);
1288             if (tw->hfont)
1289                 DeleteFont(tw->hfont);
1290             tw->hfont = (HFONT)0;
1291             tw->quitnow = TRUE;
1292             PostQuitMessage(0);
1293             break;
1294     }
1295     return DefWindowProc(hwnd, message, wParam, lParam);
1296 }
1297 
1298 HWND text_get_handle(TW *tw)
1299 {
1300     return tw->hwnd;
1301 }
1302 
1303 #ifdef NOTUSED
1304 /* test program */
1305 
1306 int PASCAL
1307 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
1308 {
1309         TW *tw;
1310 
1311         /* make a test window */
1312         tw = text_new();
1313 
1314         if (!hPrevInstance) {
1315             HICON hicon = LoadIcon(NULL, IDI_APPLICATION);
1316             text_register_class(tw, hicon);
1317         }
1318         text_font(tw, "Courier New", 10);
1319         text_size(tw, 80, 80);
1320         text_drag(tw, "(", ") run\r");
1321 
1322         /* show the text window */
1323         if (!text_create(tw, "Application Name", nCmdShow)) {
1324             /* do the real work here */
1325             /* TESTING */
1326             int ch;
1327             int len;
1328             char line[256];
1329             while ( (len = text_read_line(tw, line, 256-1)) != 0 ) {
1330                 text_write_buf(tw, line, len);
1331             }
1332 /*
1333             while ( text_gets(tw, line, 256-1) ) {
1334                 text_puts(tw, line);
1335             }
1336 */
1337 /*
1338             while ( (ch = text_getch(tw, )) != 4 )
1339                 text_putch(tw, ch);
1340 */
1341         }
1342         else {
1343 
1344         }
1345 
1346         /* clean up */
1347         text_destroy(tw);
1348 
1349         /* end program */
1350         return 0;
1351 }
1352 #endif
1353