dwtext.c 26 KB

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