Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1283 lines
30 KiB

  1. //
  2. // TXTED.CPP
  3. // Editor for Text Objects
  4. //
  5. // Copyright Microsoft 1998-
  6. //
  7. // PRECOMP
  8. #include "precomp.h"
  9. /////////////////////////////////////////////////////////////////////////////
  10. // WbTextBox
  11. //
  12. // This is a subclassed edit field
  13. //
  14. WbTextBox::WbTextBox(WbTextEditor * pEditor)
  15. {
  16. OSVERSIONINFO OsData;
  17. m_hwnd = NULL;
  18. m_pfnEditPrev = NULL;
  19. m_MaxRect.left = 0;
  20. m_MaxRect.top = 0;
  21. m_MaxRect.right = INT_MAX;
  22. m_MaxRect.bottom = INT_MAX;
  23. ::SetRectEmpty(&m_rectErase);
  24. m_bInIME = FALSE;
  25. m_bDontEscapeThisTime = FALSE;
  26. // see if we need to make adjustments for NT.
  27. m_ptNTBooger.x = 0;
  28. m_ptNTBooger.y = 0;
  29. OsData.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  30. if( GetVersionEx( &OsData ) )
  31. {
  32. if( OsData.dwPlatformId == VER_PLATFORM_WIN32_NT )
  33. {
  34. // NT editboxes are offset from Win95 editboxes. We
  35. // have to de-booger them
  36. m_ptNTBooger.x = 3;
  37. }
  38. }
  39. m_pEditor = pEditor;
  40. }
  41. //
  42. // ~WbTextBox()
  43. //
  44. WbTextBox::~WbTextBox()
  45. {
  46. if (m_hwnd != NULL)
  47. {
  48. ::DestroyWindow(m_hwnd);
  49. m_hwnd = NULL;
  50. }
  51. }
  52. //
  53. // Create()
  54. // Creates an edit field then subclasses it with our window procedure
  55. //
  56. BOOL WbTextBox::Create(HWND hwndParent)
  57. {
  58. ASSERT(!m_hwnd);
  59. m_hwnd = ::CreateWindowEx(0, _T("EDIT"), NULL,
  60. WS_CHILD | WS_BORDER | ES_MULTILINE | ES_WANTRETURN |
  61. ES_AUTOHSCROLL | ES_AUTOVSCROLL,
  62. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
  63. hwndParent, NULL, g_hInstance, NULL);
  64. if (!m_hwnd)
  65. {
  66. ERROR_OUT(("WbTextBox::Create failed to create edit window"));
  67. return(FALSE);
  68. }
  69. // Init the data
  70. ::SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
  71. // Subclass the window
  72. m_pfnEditPrev = (WNDPROC)::SetWindowLongPtr(m_hwnd, GWLP_WNDPROC,
  73. (LONG_PTR)TextWndProc);
  74. return(TRUE);
  75. }
  76. //
  77. // TextWndProc()
  78. // Message subclass handler for edit field
  79. //
  80. LRESULT CALLBACK TextWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  81. {
  82. LRESULT lResult;
  83. WbTextBox * ptb;
  84. ptb = (WbTextBox *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
  85. ASSERT(ptb != NULL);
  86. ASSERT(ptb->m_pfnEditPrev != NULL);
  87. lResult = 0;
  88. switch( message )
  89. {
  90. case WM_CLEAR:
  91. case WM_CUT:
  92. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  93. ptb->OnClearCut();
  94. break;
  95. case WM_UNDO:
  96. case WM_PASTE:
  97. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  98. ptb->OnUndoPaste();
  99. break;
  100. case WM_IME_STARTCOMPOSITION:
  101. {
  102. ptb->m_bInIME = TRUE;
  103. ptb->m_bDontEscapeThisTime = TRUE;
  104. // let editbox take it from here
  105. goto DefEditProc;
  106. break;
  107. }
  108. case WM_IME_CHAR:
  109. {
  110. ptb->m_bDontEscapeThisTime = FALSE;
  111. goto DefEditProc;
  112. break;
  113. }
  114. case WM_IME_ENDCOMPOSITION:
  115. {
  116. ptb->m_bInIME = FALSE;
  117. goto DefEditProc;
  118. break;
  119. }
  120. case WM_KILLFOCUS:
  121. {
  122. if (ptb->m_bInIME && g_fnImmGetContext)
  123. {
  124. HIMC hImc = g_fnImmGetContext(hwnd);
  125. if ((hImc != NULL) && g_fnImmNotifyIME)
  126. {
  127. // we're loosing control, tell IME to wrap it up (bug 130)
  128. g_fnImmNotifyIME( hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0 );
  129. }
  130. }
  131. // goto DefEditProc;
  132. break;
  133. }
  134. case WM_CHAR:
  135. ptb->OnChar((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
  136. break;
  137. case WM_KEYUP:
  138. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  139. ptb->OnKeyUp((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
  140. break;
  141. case WM_SYSKEYDOWN:
  142. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  143. ptb->OnSysKeyDown((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
  144. break;
  145. case WM_TIMER:
  146. ptb->OnTimer((UINT)wParam);
  147. break;
  148. case WM_MOUSEMOVE:
  149. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  150. ptb->OnMouseMove((UINT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
  151. break;
  152. case WM_LBUTTONUP:
  153. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  154. ptb->OnLButtonUp((UINT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
  155. break;
  156. case WM_MOVE:
  157. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  158. ptb->OnMove((short)LOWORD(lParam), (short)HIWORD(lParam));
  159. break;
  160. default:
  161. DefEditProc:
  162. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  163. break;
  164. }
  165. return(lResult);
  166. }
  167. //
  168. // OnClearCut()
  169. //
  170. void WbTextBox::OnClearCut()
  171. {
  172. POINT ptCaret;
  173. POINT ptPos;
  174. ::GetCaretPos(&ptCaret);
  175. m_pEditor->GetAnchorPoint(&ptPos);
  176. m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x;
  177. m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y;
  178. OnUndoPaste();
  179. }
  180. //
  181. // OnUndoPaste()
  182. //
  183. void WbTextBox::OnUndoPaste(void)
  184. {
  185. FitBox();
  186. AutoCaretScroll();
  187. OnTimer(0);
  188. }
  189. //
  190. // OnChar()
  191. //
  192. void WbTextBox::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
  193. {
  194. int nFirst;
  195. int nLast;
  196. int nPrevNumLines;
  197. int nPrevNumChars;
  198. LRESULT dwPosChar;
  199. POINT ptTop;
  200. // clear ignore next escape (NM4db:456)
  201. m_bDontEscapeThisTime = FALSE;
  202. ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&nFirst, (LPARAM)&nLast);
  203. dwPosChar = ::SendMessage(m_hwnd, EM_POSFROMCHAR, nFirst, 0);
  204. ptTop.x = (short)LOWORD(dwPosChar);
  205. ptTop.y = (short)HIWORD(dwPosChar);
  206. nPrevNumLines = (int)::SendMessage(m_hwnd, EM_GETLINECOUNT, 0, 0);
  207. nPrevNumChars = (int)::SendMessage(m_hwnd, EM_LINELENGTH, (WPARAM)-1, 0);
  208. ::CallWindowProc(m_pfnEditPrev, m_hwnd, WM_CHAR, nChar, MAKELONG(nRepCnt, nFlags));
  209. SetupBackgroundRepaint( ptTop,
  210. (nPrevNumLines != ::SendMessage(m_hwnd, EM_GETLINECOUNT, 0, 0))||
  211. (nPrevNumChars > ::SendMessage(m_hwnd, EM_LINELENGTH, (WPARAM)-1, 0)));
  212. FitBox();
  213. m_pEditor->ChangedText();
  214. }
  215. void WbTextBox::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
  216. {
  217. POINT ptCaret;
  218. POINT ptPos;
  219. AutoCaretScroll();
  220. switch( nChar )
  221. {
  222. case VK_MENU:
  223. case VK_SHIFT:
  224. case VK_CONTROL:
  225. break;
  226. case VK_DELETE:
  227. SelectAtLeastOne();
  228. ::SendMessage(m_hwnd, WM_CLEAR, 0, 0);
  229. break;
  230. case VK_ESCAPE:
  231. if( !m_bInIME )
  232. {
  233. if( m_bDontEscapeThisTime )
  234. m_bDontEscapeThisTime = FALSE;
  235. else
  236. {
  237. // End the text entry abandoning the changes
  238. g_pDraw->EndTextEntry(FALSE);
  239. return; // we don't exist anymore, bail out
  240. }
  241. }
  242. break;
  243. default:
  244. break;
  245. }
  246. ::GetCaretPos(&ptCaret);
  247. m_pEditor->GetAnchorPoint(&ptPos);
  248. m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x;
  249. m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y;
  250. }
  251. void WbTextBox::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  252. {
  253. switch( nChar )
  254. {
  255. case VK_MENU:
  256. // ignore next escape (NM4db:456)
  257. m_bDontEscapeThisTime = TRUE;
  258. break;
  259. default:
  260. break;
  261. }
  262. }
  263. BOOL WbTextBox::FitBox( void )
  264. {
  265. RECT rectErase;
  266. RECT crEditRect;
  267. RECT crEditBox;
  268. RECT crEditWnd;
  269. RECT crDrawWnd;
  270. int nDeltaWidth, nDeltaHeight;
  271. HDC hDC = NULL;
  272. HDC hDrawDC = NULL;
  273. TCHAR *szBoxText = NULL;
  274. BOOL bNoChanges;
  275. POINT ptDrawLoc;
  276. HFONT hSaveFont = NULL;
  277. POINT ptBitmapTopLeft;
  278. LPTSTR pszText;
  279. int nTextLen;
  280. int textSize;
  281. bNoChanges = TRUE;
  282. hDC = ::GetDC(m_hwnd);
  283. if (hDC == NULL)
  284. {
  285. bNoChanges = TRUE;
  286. goto bail_out;
  287. }
  288. hDrawDC = g_pDraw->GetCachedDC();
  289. if (hDrawDC == NULL)
  290. {
  291. bNoChanges = TRUE;
  292. goto bail_out;
  293. }
  294. hSaveFont = SelectFont(hDC, m_pEditor->GetFont());
  295. textSize = ::GetWindowTextLength(m_hwnd);
  296. if (!textSize)
  297. {
  298. // Use a " " for a string, which is two characters.
  299. DBG_SAVE_FILE_LINE
  300. szBoxText = new TCHAR[2];
  301. if (!szBoxText)
  302. {
  303. ERROR_OUT(("Failed to allocate TCHAR array for empty text"));
  304. goto bail_out;
  305. }
  306. else
  307. {
  308. szBoxText[0] = 0;
  309. }
  310. }
  311. else
  312. {
  313. DBG_SAVE_FILE_LINE
  314. szBoxText = new TCHAR[textSize+1];
  315. if (!szBoxText)
  316. {
  317. ERROR_OUT(("Failed to allocate TCHAR array for object text"));
  318. goto bail_out;
  319. }
  320. else
  321. {
  322. ::GetWindowText(m_hwnd, (LPTSTR)szBoxText, textSize+1);
  323. }
  324. }
  325. // Have to check string length for NT. crEditRect comes back from
  326. // DrawText giant-positive (i.e., still equal to m_MaxRect) if szBoxText
  327. // is empty. This causes crEditRect to overflow to giant negative later
  328. // resulting in very bizare painting problems. Win95 doesn't care because
  329. // it returns 16bit results that look like -1 instead of giant positive.
  330. ::GetClientRect(g_pDraw->m_hwnd, &crDrawWnd );
  331. // BUG 464 -DrawText doesn't calc widths involving tabs the same way a
  332. // standard edit control does so replace it with DrawTextEx
  333. // using the DT_EDITCONTROL format
  334. crEditRect = m_MaxRect;
  335. pszText = szBoxText;
  336. nTextLen = lstrlen(szBoxText);
  337. ::DrawTextEx( hDC,
  338. ((nTextLen == 0) ? " " : pszText),
  339. ((nTextLen == 0) ? 1 : nTextLen),
  340. &crEditRect,
  341. DT_CALCRECT | DT_EXPANDTABS | DT_NOPREFIX | DT_EDITCONTROL,
  342. NULL );
  343. m_pEditor->SetText(szBoxText);
  344. // BUG 464 (continued) -DrawTextEx doesn't include blank-last-lines in its height
  345. // calc like DrawText does so have to add an extra line
  346. // height for blank lines to have same behavior as DrawText
  347. ASSERT(m_pEditor->m_textMetrics.tmHeight != DBG_UNINIT);
  348. if ((nTextLen >= 2) && !IsDBCSLeadByte(pszText[nTextLen-2]) && (pszText[nTextLen-1] == _T('\n')) )
  349. crEditRect.bottom += m_pEditor->m_textMetrics.tmHeight;
  350. // NT sanity check if this still fails
  351. if ( ((crEditRect.right - crEditRect.left) == (m_MaxRect.right - m_MaxRect.left)) ||
  352. ((crEditRect.right - crEditRect.left) <= 0) )
  353. {
  354. crEditRect.right = crEditRect.left + crDrawWnd.right - crDrawWnd.left;
  355. }
  356. if ( ((crEditRect.bottom - crEditRect.top) == (m_MaxRect.bottom - m_MaxRect.top)) ||
  357. ((crEditRect.bottom - crEditRect.top) <= 0) )
  358. {
  359. crEditRect.bottom = crEditRect.top + crDrawWnd.bottom - crDrawWnd.top;
  360. }
  361. ::GetClientRect(m_hwnd, &crEditBox);
  362. ::GetWindowRect(m_hwnd, &crEditWnd);
  363. ::MapWindowPoints(NULL, g_pDraw->m_hwnd, (LPPOINT)&crEditWnd, 2);
  364. // do min size check for IME's.
  365. int nMinWidth;
  366. ASSERT(m_pEditor->m_textMetrics.tmMaxCharWidth != DBG_UNINIT);
  367. nMinWidth = MIN_FITBOX_CHARS*m_pEditor->m_textMetrics.tmMaxCharWidth;
  368. if ((crEditRect.right - crEditRect.left) < nMinWidth )
  369. crEditRect.right = crEditRect.left + nMinWidth;
  370. // must add some slop to prevent autoscroll from kicking in
  371. crEditRect.right += 2*m_pEditor->m_textMetrics.tmMaxCharWidth;
  372. ASSERT(m_pEditor->m_textMetrics.tmHeight != DBG_UNINIT);
  373. crEditRect.bottom += m_pEditor->m_textMetrics.tmHeight;
  374. //pretend we had a right or bottom scroll
  375. ::OffsetRect(&crEditRect, -1, -1);
  376. nDeltaWidth = (crEditRect.right - crEditRect.left) - (crEditBox.right - crEditBox.left);
  377. if (nDeltaWidth > 0)
  378. {
  379. bNoChanges = FALSE;
  380. if ( crEditRect.left < 0 )
  381. {
  382. // right scroll, adjust right edge
  383. crEditWnd.right += nDeltaWidth;
  384. }
  385. else
  386. {
  387. // left scroll, adjust left edge
  388. crEditWnd.left -= nDeltaWidth;
  389. }
  390. }
  391. nDeltaHeight = (crEditRect.bottom - crEditRect.top) - (crEditBox.bottom - crEditBox.top);
  392. if (nDeltaHeight > 0)
  393. {
  394. bNoChanges = FALSE;
  395. if( crEditRect.left < 0 )
  396. {
  397. // bottom scroll, adjust bottom edge
  398. crEditWnd.bottom += nDeltaHeight;
  399. }
  400. else
  401. {
  402. // top scroll, adjust top edge
  403. crEditWnd.top -= nDeltaHeight;
  404. }
  405. }
  406. if( bNoChanges )
  407. goto bail_out;
  408. // resize
  409. ::MoveWindow(m_hwnd, crEditWnd.left, crEditWnd.top,
  410. crEditWnd.right - crEditWnd.left, crEditWnd.bottom - crEditWnd.top, TRUE );
  411. // update bounding box
  412. ::GetClientRect(m_hwnd, &crEditBox);
  413. ::MapWindowPoints(m_hwnd, g_pDraw->m_hwnd, (LPPOINT)&crEditBox, 2);
  414. ::OffsetRect(&crEditBox, g_pDraw->m_originOffset.cx+1 + m_ptNTBooger.x,
  415. g_pDraw->m_originOffset.cy + m_ptNTBooger.y );//+1 );
  416. m_pEditor->m_rect = crEditBox;
  417. m_pEditor->SetBoundsRect(&crEditBox);
  418. bail_out:
  419. if (hDC != NULL )
  420. {
  421. if(hSaveFont)
  422. {
  423. SelectFont(hDC, hSaveFont);
  424. }
  425. ::ReleaseDC(m_hwnd, hDC );
  426. }
  427. if(szBoxText)
  428. {
  429. delete [] szBoxText;
  430. }
  431. return( !bNoChanges );
  432. }
  433. void WbTextBox::OnTimer(UINT nIDEvent)
  434. {
  435. TRACE_TIMER(("WbTextBox::OnTimer"));
  436. //
  437. // If something changed: Text,Font, anchor point etc...
  438. //
  439. if(m_pEditor->HasTextChanged())
  440. {
  441. //
  442. // If we are not added to the workspace
  443. //
  444. if(!m_pEditor->GetMyWorkspace())
  445. {
  446. m_pEditor->SetAllAttribs();
  447. m_pEditor->AddToWorkspace();
  448. }
  449. else
  450. {
  451. m_pEditor->OnObjectEdit();
  452. }
  453. }
  454. }
  455. //
  456. // WbTextBox::OnLButtonUp()
  457. //
  458. void WbTextBox::OnLButtonUp(UINT nFlags, int x, int y)
  459. {
  460. POINT ptCaret;
  461. POINT ptPos;
  462. ::GetCaretPos(&ptCaret);
  463. m_pEditor->GetAnchorPoint(&ptPos);
  464. m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x;
  465. m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y;
  466. }
  467. //
  468. // WbTextBox::OnMouseMove()
  469. //
  470. void WbTextBox::OnMouseMove(UINT nFlags, int x, int y)
  471. {
  472. if (nFlags & MK_LBUTTON )
  473. {
  474. // we're dragging
  475. ::HideCaret(m_hwnd);
  476. AutoCaretScroll();
  477. ::ShowCaret(m_hwnd);
  478. }
  479. }
  480. void WbTextBox::AutoCaretScroll( void )
  481. {
  482. POINT ptCaret;
  483. POINT ptPos;
  484. ::GetCaretPos(&ptCaret);
  485. m_pEditor->GetAnchorPoint(&ptPos);
  486. ptCaret.x += ptPos.x;
  487. ptCaret.y += ptPos.y;
  488. g_pDraw->AutoScroll(ptCaret.x, ptCaret.y, FALSE, m_pEditor->m_cursorXYPos.x,
  489. m_pEditor->m_cursorXYPos.y);
  490. m_pEditor->m_cursorXYPos = ptCaret;
  491. }
  492. void WbTextBox::SetupBackgroundRepaint
  493. (
  494. POINT ptTopPaint,
  495. BOOL bNumLinesChanged
  496. )
  497. {
  498. RECT rectBox;
  499. ::GetClientRect(m_hwnd, &rectBox);
  500. if (ptTopPaint.y == -1)
  501. {
  502. ::GetCaretPos(&ptTopPaint);
  503. }
  504. ASSERT(m_pEditor->m_textMetrics.tmHeight != DBG_UNINIT);
  505. rectBox.top = ptTopPaint.y;
  506. if( !bNumLinesChanged )
  507. rectBox.bottom = rectBox.top + m_pEditor->m_textMetrics.tmHeight;
  508. ::InvalidateRect(m_hwnd, &rectBox, TRUE );
  509. }
  510. //
  511. // Selects at least one (DBCS) char if there is not a selection already.
  512. //
  513. void WbTextBox::SelectAtLeastOne( void )
  514. {
  515. int nFirst, nLast;
  516. TCHAR * szBoxText;
  517. ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&nFirst, (LPARAM)&nLast);
  518. if( nFirst == nLast )
  519. {
  520. int textSize = ::GetWindowTextLength(m_hwnd);
  521. DBG_SAVE_FILE_LINE
  522. szBoxText = new TCHAR[textSize + 1];
  523. if (!szBoxText)
  524. {
  525. ERROR_OUT(("Failed to allocate TCHAR array for object text"));
  526. }
  527. else
  528. {
  529. ::GetWindowText( m_hwnd, szBoxText, textSize+1);
  530. if (nFirst < textSize)
  531. {
  532. nLast++;
  533. if( IsDBCSLeadByte( (BYTE) szBoxText[ nFirst ] )||
  534. (szBoxText[ nFirst ] == _T('\r'))
  535. )
  536. nLast++;
  537. ::SendMessage(m_hwnd, EM_SETSEL, nFirst, nLast);
  538. }
  539. delete [] szBoxText;
  540. }
  541. }
  542. }
  543. void WbTextBox::OnMove(int x, int y)
  544. {
  545. if (m_pEditor->m_nLastShow == SW_SHOW)
  546. {
  547. FitBox();
  548. ::ShowCaret(m_hwnd);
  549. }
  550. }
  551. int WbTextBox::GetMaxCharHeight( void )
  552. {
  553. ASSERT(m_pEditor->m_textMetrics.tmHeight != DBG_UNINIT);
  554. return( m_pEditor->m_textMetrics.tmHeight );
  555. }
  556. int WbTextBox::GetMaxCharWidth( void )
  557. {
  558. ASSERT(m_pEditor->m_textMetrics.tmMaxCharWidth != DBG_UNINIT);
  559. return( m_pEditor->m_textMetrics.tmMaxCharWidth );
  560. }
  561. //
  562. // Aborts and shuts down text editor without hitting the cores. Call this
  563. // to shutdown editing during a lock condition (that we don't own)
  564. //
  565. void WbTextBox::AbortEditGently( void )
  566. {
  567. RECT boundsRect;
  568. // shut down rejecting all edits
  569. g_pDraw->EndTextEntry(FALSE);
  570. // Delete the text object
  571. if (g_pDraw->m_pTextEditor != NULL)
  572. {
  573. m_pEditor->GetBoundsRect(&boundsRect);
  574. delete g_pDraw->m_pTextEditor; // zap in memory original
  575. g_pDraw->m_pTextEditor = NULL; // text object, loose any current edits
  576. m_pEditor = NULL;
  577. }
  578. else
  579. {
  580. SetRectEmpty(&boundsRect);
  581. }
  582. // Redraw any altered parts of the screen
  583. g_pDraw->InvalidateSurfaceRect(&boundsRect, TRUE);
  584. }
  585. //
  586. //
  587. // Function: WbTextEditor
  588. //
  589. // Purpose: Constructor
  590. //
  591. //
  592. WbTextEditor::WbTextEditor(void)
  593. {
  594. // Initialize the cursor position
  595. m_cursorCharPos.x = 0;
  596. m_cursorCharPos.y = 0;
  597. // set parent for editbox
  598. m_pEditBox = NULL;
  599. m_cursorXYPos.x = 0;
  600. m_cursorXYPos.y = 0;
  601. m_bFirstSetFontCall = TRUE;
  602. m_nLastShow = -1;
  603. }
  604. WbTextEditor::~WbTextEditor(void)
  605. {
  606. if (m_pEditBox != NULL)
  607. {
  608. delete m_pEditBox;
  609. m_pEditBox = NULL;
  610. }
  611. }
  612. //
  613. //
  614. // Function: SetCursorPosFromPoint
  615. //
  616. // Purpose: Return the character position most closely matching a
  617. // given co-ordinate position in the text object.
  618. //
  619. //
  620. void WbTextEditor::SetCursorPosFromPoint(POINT pointXY)
  621. {
  622. int nCharPos;
  623. if (::PtInRect(&m_rect, pointXY))
  624. {
  625. // make point relative to editbox
  626. pointXY.x -= g_pDraw->m_originOffset.cx;
  627. pointXY.y -= g_pDraw->m_originOffset.cy;
  628. ::MapWindowPoints(g_pDraw->m_hwnd, m_pEditBox->m_hwnd,
  629. &pointXY, 1);
  630. ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONDOWN, 0,
  631. MAKELONG( pointXY.x, pointXY.y ) );
  632. ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONUP, 0,
  633. MAKELONG( pointXY.x, pointXY.y ) );
  634. // get char index in editbox
  635. nCharPos = (int)::SendMessage(m_pEditBox->m_hwnd, EM_CHARFROMPOS, 0,
  636. MAKELPARAM(pointXY.x, pointXY.y));
  637. if( nCharPos < 0 )
  638. return;
  639. // Set the new cursor char co-ordinates
  640. m_cursorCharPos.x = (short)LOWORD(nCharPos);
  641. m_cursorCharPos.y = (short)HIWORD(nCharPos);
  642. // Move the cursor to the new position
  643. GetXYPosition(m_cursorCharPos, &m_cursorXYPos);
  644. }
  645. }
  646. //
  647. //
  648. // Function: GetCursorSize
  649. //
  650. // Purpose: Return the cursor size for the current font
  651. //
  652. //
  653. void WbTextEditor::GetCursorSize(LPSIZE lpsize)
  654. {
  655. ASSERT(m_textMetrics.tmHeight != DBG_UNINIT);
  656. lpsize->cx = ::GetSystemMetrics(SM_CXBORDER);
  657. lpsize->cy = m_textMetrics.tmHeight;
  658. }
  659. //
  660. //
  661. // Function: XYPosition
  662. //
  663. // Purpose: Calculate the X,Y co-ordinates of a character position
  664. //
  665. //
  666. void WbTextEditor::GetXYPosition(POINT pointChar, LPPOINT lpptXY)
  667. {
  668. int nCharIndex;
  669. LRESULT dwCharPos;
  670. nCharIndex = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, pointChar.y, 0)
  671. + pointChar.x;
  672. GetAnchorPoint(lpptXY);
  673. dwCharPos = ::SendMessage(m_pEditBox->m_hwnd, EM_POSFROMCHAR, nCharIndex, 0);
  674. lpptXY->x += (short)LOWORD(dwCharPos);
  675. lpptXY->y += (short)HIWORD(dwCharPos);
  676. }
  677. //
  678. //
  679. // Function: Clear
  680. //
  681. // Purpose: Clear the text editor
  682. //
  683. //
  684. void WbTextEditor::Clear(void)
  685. {
  686. RECT cEWndRect;
  687. // Remove all the current stored text
  688. strTextArray.RemoveAll();
  689. // Reset the cursor position
  690. m_cursorCharPos.x = 0;
  691. m_cursorCharPos.y = 0;
  692. // clear editbox
  693. ::SetWindowText(m_pEditBox->m_hwnd, _TEXT(""));
  694. // init editbox size
  695. m_rect.right = m_rect.left + 2*m_pEditBox->GetMaxCharWidth();
  696. m_rect.bottom = m_rect.top + 2*m_pEditBox->GetMaxCharHeight();
  697. SetBoundsRect(&m_rect);
  698. cEWndRect = m_rect;
  699. ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x),
  700. -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) );
  701. ::BringWindowToTop(m_pEditBox->m_hwnd);
  702. ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top,
  703. cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top,
  704. TRUE);
  705. }
  706. //
  707. //
  708. // Function: New
  709. //
  710. // Purpose: Clear the text editor and reset the graphic handle
  711. //
  712. //
  713. BOOL WbTextEditor::New(void)
  714. {
  715. // create editbox
  716. if (!Create())
  717. {
  718. ERROR_OUT(("Error creating drawing area window"));
  719. return(FALSE);
  720. }
  721. // Clear the object
  722. Clear();
  723. return(TRUE);
  724. }
  725. //
  726. //
  727. // Function: SetTextObject
  728. //
  729. // Purpose: Attach a text object to the editor
  730. //
  731. //
  732. BOOL WbTextEditor::SetTextObject(TextObj* ptext)
  733. {
  734. // create editbox
  735. if (!Create())
  736. {
  737. ERROR_OUT(("Error creating drawing area window"));
  738. return(FALSE);
  739. }
  740. // setup font
  741. SetFont( ptext->m_hFont );
  742. POINT pPoint;
  743. ptext->GetAnchorPoint(&pPoint);
  744. MoveTo(pPoint.x, pPoint.y);
  745. // copy the text to the editbox
  746. GetText();
  747. return(TRUE);
  748. }
  749. //
  750. //
  751. // Function: IsEmpty
  752. //
  753. // Purpose: Return TRUE if there is no text in the object
  754. //
  755. //
  756. BOOL WbTextEditor::IsEmpty(void)
  757. {
  758. return(::GetWindowTextLength(m_pEditBox->m_hwnd) <= 0 );
  759. }
  760. void WbTextEditor::PutText(void)
  761. {
  762. int nNumLines;
  763. int i;
  764. int nMaxLineLen, nLineLen;
  765. int nLine;
  766. TCHAR *cbuf;
  767. WbTextEditor *pThis;
  768. pThis = (WbTextEditor *)this; // overide const declaration
  769. nNumLines = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINECOUNT, 0, 0);
  770. // figure out buf size
  771. nMaxLineLen = 0;
  772. for (i = 0; i < nNumLines; i++)
  773. {
  774. nLine = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, i, 0);
  775. nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINELENGTH, nLine, 0);
  776. if (nMaxLineLen < nLineLen)
  777. nMaxLineLen = nLineLen;
  778. }
  779. // make buf
  780. nMaxLineLen += sizeof(WORD);
  781. DBG_SAVE_FILE_LINE
  782. cbuf = new TCHAR[nMaxLineLen];
  783. if( cbuf == NULL )
  784. {
  785. ERROR_OUT(("PutText failing; couldn't allocate TCHAR array"));
  786. return;
  787. }
  788. //
  789. // copy editbox innards to textbox string
  790. // Again, we know in advance how many lines; use SetSize/SetAt()
  791. //
  792. strTextArray.RemoveAll();
  793. strTextArray.SetSize(nNumLines);
  794. for(i = 0; i < nNumLines; i++)
  795. {
  796. *(LPWORD)cbuf = (WORD)nMaxLineLen;
  797. nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINE, i, (LPARAM)cbuf);
  798. cbuf[nLineLen] = _T('\0');
  799. strTextArray.SetAt(i, cbuf );
  800. }
  801. // clean up
  802. delete [] cbuf;
  803. }
  804. void WbTextEditor::GetText(void)
  805. {
  806. int nNumLines;
  807. int textSize = 0;
  808. int i;
  809. TCHAR * pText = NULL;
  810. TCHAR * pStartText;
  811. nNumLines = strTextArray.GetSize();
  812. //
  813. // Calculate the buffer size we need
  814. //
  815. for (i = 0; i < nNumLines; i++ )
  816. {
  817. textSize += lstrlen(strTextArray[i]);
  818. if ((i + 1) < nNumLines)
  819. textSize += lstrlen(_T("\r\n"));
  820. }
  821. //
  822. // Get the lines, with \r\n separating them
  823. //
  824. DBG_SAVE_FILE_LINE
  825. pText = new TCHAR[textSize + 1];
  826. if (!pText)
  827. {
  828. ERROR_OUT(("GetText failing; couldn't allocate TCHAR array"));
  829. }
  830. else
  831. {
  832. // Null this out in casae textSize is 0.
  833. pStartText = pText;
  834. pStartText[0] = 0;
  835. for (i = 0; i < nNumLines; i++)
  836. {
  837. lstrcpy(pStartText, strTextArray[i]);
  838. pStartText += lstrlen(strTextArray[i]);
  839. if ((i + 1) < nNumLines)
  840. {
  841. lstrcpy(pStartText, _T("\r\n"));
  842. pStartText += lstrlen(_T("\r\n"));
  843. }
  844. }
  845. ::SetWindowText(m_pEditBox->m_hwnd, pText);
  846. delete [] pText;
  847. }
  848. }
  849. void WbTextEditor::CalculateBoundsRect( void )
  850. {
  851. RECT cEWndRect;
  852. ASSERT(m_pEditBox);
  853. ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect);
  854. ::MapWindowPoints(m_pEditBox->m_hwnd, g_pDraw->m_hwnd,
  855. (LPPOINT)&cEWndRect, 2);
  856. m_rect = cEWndRect;
  857. ::OffsetRect(&m_rect, g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x,
  858. g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y);//+1 );
  859. SetBoundsRect(&m_rect);
  860. ::BringWindowToTop(m_pEditBox->m_hwnd);
  861. }
  862. // set editbox visibility
  863. void WbTextEditor::ShowBox( int nShow )
  864. {
  865. if (m_nLastShow == nShow)
  866. {
  867. if( nShow == SW_SHOW)
  868. ::SetFocus(m_pEditBox->m_hwnd);
  869. return;
  870. }
  871. m_nLastShow = nShow;
  872. if (nShow == SW_SHOW)
  873. {
  874. // show it
  875. if (m_pEditBox != NULL)
  876. {
  877. ::ShowWindow(m_pEditBox->m_hwnd, SW_SHOW);
  878. ::BringWindowToTop(m_pEditBox->m_hwnd);
  879. ::SetFocus(m_pEditBox->m_hwnd);
  880. ::HideCaret(m_pEditBox->m_hwnd);
  881. m_pEditBox->FitBox();
  882. ::ShowCaret(m_pEditBox->m_hwnd);
  883. }
  884. }
  885. else
  886. {
  887. if (m_pEditBox != NULL)
  888. {
  889. ::ShowWindow(m_pEditBox->m_hwnd, SW_HIDE);
  890. m_bFirstSetFontCall = TRUE;
  891. KillTimer();
  892. delete m_pEditBox;
  893. m_pEditBox = NULL;
  894. }
  895. }
  896. }
  897. //
  898. // Create()
  899. //
  900. // Creates the edit box window
  901. //
  902. BOOL WbTextEditor::Create( void )
  903. {
  904. if (m_pEditBox == NULL)
  905. {
  906. DBG_SAVE_FILE_LINE
  907. m_pEditBox = new WbTextBox(this);
  908. if (m_pEditBox == NULL)
  909. {
  910. ERROR_OUT(("Couldn't create edit text box"));
  911. return(FALSE);
  912. }
  913. if (!m_pEditBox->Create(g_pDraw->m_hwnd))
  914. {
  915. ERROR_OUT(("WbTextEditor::Create failed; can't create edit field"));
  916. return(FALSE);
  917. }
  918. }
  919. return(TRUE);
  920. }
  921. void WbTextEditor::MoveBy(int cx, int cy)
  922. {
  923. RECT cEWndRect;
  924. ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect);
  925. if ((m_rect.right - m_rect.left) < (cEWndRect.right - cEWndRect.left))
  926. m_rect.right = m_rect.left + (cEWndRect.right - cEWndRect.left);
  927. if ((m_rect.bottom - m_rect.top) < (cEWndRect.bottom - cEWndRect.top))
  928. m_rect.bottom = m_rect.top + (cEWndRect.bottom - cEWndRect.top);
  929. cEWndRect = m_rect;
  930. ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x),
  931. -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) );//+1) );
  932. ::BringWindowToTop(m_pEditBox->m_hwnd);
  933. ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top,
  934. cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top,
  935. TRUE);
  936. }
  937. void WbTextEditor::SetFont( LOGFONT *pLogFont, BOOL bDummy )
  938. {
  939. // Ignore bDummy. Had to add that so polymorph will work correctly
  940. // for DCWbGraphicText::SetFont()
  941. // Have to delay recalc of bounding rect because editbox will have a
  942. // bogus (bad) font until SetFont is called. Can't call SetFont
  943. // before this because new font hasn't been made yet.
  944. TextObj::SetFont( pLogFont, FALSE );
  945. if(m_pEditBox)
  946. {
  947. ::SendMessage(m_pEditBox->m_hwnd, WM_SETFONT, (WPARAM)m_hFont, TRUE);
  948. // now do bounding rect
  949. CalculateBoundsRect();
  950. if( m_bFirstSetFontCall )
  951. m_bFirstSetFontCall = FALSE;
  952. else
  953. {
  954. ::HideCaret(m_pEditBox->m_hwnd);
  955. m_pEditBox->FitBox();
  956. m_pEditBox->AutoCaretScroll();
  957. ::ShowCaret(m_pEditBox->m_hwnd);
  958. }
  959. }
  960. }
  961. void WbTextEditor::SetTimer( UINT nElapse )
  962. {
  963. ::SetTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE, nElapse, NULL);
  964. }
  965. void WbTextEditor::KillTimer( void )
  966. {
  967. if(m_pEditBox)
  968. {
  969. ::KillTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE);
  970. }
  971. }
  972. // Resets editbox painting for a resized drawing window
  973. void WbTextEditor::ParentResize( void )
  974. {
  975. ::HideCaret(m_pEditBox->m_hwnd);
  976. m_pEditBox->FitBox();
  977. ::ShowCaret(m_pEditBox->m_hwnd);
  978. }
  979. void WbTextEditor::RedrawEditbox(void)
  980. {
  981. ::InvalidateRect(m_pEditBox->m_hwnd, NULL, TRUE);
  982. m_pEditBox->FitBox();
  983. }
  984.