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 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 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 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 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 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 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 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 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 */ 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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