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