Source code of Windows XP (NT5)
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.

1275 lines
28 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;
  274. BOOL bNoChanges;
  275. POINT ptDrawLoc;
  276. HFONT hSaveFont;
  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. SelectFont(hDC, hSaveFont);
  422. ::ReleaseDC(m_hwnd, hDC );
  423. }
  424. delete szBoxText;
  425. return( !bNoChanges );
  426. }
  427. void WbTextBox::OnTimer(UINT nIDEvent)
  428. {
  429. TRACE_TIMER(("WbTextBox::OnTimer"));
  430. //
  431. // If something changed: Text,Font, anchor point etc...
  432. //
  433. if(m_pEditor->HasTextChanged())
  434. {
  435. //
  436. // If we are not added to the workspace
  437. //
  438. if(!m_pEditor->GetMyWorkspace())
  439. {
  440. m_pEditor->SetAllAttribs();
  441. m_pEditor->AddToWorkspace();
  442. }
  443. else
  444. {
  445. m_pEditor->OnObjectEdit();
  446. }
  447. }
  448. }
  449. //
  450. // WbTextBox::OnLButtonUp()
  451. //
  452. void WbTextBox::OnLButtonUp(UINT nFlags, int x, int y)
  453. {
  454. POINT ptCaret;
  455. POINT ptPos;
  456. ::GetCaretPos(&ptCaret);
  457. m_pEditor->GetAnchorPoint(&ptPos);
  458. m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x;
  459. m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y;
  460. }
  461. //
  462. // WbTextBox::OnMouseMove()
  463. //
  464. void WbTextBox::OnMouseMove(UINT nFlags, int x, int y)
  465. {
  466. if (nFlags & MK_LBUTTON )
  467. {
  468. // we're dragging
  469. ::HideCaret(m_hwnd);
  470. AutoCaretScroll();
  471. ::ShowCaret(m_hwnd);
  472. }
  473. }
  474. void WbTextBox::AutoCaretScroll( void )
  475. {
  476. POINT ptCaret;
  477. POINT ptPos;
  478. ::GetCaretPos(&ptCaret);
  479. m_pEditor->GetAnchorPoint(&ptPos);
  480. ptCaret.x += ptPos.x;
  481. ptCaret.y += ptPos.y;
  482. g_pDraw->AutoScroll(ptCaret.x, ptCaret.y, FALSE, m_pEditor->m_cursorXYPos.x,
  483. m_pEditor->m_cursorXYPos.y);
  484. m_pEditor->m_cursorXYPos = ptCaret;
  485. }
  486. void WbTextBox::SetupBackgroundRepaint
  487. (
  488. POINT ptTopPaint,
  489. BOOL bNumLinesChanged
  490. )
  491. {
  492. RECT rectBox;
  493. ::GetClientRect(m_hwnd, &rectBox);
  494. if (ptTopPaint.y == -1)
  495. {
  496. ::GetCaretPos(&ptTopPaint);
  497. }
  498. ASSERT(m_pEditor->m_textMetrics.tmHeight != DBG_UNINIT);
  499. rectBox.top = ptTopPaint.y;
  500. if( !bNumLinesChanged )
  501. rectBox.bottom = rectBox.top + m_pEditor->m_textMetrics.tmHeight;
  502. ::InvalidateRect(m_hwnd, &rectBox, TRUE );
  503. }
  504. //
  505. // Selects at least one (DBCS) char if there is not a selection already.
  506. //
  507. void WbTextBox::SelectAtLeastOne( void )
  508. {
  509. int nFirst, nLast;
  510. TCHAR * szBoxText;
  511. ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&nFirst, (LPARAM)&nLast);
  512. if( nFirst == nLast )
  513. {
  514. int textSize = ::GetWindowTextLength(m_hwnd);
  515. DBG_SAVE_FILE_LINE
  516. szBoxText = new TCHAR[textSize + 1];
  517. if (!szBoxText)
  518. {
  519. ERROR_OUT(("Failed to allocate TCHAR array for object text"));
  520. }
  521. else
  522. {
  523. ::GetWindowText( m_hwnd, szBoxText, textSize+1);
  524. if (nFirst < textSize)
  525. {
  526. nLast++;
  527. if( IsDBCSLeadByte( (BYTE) szBoxText[ nFirst ] )||
  528. (szBoxText[ nFirst ] == _T('\r'))
  529. )
  530. nLast++;
  531. ::SendMessage(m_hwnd, EM_SETSEL, nFirst, nLast);
  532. }
  533. delete szBoxText;
  534. }
  535. }
  536. }
  537. void WbTextBox::OnMove(int x, int y)
  538. {
  539. if (m_pEditor->m_nLastShow == SW_SHOW)
  540. {
  541. FitBox();
  542. ::ShowCaret(m_hwnd);
  543. }
  544. }
  545. int WbTextBox::GetMaxCharHeight( void )
  546. {
  547. ASSERT(m_pEditor->m_textMetrics.tmHeight != DBG_UNINIT);
  548. return( m_pEditor->m_textMetrics.tmHeight );
  549. }
  550. int WbTextBox::GetMaxCharWidth( void )
  551. {
  552. ASSERT(m_pEditor->m_textMetrics.tmMaxCharWidth != DBG_UNINIT);
  553. return( m_pEditor->m_textMetrics.tmMaxCharWidth );
  554. }
  555. //
  556. // Aborts and shuts down text editor without hitting the cores. Call this
  557. // to shutdown editing during a lock condition (that we don't own)
  558. //
  559. void WbTextBox::AbortEditGently( void )
  560. {
  561. RECT boundsRect;
  562. // shut down rejecting all edits
  563. g_pDraw->EndTextEntry(FALSE);
  564. // Delete the text object
  565. if (g_pDraw->m_pTextEditor != NULL)
  566. {
  567. m_pEditor->GetBoundsRect(&boundsRect);
  568. delete g_pDraw->m_pTextEditor; // zap in memory original
  569. g_pDraw->m_pTextEditor = NULL; // text object, loose any current edits
  570. m_pEditor = NULL;
  571. }
  572. else
  573. {
  574. SetRectEmpty(&boundsRect);
  575. }
  576. // Redraw any altered parts of the screen
  577. g_pDraw->InvalidateSurfaceRect(&boundsRect, TRUE);
  578. }
  579. //
  580. //
  581. // Function: WbTextEditor
  582. //
  583. // Purpose: Constructor
  584. //
  585. //
  586. WbTextEditor::WbTextEditor(void)
  587. {
  588. // Initialize the cursor position
  589. m_cursorCharPos.x = 0;
  590. m_cursorCharPos.y = 0;
  591. // set parent for editbox
  592. m_pEditBox = NULL;
  593. m_cursorXYPos.x = 0;
  594. m_cursorXYPos.y = 0;
  595. m_bFirstSetFontCall = TRUE;
  596. m_nLastShow = -1;
  597. }
  598. WbTextEditor::~WbTextEditor(void)
  599. {
  600. if (m_pEditBox != NULL)
  601. {
  602. delete m_pEditBox;
  603. m_pEditBox = NULL;
  604. }
  605. }
  606. //
  607. //
  608. // Function: SetCursorPosFromPoint
  609. //
  610. // Purpose: Return the character position most closely matching a
  611. // given co-ordinate position in the text object.
  612. //
  613. //
  614. void WbTextEditor::SetCursorPosFromPoint(POINT pointXY)
  615. {
  616. int nCharPos;
  617. if (::PtInRect(&m_rect, pointXY))
  618. {
  619. // make point relative to editbox
  620. pointXY.x -= g_pDraw->m_originOffset.cx;
  621. pointXY.y -= g_pDraw->m_originOffset.cy;
  622. ::MapWindowPoints(g_pDraw->m_hwnd, m_pEditBox->m_hwnd,
  623. &pointXY, 1);
  624. ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONDOWN, 0,
  625. MAKELONG( pointXY.x, pointXY.y ) );
  626. ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONUP, 0,
  627. MAKELONG( pointXY.x, pointXY.y ) );
  628. // get char index in editbox
  629. nCharPos = (int)::SendMessage(m_pEditBox->m_hwnd, EM_CHARFROMPOS, 0,
  630. MAKELPARAM(pointXY.x, pointXY.y));
  631. if( nCharPos < 0 )
  632. return;
  633. // Set the new cursor char co-ordinates
  634. m_cursorCharPos.x = (short)LOWORD(nCharPos);
  635. m_cursorCharPos.y = (short)HIWORD(nCharPos);
  636. // Move the cursor to the new position
  637. GetXYPosition(m_cursorCharPos, &m_cursorXYPos);
  638. }
  639. }
  640. //
  641. //
  642. // Function: GetCursorSize
  643. //
  644. // Purpose: Return the cursor size for the current font
  645. //
  646. //
  647. void WbTextEditor::GetCursorSize(LPSIZE lpsize)
  648. {
  649. ASSERT(m_textMetrics.tmHeight != DBG_UNINIT);
  650. lpsize->cx = ::GetSystemMetrics(SM_CXBORDER);
  651. lpsize->cy = m_textMetrics.tmHeight;
  652. }
  653. //
  654. //
  655. // Function: XYPosition
  656. //
  657. // Purpose: Calculate the X,Y co-ordinates of a character position
  658. //
  659. //
  660. void WbTextEditor::GetXYPosition(POINT pointChar, LPPOINT lpptXY)
  661. {
  662. int nCharIndex;
  663. LRESULT dwCharPos;
  664. nCharIndex = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, pointChar.y, 0)
  665. + pointChar.x;
  666. GetAnchorPoint(lpptXY);
  667. dwCharPos = ::SendMessage(m_pEditBox->m_hwnd, EM_POSFROMCHAR, nCharIndex, 0);
  668. lpptXY->x += (short)LOWORD(dwCharPos);
  669. lpptXY->y += (short)HIWORD(dwCharPos);
  670. }
  671. //
  672. //
  673. // Function: Clear
  674. //
  675. // Purpose: Clear the text editor
  676. //
  677. //
  678. void WbTextEditor::Clear(void)
  679. {
  680. RECT cEWndRect;
  681. // Remove all the current stored text
  682. strTextArray.RemoveAll();
  683. // Reset the cursor position
  684. m_cursorCharPos.x = 0;
  685. m_cursorCharPos.y = 0;
  686. // clear editbox
  687. ::SetWindowText(m_pEditBox->m_hwnd, _TEXT(""));
  688. // init editbox size
  689. m_rect.right = m_rect.left + 2*m_pEditBox->GetMaxCharWidth();
  690. m_rect.bottom = m_rect.top + 2*m_pEditBox->GetMaxCharHeight();
  691. SetBoundsRect(&m_rect);
  692. cEWndRect = m_rect;
  693. ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x),
  694. -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) );
  695. ::BringWindowToTop(m_pEditBox->m_hwnd);
  696. ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top,
  697. cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top,
  698. TRUE);
  699. }
  700. //
  701. //
  702. // Function: New
  703. //
  704. // Purpose: Clear the text editor and reset the graphic handle
  705. //
  706. //
  707. BOOL WbTextEditor::New(void)
  708. {
  709. // create editbox
  710. if (!Create())
  711. {
  712. ERROR_OUT(("Error creating drawing area window"));
  713. return(FALSE);
  714. }
  715. // Clear the object
  716. Clear();
  717. return(TRUE);
  718. }
  719. //
  720. //
  721. // Function: SetTextObject
  722. //
  723. // Purpose: Attach a text object to the editor
  724. //
  725. //
  726. BOOL WbTextEditor::SetTextObject(TextObj* ptext)
  727. {
  728. // create editbox
  729. if (!Create())
  730. {
  731. ERROR_OUT(("Error creating drawing area window"));
  732. return(FALSE);
  733. }
  734. // setup font
  735. SetFont( ptext->m_hFont );
  736. POINT pPoint;
  737. ptext->GetAnchorPoint(&pPoint);
  738. MoveTo(pPoint.x, pPoint.y);
  739. // copy the text to the editbox
  740. GetText();
  741. return(TRUE);
  742. }
  743. //
  744. //
  745. // Function: IsEmpty
  746. //
  747. // Purpose: Return TRUE if there is no text in the object
  748. //
  749. //
  750. BOOL WbTextEditor::IsEmpty(void)
  751. {
  752. return(::GetWindowTextLength(m_pEditBox->m_hwnd) <= 0 );
  753. }
  754. void WbTextEditor::PutText(void)
  755. {
  756. int nNumLines;
  757. int i;
  758. int nMaxLineLen, nLineLen;
  759. int nLine;
  760. TCHAR *cbuf;
  761. WbTextEditor *pThis;
  762. pThis = (WbTextEditor *)this; // overide const declaration
  763. nNumLines = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINECOUNT, 0, 0);
  764. // figure out buf size
  765. nMaxLineLen = 0;
  766. for (i = 0; i < nNumLines; i++)
  767. {
  768. nLine = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, i, 0);
  769. nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINELENGTH, nLine, 0);
  770. if (nMaxLineLen < nLineLen)
  771. nMaxLineLen = nLineLen;
  772. }
  773. // make buf
  774. nMaxLineLen += sizeof(WORD);
  775. DBG_SAVE_FILE_LINE
  776. cbuf = new TCHAR[nMaxLineLen];
  777. if( cbuf == NULL )
  778. {
  779. ERROR_OUT(("PutText failing; couldn't allocate TCHAR array"));
  780. return;
  781. }
  782. //
  783. // copy editbox innards to textbox string
  784. // Again, we know in advance how many lines; use SetSize/SetAt()
  785. //
  786. strTextArray.RemoveAll();
  787. strTextArray.SetSize(nNumLines);
  788. for(i = 0; i < nNumLines; i++)
  789. {
  790. *(LPWORD)cbuf = (WORD)nMaxLineLen;
  791. nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINE, i, (LPARAM)cbuf);
  792. cbuf[nLineLen] = _T('\0');
  793. strTextArray.SetAt(i, cbuf );
  794. }
  795. // clean up
  796. delete cbuf;
  797. }
  798. void WbTextEditor::GetText(void)
  799. {
  800. int nNumLines;
  801. int textSize = 0;
  802. int i;
  803. TCHAR * pText = NULL;
  804. TCHAR * pStartText;
  805. nNumLines = strTextArray.GetSize();
  806. //
  807. // Calculate the buffer size we need
  808. //
  809. for (i = 0; i < nNumLines; i++ )
  810. {
  811. textSize += lstrlen(strTextArray[i]);
  812. if ((i + 1) < nNumLines)
  813. textSize += lstrlen(_T("\r\n"));
  814. }
  815. //
  816. // Get the lines, with \r\n separating them
  817. //
  818. DBG_SAVE_FILE_LINE
  819. pText = new TCHAR[textSize + 1];
  820. if (!pText)
  821. {
  822. ERROR_OUT(("GetText failing; couldn't allocate TCHAR array"));
  823. }
  824. else
  825. {
  826. // Null this out in casae textSize is 0.
  827. pStartText = pText;
  828. pStartText[0] = 0;
  829. for (i = 0; i < nNumLines; i++)
  830. {
  831. lstrcpy(pStartText, strTextArray[i]);
  832. pStartText += lstrlen(strTextArray[i]);
  833. if ((i + 1) < nNumLines)
  834. {
  835. lstrcpy(pStartText, _T("\r\n"));
  836. pStartText += lstrlen(_T("\r\n"));
  837. }
  838. }
  839. ::SetWindowText(m_pEditBox->m_hwnd, pText);
  840. delete pText;
  841. }
  842. }
  843. void WbTextEditor::CalculateBoundsRect( void )
  844. {
  845. RECT cEWndRect;
  846. ASSERT(m_pEditBox);
  847. ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect);
  848. ::MapWindowPoints(m_pEditBox->m_hwnd, g_pDraw->m_hwnd,
  849. (LPPOINT)&cEWndRect, 2);
  850. m_rect = cEWndRect;
  851. ::OffsetRect(&m_rect, g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x,
  852. g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y);//+1 );
  853. SetBoundsRect(&m_rect);
  854. ::BringWindowToTop(m_pEditBox->m_hwnd);
  855. }
  856. // set editbox visibility
  857. void WbTextEditor::ShowBox( int nShow )
  858. {
  859. if (m_nLastShow == nShow)
  860. {
  861. if( nShow == SW_SHOW)
  862. ::SetFocus(m_pEditBox->m_hwnd);
  863. return;
  864. }
  865. m_nLastShow = nShow;
  866. if (nShow == SW_SHOW)
  867. {
  868. // show it
  869. if (m_pEditBox != NULL)
  870. {
  871. ::ShowWindow(m_pEditBox->m_hwnd, SW_SHOW);
  872. ::BringWindowToTop(m_pEditBox->m_hwnd);
  873. ::SetFocus(m_pEditBox->m_hwnd);
  874. ::HideCaret(m_pEditBox->m_hwnd);
  875. m_pEditBox->FitBox();
  876. ::ShowCaret(m_pEditBox->m_hwnd);
  877. }
  878. }
  879. else
  880. {
  881. if (m_pEditBox != NULL)
  882. {
  883. ::ShowWindow(m_pEditBox->m_hwnd, SW_HIDE);
  884. m_bFirstSetFontCall = TRUE;
  885. KillTimer();
  886. delete m_pEditBox;
  887. m_pEditBox = NULL;
  888. }
  889. }
  890. }
  891. //
  892. // Create()
  893. //
  894. // Creates the edit box window
  895. //
  896. BOOL WbTextEditor::Create( void )
  897. {
  898. if (m_pEditBox == NULL)
  899. {
  900. DBG_SAVE_FILE_LINE
  901. m_pEditBox = new WbTextBox(this);
  902. if (m_pEditBox == NULL)
  903. {
  904. ERROR_OUT(("Couldn't create edit text box"));
  905. return(FALSE);
  906. }
  907. if (!m_pEditBox->Create(g_pDraw->m_hwnd))
  908. {
  909. ERROR_OUT(("WbTextEditor::Create failed; can't create edit field"));
  910. return(FALSE);
  911. }
  912. }
  913. return(TRUE);
  914. }
  915. void WbTextEditor::MoveBy(int cx, int cy)
  916. {
  917. RECT cEWndRect;
  918. ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect);
  919. if ((m_rect.right - m_rect.left) < (cEWndRect.right - cEWndRect.left))
  920. m_rect.right = m_rect.left + (cEWndRect.right - cEWndRect.left);
  921. if ((m_rect.bottom - m_rect.top) < (cEWndRect.bottom - cEWndRect.top))
  922. m_rect.bottom = m_rect.top + (cEWndRect.bottom - cEWndRect.top);
  923. cEWndRect = m_rect;
  924. ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x),
  925. -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) );//+1) );
  926. ::BringWindowToTop(m_pEditBox->m_hwnd);
  927. ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top,
  928. cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top,
  929. TRUE);
  930. }
  931. void WbTextEditor::SetFont( LOGFONT *pLogFont, BOOL bDummy )
  932. {
  933. // Ignore bDummy. Had to add that so polymorph will work correctly
  934. // for DCWbGraphicText::SetFont()
  935. // Have to delay recalc of bounding rect because editbox will have a
  936. // bogus (bad) font until SetFont is called. Can't call SetFont
  937. // before this because new font hasn't been made yet.
  938. TextObj::SetFont( pLogFont, FALSE );
  939. if(m_pEditBox)
  940. {
  941. ::SendMessage(m_pEditBox->m_hwnd, WM_SETFONT, (WPARAM)m_hFont, TRUE);
  942. // now do bounding rect
  943. CalculateBoundsRect();
  944. if( m_bFirstSetFontCall )
  945. m_bFirstSetFontCall = FALSE;
  946. else
  947. {
  948. ::HideCaret(m_pEditBox->m_hwnd);
  949. m_pEditBox->FitBox();
  950. m_pEditBox->AutoCaretScroll();
  951. ::ShowCaret(m_pEditBox->m_hwnd);
  952. }
  953. }
  954. }
  955. void WbTextEditor::SetTimer( UINT nElapse )
  956. {
  957. ::SetTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE, nElapse, NULL);
  958. }
  959. void WbTextEditor::KillTimer( void )
  960. {
  961. if(m_pEditBox)
  962. {
  963. ::KillTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE);
  964. }
  965. }
  966. // Resets editbox painting for a resized drawing window
  967. void WbTextEditor::ParentResize( void )
  968. {
  969. ::HideCaret(m_pEditBox->m_hwnd);
  970. m_pEditBox->FitBox();
  971. ::ShowCaret(m_pEditBox->m_hwnd);
  972. }
  973. void WbTextEditor::RedrawEditbox(void)
  974. {
  975. ::InvalidateRect(m_pEditBox->m_hwnd, NULL, TRUE);
  976. m_pEditBox->FitBox();
  977. }
  978.