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.

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