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.

1316 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, (LPARAM)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(wParam);
  147. break;
  148. case WM_MOUSEMOVE:
  149. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  150. ptb->OnMouseMove((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
  151. break;
  152. case WM_LBUTTONUP:
  153. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  154. ptb->OnLButtonUp((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
  155. break;
  156. case WM_MOVE:
  157. lResult = ::CallWindowProc(ptb->m_pfnEditPrev, hwnd, message, wParam, lParam);
  158. ptb->OnMove(LOWORD(lParam), 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->GetPosition(&ptPos);
  176. m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x;
  177. m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y;
  178. m_pEditor->m_bChanged = TRUE;
  179. }
  180. //
  181. // OnUndoPaste()
  182. //
  183. void WbTextBox::OnUndoPaste(void)
  184. {
  185. FitBox();
  186. AutoCaretScroll();
  187. m_pEditor->m_bChanged = TRUE;
  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->m_bChanged = TRUE;
  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->GetPosition(&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. szBoxText = new TCHAR[2];
  300. if (!szBoxText)
  301. {
  302. ERROR_OUT(("Failed to allocate TCHAR array for empty text"));
  303. goto bail_out;
  304. }
  305. else
  306. {
  307. lstrcpy(szBoxText, _T(" "));
  308. }
  309. }
  310. else
  311. {
  312. szBoxText = new TCHAR[textSize+1];
  313. if (!szBoxText)
  314. {
  315. ERROR_OUT(("Failed to allocate TCHAR array for object text"));
  316. goto bail_out;
  317. }
  318. else
  319. {
  320. ::GetWindowText(m_hwnd, (LPTSTR)szBoxText, textSize+1);
  321. }
  322. }
  323. // Have to check string length for NT. crEditRect comes back from
  324. // DrawText giant-positive (i.e., still equal to m_MaxRect) if szBoxText
  325. // is empty. This causes crEditRect to overflow to giant negative later
  326. // resulting in very bizare painting problems. Win95 doesn't care because
  327. // it returns 16bit results that look like -1 instead of giant positive.
  328. ::GetClientRect(g_pDraw->m_hwnd, &crDrawWnd );
  329. // BUG 464 -DrawText doesn't calc widths involving tabs the same way a
  330. // standard edit control does so replace it with DrawTextEx
  331. // using the DT_EDITCONTROL format
  332. crEditRect = m_MaxRect;
  333. pszText = szBoxText;
  334. nTextLen = lstrlen(szBoxText);
  335. ::DrawTextEx( hDC,
  336. pszText, nTextLen,
  337. &crEditRect,
  338. DT_CALCRECT | DT_EXPANDTABS | DT_NOPREFIX | DT_EDITCONTROL,
  339. NULL );
  340. // BUG 464 (continued) -DrawTextEx doesn't include blank-last-lines in its height
  341. // calc like DrawText does so have to add an extra line
  342. // height for blank lines to have same behavior as DrawText
  343. if ((nTextLen >= 2) && !IsDBCSLeadByte(pszText[nTextLen-2]) && (pszText[nTextLen-1] == _T('\n')) )
  344. crEditRect.bottom += m_pEditor->m_textMetrics.tmHeight;
  345. // NT sanity check if this still fails
  346. if ( ((crEditRect.right - crEditRect.left) == (m_MaxRect.right - m_MaxRect.left)) ||
  347. ((crEditRect.right - crEditRect.left) <= 0) )
  348. {
  349. crEditRect.right = crEditRect.left + crDrawWnd.right - crDrawWnd.left;
  350. }
  351. if ( ((crEditRect.bottom - crEditRect.top) == (m_MaxRect.bottom - m_MaxRect.top)) ||
  352. ((crEditRect.bottom - crEditRect.top) <= 0) )
  353. {
  354. crEditRect.bottom = crEditRect.top + crDrawWnd.bottom - crDrawWnd.top;
  355. }
  356. ::GetClientRect(m_hwnd, &crEditBox);
  357. ::GetWindowRect(m_hwnd, &crEditWnd);
  358. ::MapWindowPoints(NULL, g_pDraw->m_hwnd, (LPPOINT)&crEditWnd, 2);
  359. // do min size check for IME's.
  360. int nMinWidth;
  361. nMinWidth = MIN_FITBOX_CHARS*m_pEditor->m_textMetrics.tmMaxCharWidth;
  362. if ((crEditRect.right - crEditRect.left) < nMinWidth )
  363. crEditRect.right = crEditRect.left + nMinWidth;
  364. // must add some slop to prevent autoscroll from kicking in
  365. crEditRect.right += 2*m_pEditor->m_textMetrics.tmMaxCharWidth;
  366. crEditRect.bottom += m_pEditor->m_textMetrics.tmHeight;
  367. //pretend we had a right or bottom scroll
  368. ::OffsetRect(&crEditRect, -1, -1);
  369. nDeltaWidth = (crEditRect.right - crEditRect.left) - (crEditBox.right - crEditBox.left);
  370. if (nDeltaWidth > 0)
  371. {
  372. bNoChanges = FALSE;
  373. if ( crEditRect.left < 0 )
  374. {
  375. // right scroll, adjust right edge
  376. crEditWnd.right += nDeltaWidth;
  377. }
  378. else
  379. {
  380. // left scroll, adjust left edge
  381. crEditWnd.left -= nDeltaWidth;
  382. }
  383. }
  384. nDeltaHeight = (crEditRect.bottom - crEditRect.top) - (crEditBox.bottom - crEditBox.top);
  385. if (nDeltaHeight > 0)
  386. {
  387. bNoChanges = FALSE;
  388. if( crEditRect.left < 0 )
  389. {
  390. // bottom scroll, adjust bottom edge
  391. crEditWnd.bottom += nDeltaHeight;
  392. }
  393. else
  394. {
  395. // top scroll, adjust top edge
  396. crEditWnd.top -= nDeltaHeight;
  397. }
  398. }
  399. if( bNoChanges )
  400. goto bail_out;
  401. // resize
  402. ::MoveWindow(m_hwnd, crEditWnd.left, crEditWnd.top,
  403. crEditWnd.right - crEditWnd.left, crEditWnd.bottom - crEditWnd.top, TRUE );
  404. // update bounding box
  405. ::GetClientRect(m_hwnd, &crEditBox);
  406. ::MapWindowPoints(m_hwnd, g_pDraw->m_hwnd, (LPPOINT)&crEditBox, 2);
  407. ::OffsetRect(&crEditBox, g_pDraw->m_originOffset.cx+1 + m_ptNTBooger.x,
  408. g_pDraw->m_originOffset.cy + m_ptNTBooger.y );//+1 );
  409. m_pEditor->m_boundsRect = crEditBox;
  410. bail_out:
  411. if (hDC != NULL )
  412. {
  413. SelectFont(hDC, hSaveFont);
  414. ::ReleaseDC(m_hwnd, hDC );
  415. }
  416. delete szBoxText;
  417. return( !bNoChanges );
  418. }
  419. void WbTextBox::OnTimer(UINT_PTR nIDEvent)
  420. {
  421. TRACE_TIMER(("WbTextBox::OnTimer"));
  422. // If the text object has not been added to the page before...
  423. if (m_pEditor->Handle() == NULL)
  424. {
  425. // Add it now
  426. m_pEditor->AddToPageLast(g_pDraw->Page());
  427. }
  428. else
  429. {
  430. // Otherwise, replace the external copy
  431. m_pEditor->Replace();
  432. }
  433. }
  434. //
  435. // WbTextBox::OnLButtonUp()
  436. //
  437. void WbTextBox::OnLButtonUp(UINT nFlags, int x, int y)
  438. {
  439. POINT ptCaret;
  440. POINT ptPos;
  441. ::GetCaretPos(&ptCaret);
  442. m_pEditor->GetPosition(&ptPos);
  443. m_pEditor->m_cursorXYPos.x = ptCaret.x + ptPos.x;
  444. m_pEditor->m_cursorXYPos.y = ptCaret.y + ptPos.y;
  445. }
  446. //
  447. // WbTextBox::OnMouseMove()
  448. //
  449. void WbTextBox::OnMouseMove(UINT nFlags, int x, int y)
  450. {
  451. if (nFlags & MK_LBUTTON )
  452. {
  453. // we're dragging
  454. ::HideCaret(m_hwnd);
  455. AutoCaretScroll();
  456. ::ShowCaret(m_hwnd);
  457. }
  458. }
  459. void WbTextBox::AutoCaretScroll( void )
  460. {
  461. POINT ptCaret;
  462. POINT ptPos;
  463. ::GetCaretPos(&ptCaret);
  464. m_pEditor->GetPosition(&ptPos);
  465. ptCaret.x += ptPos.x;
  466. ptCaret.y += ptPos.y;
  467. g_pDraw->AutoScroll(ptCaret.x, ptCaret.y, FALSE, m_pEditor->m_cursorXYPos.x,
  468. m_pEditor->m_cursorXYPos.y);
  469. m_pEditor->m_cursorXYPos = ptCaret;
  470. }
  471. void WbTextBox::SetupBackgroundRepaint
  472. (
  473. POINT ptTopPaint,
  474. BOOL bNumLinesChanged
  475. )
  476. {
  477. RECT rectBox;
  478. ::GetClientRect(m_hwnd, &rectBox);
  479. if (ptTopPaint.y == -1)
  480. {
  481. ::GetCaretPos(&ptTopPaint);
  482. }
  483. rectBox.top = ptTopPaint.y;
  484. if( !bNumLinesChanged )
  485. rectBox.bottom = rectBox.top + m_pEditor->m_textMetrics.tmHeight;
  486. ::InvalidateRect(m_hwnd, &rectBox, TRUE );
  487. }
  488. //
  489. // Selects at least one (DBCS) char if there is not a selection already.
  490. //
  491. void WbTextBox::SelectAtLeastOne( void )
  492. {
  493. int nFirst, nLast;
  494. TCHAR * szBoxText;
  495. ::SendMessage(m_hwnd, EM_GETSEL, (WPARAM)&nFirst, (LPARAM)&nLast);
  496. if( nFirst == nLast )
  497. {
  498. int textSize = ::GetWindowTextLength(m_hwnd);
  499. szBoxText = new TCHAR[textSize + 1];
  500. if (!szBoxText)
  501. {
  502. ERROR_OUT(("Failed to allocate TCHAR array for object text"));
  503. }
  504. else
  505. {
  506. ::GetWindowText( m_hwnd, szBoxText, textSize+1);
  507. if (nFirst < textSize)
  508. {
  509. nLast++;
  510. if( IsDBCSLeadByte( (BYTE) szBoxText[ nFirst ] )||
  511. (szBoxText[ nFirst ] == _T('\r'))
  512. )
  513. nLast++;
  514. ::SendMessage(m_hwnd, EM_SETSEL, nFirst, nLast);
  515. }
  516. delete szBoxText;
  517. }
  518. }
  519. }
  520. void WbTextBox::OnMove(int x, int y)
  521. {
  522. if (m_pEditor->m_nLastShow == SW_SHOW)
  523. {
  524. FitBox();
  525. ::ShowCaret(m_hwnd);
  526. }
  527. }
  528. int WbTextBox::GetMaxCharHeight( void )
  529. {
  530. return( m_pEditor->m_textMetrics.tmHeight );
  531. }
  532. int WbTextBox::GetMaxCharWidth( void )
  533. {
  534. return( m_pEditor->m_textMetrics.tmMaxCharWidth );
  535. }
  536. //
  537. // Aborts and shuts down text editor without hitting the cores. Call this
  538. // to shutdown editing during a lock condition (that we don't own)
  539. //
  540. void WbTextBox::AbortEditGently( void )
  541. {
  542. RECT boundsRect;
  543. // have to be carefull how we bail out of this so we
  544. // don't fall into an infinte exception loop (bugs 3550, 3556)
  545. if (g_pDraw->m_pActiveText != NULL)
  546. {
  547. delete g_pDraw->m_pActiveText; // zap in memory original
  548. g_pDraw->m_pActiveText = NULL; // text object, loose any current
  549. // edits
  550. }
  551. //
  552. // NOTE:
  553. // Can't reread the text object from cores to update
  554. // boundsrect because the code that does that
  555. // (DCWbGraphic::ReadExternal()) tries to lock
  556. // the object and off we go into another exception
  557. // loop. SO, the boundsrect we use below might
  558. // not be the right size and the text might
  559. // appear to be clipped when we redraw it. This
  560. // will correct itself as soon as another graphic
  561. // update notice comes in from DCL (the lock owner
  562. // types another char). I think we have to live
  563. // with that.
  564. //
  565. m_pEditor->GetBoundsRect(&boundsRect);
  566. // let go of core text object (see above)
  567. // don't unlock if handle is NULL or we will blow up the cores (bug 4621)
  568. if (g_pDraw->m_textEditor.Handle() != NULL )
  569. {
  570. if (g_pDraw->m_textEditor.GotLock() )
  571. {
  572. g_pDraw->m_textEditor.Unlock();
  573. g_pDraw->m_textEditor.ForceUpdate();
  574. }
  575. }
  576. g_pDraw->m_textEditor.ZapHandle();
  577. // shut down rejecting all edits
  578. g_pDraw->EndTextEntry(FALSE);
  579. // Redraw any altered parts of the screen
  580. g_pDraw->InvalidateSurfaceRect(&boundsRect);
  581. }
  582. //
  583. //
  584. // Function: WbTextEditor
  585. //
  586. // Purpose: Constructor
  587. //
  588. //
  589. WbTextEditor::WbTextEditor(void)
  590. {
  591. // Initialize the cursor position
  592. m_cursorCharPos.x = 0;
  593. m_cursorCharPos.y = 0;
  594. // set parent for editbox
  595. m_pEditBox = NULL;
  596. m_cursorXYPos.x = 0;
  597. m_cursorXYPos.y = 0;
  598. m_bFirstSetFontCall = TRUE;
  599. m_nLastShow = -1;
  600. }
  601. WbTextEditor::~WbTextEditor(void)
  602. {
  603. if (m_pEditBox != NULL)
  604. {
  605. delete m_pEditBox;
  606. m_pEditBox = NULL;
  607. }
  608. }
  609. //
  610. //
  611. // Function: SetCursorPosFromPoint
  612. //
  613. // Purpose: Return the character position most closely matching a
  614. // given co-ordinate position in the text object.
  615. //
  616. //
  617. void WbTextEditor::SetCursorPosFromPoint(POINT pointXY)
  618. {
  619. int nCharPos;
  620. if (::PtInRect(&m_boundsRect, pointXY))
  621. {
  622. // make point relative to editbox
  623. pointXY.x -= g_pDraw->m_originOffset.cx;
  624. pointXY.y -= g_pDraw->m_originOffset.cy;
  625. ::MapWindowPoints(g_pDraw->m_hwnd, m_pEditBox->m_hwnd,
  626. &pointXY, 1);
  627. ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONDOWN, 0,
  628. MAKELONG( pointXY.x, pointXY.y ) );
  629. ::SendMessage(m_pEditBox->m_hwnd, WM_LBUTTONUP, 0,
  630. MAKELONG( pointXY.x, pointXY.y ) );
  631. // get char index in editbox
  632. nCharPos = (int)::SendMessage(m_pEditBox->m_hwnd, EM_CHARFROMPOS, 0,
  633. MAKELPARAM(pointXY.x, pointXY.y));
  634. if( nCharPos < 0 )
  635. return;
  636. // Set the new cursor char co-ordinates
  637. m_cursorCharPos.x = (short)LOWORD(nCharPos);
  638. m_cursorCharPos.y = (short)HIWORD(nCharPos);
  639. // Move the cursor to the new position
  640. GetXYPosition(m_cursorCharPos, &m_cursorXYPos);
  641. }
  642. }
  643. //
  644. //
  645. // Function: GetCursorSize
  646. //
  647. // Purpose: Return the cursor size for the current font
  648. //
  649. //
  650. void WbTextEditor::GetCursorSize(LPSIZE lpsize)
  651. {
  652. lpsize->cx = ::GetSystemMetrics(SM_CXBORDER);
  653. lpsize->cy = m_textMetrics.tmHeight;
  654. }
  655. //
  656. //
  657. // Function: XYPosition
  658. //
  659. // Purpose: Calculate the X,Y co-ordinates of a character position
  660. //
  661. //
  662. void WbTextEditor::GetXYPosition(POINT pointChar, LPPOINT lpptXY)
  663. {
  664. int nCharIndex;
  665. LRESULT dwCharPos;
  666. nCharIndex = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, pointChar.y, 0)
  667. + pointChar.x;
  668. GetPosition(lpptXY);
  669. dwCharPos = ::SendMessage(m_pEditBox->m_hwnd, EM_POSFROMCHAR, nCharIndex, 0);
  670. lpptXY->x += (short)LOWORD(dwCharPos);
  671. lpptXY->y += (short)HIWORD(dwCharPos);
  672. }
  673. //
  674. //
  675. // Function: Clear
  676. //
  677. // Purpose: Clear the text editor
  678. //
  679. //
  680. void WbTextEditor::Clear(void)
  681. {
  682. RECT cEWndRect;
  683. // Remove all the current stored text
  684. strTextArray.RemoveAll();
  685. strTextArray.Add(_TEXT(""));
  686. // Reset the cursor position
  687. m_cursorCharPos.x = 0;
  688. m_cursorCharPos.y = 0;
  689. // clear editbox
  690. ::SetWindowText(m_pEditBox->m_hwnd, _TEXT(""));
  691. // Show that the text has not been changed
  692. m_bChanged = FALSE;
  693. // init editbox size
  694. m_boundsRect.right = m_boundsRect.left + 2*m_pEditBox->GetMaxCharWidth();
  695. m_boundsRect.bottom = m_boundsRect.top + 2*m_pEditBox->GetMaxCharHeight();
  696. cEWndRect = m_boundsRect;
  697. ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x),
  698. -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) );
  699. ::BringWindowToTop(m_pEditBox->m_hwnd);
  700. ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top,
  701. cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top,
  702. TRUE);
  703. }
  704. //
  705. //
  706. // Function: New
  707. //
  708. // Purpose: Clear the text editor and reset the graphic handle
  709. //
  710. //
  711. BOOL WbTextEditor::New(void)
  712. {
  713. // create editbox
  714. if (!Create())
  715. {
  716. ERROR_OUT(("Error creating drawing area window"));
  717. return(FALSE);
  718. }
  719. // Clear the object
  720. Clear();
  721. // Show that the text has not been changed
  722. m_bChanged = FALSE;
  723. // Reset the graphic and page handles
  724. m_hGraphic = NULL;
  725. m_hPage = WB_PAGE_HANDLE_NULL;
  726. return(TRUE);
  727. }
  728. //
  729. //
  730. // Function: SetTextObject
  731. //
  732. // Purpose: Attach a text object to the editor
  733. //
  734. //
  735. BOOL WbTextEditor::SetTextObject(DCWbGraphicText* ptext)
  736. {
  737. // create editbox
  738. if (!Create())
  739. {
  740. ERROR_OUT(("Error creating drawing area window"));
  741. return(FALSE);
  742. }
  743. // Clear and place the object
  744. Clear();
  745. // setup font
  746. DCWbGraphicText::SetFont( ptext->m_hFont );
  747. // Copy the text object's attributes to the editor
  748. SetText(ptext->strTextArray );
  749. ptext->GetBoundsRect(&m_boundsRect);
  750. SetColor(ptext->m_clrPenColor );
  751. MoveTo(m_boundsRect.left, m_boundsRect.top); // avoid kerning offset correction in Position()(bug 469)
  752. // Copy the page and handle of the graphic text object
  753. m_hPage = ptext->m_hPage;
  754. m_hGraphic = ptext->m_hGraphic;
  755. // copy the text to the editbox
  756. GetText();
  757. // Say the text has changed so that all nodes will notice this thing
  758. // is locked - bug 2185
  759. m_bChanged = TRUE;
  760. return(TRUE);
  761. }
  762. //
  763. //
  764. // Function: IsEmpty
  765. //
  766. // Purpose: Return TRUE if there is no text in the object
  767. //
  768. //
  769. BOOL WbTextEditor::IsEmpty(void)
  770. {
  771. return(::GetWindowTextLength(m_pEditBox->m_hwnd) <= 0 );
  772. }
  773. void WbTextEditor::PutText(void)
  774. {
  775. int nNumLines;
  776. int i;
  777. int nMaxLineLen, nLineLen;
  778. int nLine;
  779. TCHAR *cbuf;
  780. WbTextEditor *pThis;
  781. pThis = (WbTextEditor *)this; // overide const declaration
  782. if(m_pEditBox == NULL)
  783. {
  784. return;
  785. }
  786. nNumLines = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINECOUNT, 0, 0);
  787. // figure out buf size
  788. nMaxLineLen = 0;
  789. for (i = 0; i < nNumLines; i++)
  790. {
  791. nLine = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINEINDEX, i, 0);
  792. nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_LINELENGTH, nLine, 0);
  793. if (nMaxLineLen < nLineLen)
  794. nMaxLineLen = nLineLen;
  795. }
  796. // make buf
  797. nMaxLineLen += sizeof(WORD);
  798. cbuf = new TCHAR[nMaxLineLen];
  799. if( cbuf == NULL )
  800. {
  801. ERROR_OUT(("PutText failing; couldn't allocate TCHAR array"));
  802. return;
  803. }
  804. //
  805. // copy editbox innards to textbox string
  806. // Again, we know in advance how many lines; use SetSize/SetAt()
  807. //
  808. strTextArray.RemoveAll();
  809. strTextArray.SetSize(nNumLines);
  810. for(i = 0; i < nNumLines; i++)
  811. {
  812. *(LPWORD)cbuf = (WORD)nMaxLineLen;
  813. nLineLen = (int)::SendMessage(m_pEditBox->m_hwnd, EM_GETLINE, i, (LPARAM)cbuf);
  814. cbuf[nLineLen] = _T('\0');
  815. strTextArray.SetAt(i, cbuf );
  816. }
  817. // clean up
  818. delete cbuf;
  819. }
  820. void WbTextEditor::GetText(void)
  821. {
  822. int nNumLines;
  823. int textSize = 0;
  824. int i;
  825. TCHAR * pText = NULL;
  826. TCHAR * pStartText;
  827. nNumLines = strTextArray.GetSize();
  828. //
  829. // Calculate the buffer size we need
  830. //
  831. for (i = 0; i < nNumLines; i++ )
  832. {
  833. textSize += lstrlen(strTextArray[i]);
  834. if ((i + 1) < nNumLines)
  835. textSize += lstrlen(_T("\r\n"));
  836. }
  837. //
  838. // Get the lines, with \r\n separating them
  839. //
  840. pText = new TCHAR[textSize + 1];
  841. if (!pText)
  842. {
  843. ERROR_OUT(("GetText failing; couldn't allocate TCHAR array"));
  844. }
  845. else
  846. {
  847. // Null this out in casae textSize is 0.
  848. pStartText = pText;
  849. pStartText[0] = 0;
  850. for (i = 0; i < nNumLines; i++)
  851. {
  852. lstrcpy(pStartText, strTextArray[i]);
  853. pStartText += lstrlen(strTextArray[i]);
  854. if ((i + 1) < nNumLines)
  855. {
  856. lstrcpy(pStartText, _T("\r\n"));
  857. pStartText += lstrlen(_T("\r\n"));
  858. }
  859. }
  860. ::SetWindowText(m_pEditBox->m_hwnd, pText);
  861. delete pText;
  862. }
  863. }
  864. DWORD WbTextEditor::CalculateExternalLength( void )
  865. {
  866. // stick text in underlying text object
  867. if( m_bChanged )
  868. PutText();
  869. // fix bounds to get rid off editbox slop
  870. ((WbTextEditor *)this)->DCWbGraphicText::CalculateBoundsRect();
  871. // tell textbox to use it in length calculation
  872. return( DCWbGraphicText::CalculateExternalLength() );
  873. }
  874. void WbTextEditor::CalculateBoundsRect( void )
  875. {
  876. RECT cEWndRect;
  877. ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect);
  878. ::MapWindowPoints(m_pEditBox->m_hwnd, g_pDraw->m_hwnd,
  879. (LPPOINT)&cEWndRect, 2);
  880. m_boundsRect = cEWndRect;
  881. ::OffsetRect(&m_boundsRect, g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x,
  882. g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y);//+1 );
  883. ::BringWindowToTop(m_pEditBox->m_hwnd);
  884. }
  885. // set editbox visibility
  886. void WbTextEditor::ShowBox( int nShow )
  887. {
  888. if (m_nLastShow == nShow)
  889. {
  890. if( nShow == SW_SHOW)
  891. ::SetFocus(m_pEditBox->m_hwnd);
  892. return;
  893. }
  894. m_nLastShow = nShow;
  895. if (nShow == SW_SHOW)
  896. {
  897. // show it
  898. if (m_pEditBox != NULL)
  899. {
  900. ::ShowWindow(m_pEditBox->m_hwnd, SW_SHOW);
  901. ::BringWindowToTop(m_pEditBox->m_hwnd);
  902. ::SetFocus(m_pEditBox->m_hwnd);
  903. ::HideCaret(m_pEditBox->m_hwnd);
  904. m_pEditBox->FitBox();
  905. ::ShowCaret(m_pEditBox->m_hwnd);
  906. }
  907. }
  908. else
  909. {
  910. if (m_pEditBox != NULL)
  911. {
  912. ::ShowWindow(m_pEditBox->m_hwnd, SW_HIDE);
  913. m_bFirstSetFontCall = TRUE;
  914. delete m_pEditBox;
  915. m_pEditBox = NULL;
  916. }
  917. }
  918. }
  919. //
  920. // Create()
  921. //
  922. // Creates the edit box window
  923. //
  924. BOOL WbTextEditor::Create( void )
  925. {
  926. if (m_pEditBox == NULL)
  927. {
  928. m_pEditBox = new WbTextBox(this);
  929. if (m_pEditBox == NULL)
  930. {
  931. ERROR_OUT(("Couldn't create edit text box"));
  932. return(FALSE);
  933. }
  934. if (!m_pEditBox->Create(g_pDraw->m_hwnd))
  935. {
  936. ERROR_OUT(("WbTextEditor::Create failed; can't create edit field"));
  937. return(FALSE);
  938. }
  939. }
  940. return(TRUE);
  941. }
  942. void WbTextEditor::MoveBy(int cx, int cy)
  943. {
  944. RECT cEWndRect;
  945. DCWbGraphicText::MoveBy(cx, cy);
  946. ::GetClientRect(m_pEditBox->m_hwnd, &cEWndRect);
  947. if ((m_boundsRect.right - m_boundsRect.left) < (cEWndRect.right - cEWndRect.left))
  948. m_boundsRect.right = m_boundsRect.left + (cEWndRect.right - cEWndRect.left);
  949. if ((m_boundsRect.bottom - m_boundsRect.top) < (cEWndRect.bottom - cEWndRect.top))
  950. m_boundsRect.bottom = m_boundsRect.top + (cEWndRect.bottom - cEWndRect.top);
  951. cEWndRect = m_boundsRect;
  952. ::OffsetRect(&cEWndRect, -(g_pDraw->m_originOffset.cx+1 + m_pEditBox->m_ptNTBooger.x),
  953. -(g_pDraw->m_originOffset.cy + m_pEditBox->m_ptNTBooger.y) );//+1) );
  954. ::BringWindowToTop(m_pEditBox->m_hwnd);
  955. ::MoveWindow(m_pEditBox->m_hwnd, cEWndRect.left, cEWndRect.top,
  956. cEWndRect.right - cEWndRect.left, cEWndRect.bottom - cEWndRect.top,
  957. TRUE);
  958. }
  959. void WbTextEditor::SetFont( LOGFONT *pLogFont, BOOL bDummy )
  960. {
  961. // Ignore bDummy. Had to add that so polymorph will work correctly
  962. // for DCWbGraphicText::SetFont()
  963. // Have to delay recalc of bounding rect because editbox will have a
  964. // bogus (bad) font until SetFont is called. Can't call SetFont
  965. // before this because new font hasn't been made yet.
  966. DCWbGraphicText::SetFont( pLogFont, FALSE );
  967. ::SendMessage(m_pEditBox->m_hwnd, WM_SETFONT, (WPARAM)m_hFont, TRUE);
  968. // now do bounding rect
  969. CalculateBoundsRect();
  970. if( m_bFirstSetFontCall )
  971. m_bFirstSetFontCall = FALSE;
  972. else
  973. {
  974. ::HideCaret(m_pEditBox->m_hwnd);
  975. m_pEditBox->FitBox();
  976. m_pEditBox->AutoCaretScroll();
  977. ::ShowCaret(m_pEditBox->m_hwnd);
  978. }
  979. }
  980. void WbTextEditor::WriteExtra( PWB_GRAPHIC pHeader )
  981. {
  982. PutText();
  983. DCWbGraphicText::WriteExtra( pHeader );
  984. }
  985. void WbTextEditor::SetTimer( UINT nElapse )
  986. {
  987. ::SetTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE, nElapse, NULL);
  988. }
  989. void WbTextEditor::KillTimer( void )
  990. {
  991. ::KillTimer(m_pEditBox->m_hwnd, TIMER_GRAPHIC_UPDATE);
  992. }
  993. // Resets editbox painting for a resized drawing window
  994. void WbTextEditor::ParentResize( void )
  995. {
  996. ::HideCaret(m_pEditBox->m_hwnd);
  997. m_pEditBox->FitBox();
  998. ::ShowCaret(m_pEditBox->m_hwnd);
  999. }
  1000. void WbTextEditor::RedrawEditbox(void)
  1001. {
  1002. ::InvalidateRect(m_pEditBox->m_hwnd, NULL, TRUE);
  1003. m_pEditBox->FitBox();
  1004. }