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