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