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