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.

1400 lines
31 KiB

  1. //
  2. // TEXTOBJ.CPP
  3. // Drawing objects: point, openpolyline, closepolyline, ellipse
  4. //
  5. // Copyright Microsoft 1998-
  6. //
  7. #include "precomp.h"
  8. #include "nmwbobj.h"
  9. TextObj::TextObj(void)
  10. {
  11. #ifdef _DEBUG
  12. FillMemory(&m_textMetrics, sizeof(m_textMetrics), DBG_UNINIT);
  13. #endif // _DEBUG
  14. //
  15. // ALWAYS ZERO OUT m_textMetrics. Calculations depend on the height
  16. // and width of chars being zero before the font is set.
  17. //
  18. ZeroMemory(&m_textMetrics, sizeof(m_textMetrics));
  19. SetMyWorkspace(NULL);
  20. SetOwnerID(g_MyMemberID);
  21. m_ToolType = TOOLTYPE_TEXT;
  22. //
  23. // Created locally, not selected, not editing or deleting.
  24. //
  25. CreatedLocally();
  26. ClearSelectionFlags();
  27. ClearEditionFlags();
  28. ClearDeletionFlags();
  29. SetType(siNonStandardPDU_chosen);
  30. SetFillColor(RGB(-1,-1,-1),TRUE);
  31. SetZOrder(front);
  32. //
  33. // No attributes changed, they will be set as we change them
  34. //
  35. SetWorkspaceHandle(g_pCurrentWorkspace == NULL ? 0 : g_pCurrentWorkspace->GetWorkspaceHandle());
  36. SetType(drawingCreatePDU_chosen);
  37. SetROP(R2_NOTXORPEN);
  38. SetPlaneID(1);
  39. SetMyPosition(NULL);
  40. SetMyWorkspace(NULL);
  41. // 1 Pixels for pen thickness
  42. SetPenThickness(2);
  43. SetAnchorPoint(0,0);
  44. RECT rect;
  45. ::SetRectEmpty(&rect);
  46. SetRect(&rect);
  47. SetBoundsRect(&rect);
  48. m_hFontThumb = ::CreateFont(0,0,0,0,FW_NORMAL,0,0,0,0,OUT_TT_PRECIS,
  49. CLIP_DFA_OVERRIDE, DRAFT_QUALITY,FF_SWISS,NULL);
  50. m_hFont = ::CreateFont(0,0,0,0,FW_NORMAL,0,0,0,0,OUT_TT_PRECIS,
  51. CLIP_DFA_OVERRIDE,
  52. DRAFT_QUALITY,
  53. FF_SWISS,NULL);
  54. m_nKerningOffset = 0;
  55. ResetAttrib();
  56. }
  57. TextObj::~TextObj( void )
  58. {
  59. RemoveObjectFromResendList(this);
  60. RemoveObjectFromRequestHandleList(this);
  61. TRACE_DEBUG(("drawingHandle = %d", GetThisObjectHandle() ));
  62. //
  63. // Tell other nodes that we are gone
  64. //
  65. if(GetMyWorkspace() != NULL && WasDeletedLocally())
  66. {
  67. OnObjectDelete();
  68. }
  69. if(m_hFont)
  70. {
  71. ::DeleteFont(m_hFont);
  72. m_hFont = NULL;
  73. }
  74. if (m_hFontThumb)
  75. {
  76. ::DeleteFont(m_hFontThumb);
  77. m_hFontThumb = NULL;
  78. }
  79. strTextArray.ClearOut();
  80. strTextArray.RemoveAll();
  81. }
  82. void TextObj::TextEditObj (TEXTPDU_ATTRIB* pEditAttrib )
  83. {
  84. RECT rect;
  85. POSITION pos;
  86. POINT anchorPoint;
  87. LONG deltaX = 0;
  88. LONG deltaY = 0;
  89. TRACE_DEBUG(("TextEditObj drawingHandle = %d", GetThisObjectHandle() ));
  90. //
  91. // Was edited remotely
  92. //
  93. ClearEditionFlags();
  94. //
  95. // Get the previous anchor point
  96. //
  97. GetAnchorPoint(&anchorPoint);
  98. //
  99. // Read attributes
  100. //
  101. m_dwChangedAttrib = pEditAttrib->attributesFlag;
  102. GetTextAttrib(pEditAttrib);
  103. //
  104. // Change the anchor point
  105. //
  106. if(HasAnchorPointChanged())
  107. {
  108. {
  109. //
  110. // Get the delta from previous anchor point
  111. //
  112. deltaX -= anchorPoint.x;
  113. deltaY -= anchorPoint.y;
  114. //
  115. // Get the new anchor point
  116. //
  117. GetAnchorPoint(&anchorPoint);
  118. deltaX += anchorPoint.x;
  119. deltaY += anchorPoint.y;
  120. TRACE_DEBUG(("Delta (%d,%d)", deltaX , deltaY));
  121. //
  122. // Was edited remotely
  123. //
  124. ClearEditionFlags();
  125. }
  126. UnDraw();
  127. GetRect(&rect);
  128. ::OffsetRect(&rect, deltaX, deltaY);
  129. SetRect(&rect);
  130. SetBoundsRect(&rect);
  131. }
  132. if(HasAnchorPointChanged() ||
  133. HasFillColorChanged() ||
  134. HasPenColorChanged() ||
  135. HasFontChanged() ||
  136. HasTextChanged())
  137. {
  138. Draw(NULL);
  139. }
  140. else if(HasZOrderChanged())
  141. {
  142. if(GetZOrder() == front)
  143. {
  144. g_pDraw->BringToTopSelection(FALSE, this);
  145. }
  146. else
  147. {
  148. g_pDraw->SendToBackSelection(FALSE, this);
  149. }
  150. }
  151. //
  152. // If it just select/unselected it
  153. //
  154. else if(HasViewStateChanged())
  155. {
  156. ; // do nothing
  157. }
  158. //
  159. // If we have a valid font.
  160. //
  161. else if(GetFont())
  162. {
  163. Draw();
  164. }
  165. //
  166. // Reset all the attributes
  167. //
  168. ResetAttrib();
  169. }
  170. void TextObj::GetTextAttrib(TEXTPDU_ATTRIB * pattributes)
  171. {
  172. if(HasPenColorChanged())
  173. {
  174. SetPenColor(pattributes->textPenColor, TRUE);
  175. }
  176. if(HasFillColorChanged())
  177. {
  178. SetFillColor(pattributes->textFillColor, TRUE);
  179. }
  180. if(HasViewStateChanged())
  181. {
  182. //
  183. // If the other node is selecting the drawing or unselecting
  184. //
  185. if(pattributes->textViewState == selected_chosen)
  186. {
  187. SelectedRemotely();
  188. }
  189. else if(pattributes->textViewState == unselected_chosen)
  190. {
  191. ClearSelectionFlags();
  192. }
  193. SetViewState(pattributes->textViewState);
  194. }
  195. if(HasZOrderChanged())
  196. {
  197. SetZOrder((ZOrder)pattributes->textZOrder);
  198. }
  199. if(HasAnchorPointChanged())
  200. {
  201. SetAnchorPoint(pattributes->textAnchorPoint.x, pattributes->textAnchorPoint.y );
  202. }
  203. if(HasFontChanged())
  204. {
  205. UnDraw();
  206. if(m_hFont)
  207. {
  208. ::DeleteFont(m_hFont);
  209. m_hFont = NULL;
  210. }
  211. m_hFont = ::CreateFontIndirect(&pattributes->textFont);
  212. if (!m_hFont)
  213. {
  214. // Could not create the font
  215. ERROR_OUT(("Failed to create font"));
  216. }
  217. if (m_hFontThumb)
  218. {
  219. ::DeleteFont(m_hFontThumb);
  220. m_hFontThumb = NULL;
  221. }
  222. m_hFontThumb = ::CreateFontIndirect(&pattributes->textFont);
  223. if (!m_hFontThumb)
  224. {
  225. // Could not create the font
  226. ERROR_OUT(("Failed to create thumbnail font"));
  227. }
  228. }
  229. int lines = 0;
  230. UINT maxString = 0;
  231. if(HasTextChanged())
  232. {
  233. BYTE * pBuff = (BYTE *)&pattributes->textString;
  234. VARIABLE_STRING * pVarString = NULL;
  235. lines = pattributes->numberOfLines;
  236. int i;
  237. CHAR * cBuff = NULL;
  238. LPWSTR lpWideCharStr;
  239. for (i = 0; i < lines ; i++)
  240. {
  241. pVarString = (VARIABLE_STRING *) pBuff;
  242. lpWideCharStr = (LPWSTR)&pVarString->string;
  243. UINT strSize = 0;
  244. strSize= WideCharToMultiByte(CP_ACP, 0, lpWideCharStr, -1, NULL, 0, NULL, NULL );
  245. //
  246. // Get the longest string
  247. //
  248. if(strSize > maxString)
  249. {
  250. maxString = strSize;
  251. }
  252. DBG_SAVE_FILE_LINE
  253. cBuff = new TCHAR[strSize];
  254. WideCharToMultiByte(CP_ACP, 0, lpWideCharStr, -1, cBuff, strSize, NULL, NULL );
  255. strTextArray.SetSize(i);
  256. strTextArray.SetAtGrow(i, cBuff );
  257. delete cBuff;
  258. ASSERT(pVarString->header.start.y == i);
  259. pBuff += pVarString->header.len;
  260. }
  261. //
  262. // Calculate the rect
  263. //
  264. if(m_hFont)
  265. {
  266. //
  267. // Remove the old text before we paly with the text size
  268. //
  269. UnDraw();
  270. g_pDraw->PrimeFont(g_pDraw->m_hDCCached, m_hFont, &m_textMetrics);
  271. g_pDraw->UnPrimeFont(g_pDraw->m_hDCCached);
  272. }
  273. }
  274. }
  275. void TextObj::SetTextAttrib(TEXTPDU_ATTRIB * pattributes)
  276. {
  277. if(HasPenColorChanged())
  278. {
  279. GetPenColor(&pattributes->textPenColor);
  280. }
  281. if(HasFillColorChanged())
  282. {
  283. GetFillColor(&pattributes->textFillColor);
  284. }
  285. if(HasViewStateChanged())
  286. {
  287. pattributes->textViewState = GetViewState();
  288. }
  289. if(HasZOrderChanged())
  290. {
  291. pattributes->textZOrder = GetZOrder();
  292. }
  293. if(HasAnchorPointChanged())
  294. {
  295. GetAnchorPoint(&pattributes->textAnchorPoint);
  296. }
  297. if(HasFontChanged())
  298. {
  299. ::GetObject(m_hFont, sizeof(LOGFONT), &pattributes->textFont);
  300. }
  301. if(HasTextChanged())
  302. {
  303. BYTE * pBuff = (BYTE *)&pattributes->textString;
  304. VARIABLE_STRING * pVarString= NULL;
  305. LPWSTR lpWideCharStr;
  306. int size = strTextArray.GetSize();
  307. int i;
  308. for (i = 0; i < size ; i++)
  309. {
  310. pVarString = (VARIABLE_STRING *)pBuff;
  311. lpWideCharStr = (LPWSTR)&pVarString->string;
  312. int strSize = 0;
  313. strSize= MultiByteToWideChar(CP_ACP, 0, strTextArray[i], -1, lpWideCharStr, 0)*sizeof(WCHAR);
  314. MultiByteToWideChar(CP_ACP, 0, strTextArray[i], -1, lpWideCharStr, strSize);
  315. pVarString->header.len = strSize + sizeof(VARIABLE_STRING_HEADER);
  316. pVarString->header.start.x = 0; // JOSEF change that
  317. pVarString->header.start.y = i;
  318. pBuff += pVarString->header.len;
  319. }
  320. pattributes->numberOfLines = size;
  321. //
  322. // Since we are sending text, need to send some font
  323. //
  324. ::GetObject(m_hFont, sizeof(LOGFONT), &pattributes->textFont);
  325. }
  326. }
  327. void TextObj::CreateTextPDU(ASN1octetstring_t *pData, UINT choice)
  328. {
  329. MSTextPDU * pTextPDU = NULL;
  330. UINT stringSize = 0; // Size of all the strings UNICODE
  331. int lines = 0; // Number of text lines
  332. //
  333. // Calculate the size of the whole pdu
  334. //
  335. ULONG length = 0;
  336. if(choice == textDeletePDU_chosen)
  337. {
  338. length = sizeof(MSTextDeletePDU);
  339. }
  340. else
  341. {
  342. //
  343. // Calculate the size of the text
  344. //
  345. if(HasTextChanged())
  346. {
  347. int i;
  348. lines = strTextArray.GetSize();
  349. for (i = 0; i < lines ; i++)
  350. {
  351. stringSize += MultiByteToWideChar(CP_ACP, 0, strTextArray[i], -1, NULL, 0) * sizeof(WCHAR);
  352. }
  353. }
  354. length = sizeof(MSTextPDU) + sizeof(VARIABLE_STRING_HEADER)* lines + stringSize;
  355. }
  356. DBG_SAVE_FILE_LINE
  357. pTextPDU = (MSTextPDU *) new BYTE[length];
  358. //
  359. // PDU choice: create, edit delete
  360. //
  361. pTextPDU->header.nonStandardPDU = choice;
  362. //
  363. // This objects handle
  364. //
  365. pTextPDU->header.textHandle = GetThisObjectHandle();
  366. TRACE_DEBUG(("Text >> Text handle = %d",pTextPDU->header.textHandle ));
  367. //
  368. // This objects workspacehandle
  369. //
  370. WorkspaceObj * pWorkspace = GetMyWorkspace();
  371. ASSERT(pWorkspace);
  372. if(pWorkspace == NULL)
  373. {
  374. delete pTextPDU;
  375. pData->value = NULL;
  376. pData->length = 0;
  377. return;
  378. }
  379. pTextPDU->header.workspaceHandle = pWorkspace->GetThisObjectHandle();
  380. TRACE_DEBUG(("Text >> Workspace handle = %d",pTextPDU->header.workspaceHandle ));
  381. if(choice != textDeletePDU_chosen)
  382. {
  383. //
  384. // Get all the attributes that changed
  385. //
  386. pTextPDU->attrib.attributesFlag = GetPresentAttribs();
  387. SetTextAttrib(&pTextPDU->attrib);
  388. }
  389. //
  390. // Set the pointer for the data that is going to be encoded
  391. //
  392. pData->value = (ASN1octet_t *)pTextPDU;
  393. pData->length = length;
  394. }
  395. void TextObj::UnDraw(void)
  396. {
  397. RECT rect;
  398. GetBoundsRect(&rect);
  399. g_pDraw->InvalidateSurfaceRect(&rect,TRUE);
  400. }
  401. void TextObj::Draw(HDC hDC, BOOL thumbNail, BOOL bPrinting)
  402. {
  403. if(!bPrinting)
  404. {
  405. //
  406. // Don't draw anything if we don't belong in this workspace
  407. //
  408. if(GetWorkspaceHandle() != g_pCurrentWorkspace->GetThisObjectHandle())
  409. {
  410. return;
  411. }
  412. }
  413. RECT clipBox;
  414. BOOL dbcsEnabled = GetSystemMetrics(SM_DBCSENABLED);
  415. INT *tabArray;
  416. UINT ch;
  417. int i,j;
  418. BOOL zoomed = g_pDraw->Zoomed();
  419. int oldBkMode = 0;
  420. int iIndex = 0;
  421. POINT pointPos;
  422. int nLastTab;
  423. ABC abc;
  424. int iLength;
  425. TCHAR * strLine;
  426. MLZ_EntryOut(ZONE_FUNCTION, "DCWbGraphicText::Draw");
  427. if(hDC == NULL)
  428. {
  429. hDC = g_pDraw->m_hDCCached;
  430. }
  431. //
  432. // Only draw anything if the bounding rectangle intersects the current
  433. // clip box.
  434. //
  435. if (::GetClipBox(hDC, &clipBox) == ERROR)
  436. {
  437. WARNING_OUT(("Failed to get clip box"));
  438. }
  439. //
  440. // Select the font.
  441. //
  442. if (thumbNail)
  443. {
  444. TRACE_MSG(("Using thumbnail font"));
  445. g_pDraw->PrimeFont(hDC, m_hFontThumb, &m_textMetrics);
  446. }
  447. else
  448. {
  449. TRACE_MSG(("Using standard font"));
  450. g_pDraw->PrimeFont(hDC, m_hFont, &m_textMetrics);
  451. }
  452. //
  453. // Set the color and mode for drawing.
  454. //
  455. COLORREF rgb;
  456. GetPenColor(&rgb);
  457. ::SetTextColor(hDC, SET_PALETTERGB(rgb));
  458. //
  459. // Set the background to be transparent
  460. //
  461. oldBkMode = ::SetBkMode(hDC, TRANSPARENT);
  462. //
  463. // Calculate the bounding rectangle, accounting for the new font.
  464. //
  465. CalculateBoundsRect();
  466. if (!::IntersectRect(&clipBox, &clipBox, &m_rect))
  467. {
  468. TRACE_MSG(("No clip/bounds intersection"));
  469. return;
  470. }
  471. //
  472. // Get the start point for the text.
  473. //
  474. pointPos.x = m_rect.left + m_nKerningOffset;
  475. pointPos.y = m_rect.top;
  476. //
  477. // Loop through the text strings drawing each as we go.
  478. //
  479. for (iIndex = 0; iIndex < strTextArray.GetSize(); iIndex++)
  480. {
  481. //
  482. // Get a reference to the line to be printed for convenience.
  483. //
  484. strLine = (LPTSTR)strTextArray[iIndex];
  485. iLength = lstrlen(strLine);
  486. //
  487. // Only draw the line if there are any characters in it.
  488. //
  489. if (iLength > 0)
  490. {
  491. if (zoomed)
  492. {
  493. // if new fails just skip it
  494. DBG_SAVE_FILE_LINE
  495. tabArray = new INT[iLength+1];
  496. if( tabArray == NULL )
  497. {
  498. ERROR_OUT(("Failed to allocate tabArray"));
  499. continue;
  500. }
  501. // We are zoomed. Must calculate char spacings
  502. // ourselfs so that they end up proportionally
  503. // in the right places. TabbedTextOut will not
  504. // do this right so we have to use ExtTextOut with
  505. // a tab array.
  506. // figure out tab array
  507. j = 0;
  508. nLastTab = 0;
  509. for (i=0; i < iLength; i++)
  510. {
  511. ch = strLine[(int)i]; //Don't worry about DBCS here...
  512. abc = GetTextABC(strLine, 0, i);
  513. if( j > 0 )
  514. tabArray[j-1] = abc.abcB - nLastTab;
  515. nLastTab = abc.abcB;
  516. j++;
  517. }
  518. // Now, strip out any tab chars so they don't interact
  519. // in an obnoxious manner with the tab array we just
  520. // made and so they don't make ugly little
  521. // blocks when they are drawn.
  522. for (i=0; i < iLength; i++)
  523. {
  524. ch = strLine[(int)i];
  525. if ((dbcsEnabled) && (IsDBCSLeadByte((BYTE)ch)))
  526. i++;
  527. else
  528. if(strLine[(int)i] == '\t')
  529. strLine[i] = ' '; // blow off tab, tab array
  530. // will compensate for this
  531. }
  532. // do it
  533. ::ExtTextOut(hDC, pointPos.x,
  534. pointPos.y,
  535. 0,
  536. NULL,
  537. strLine,
  538. iLength,
  539. tabArray);
  540. delete tabArray;
  541. }
  542. else
  543. {
  544. POINT ptPos;
  545. GetAnchorPoint(&ptPos);
  546. // Not zoomed, just do it
  547. ::TabbedTextOut(hDC, pointPos.x,
  548. pointPos.y,
  549. strLine,
  550. iLength,
  551. 0,
  552. NULL,
  553. ptPos.x);
  554. }
  555. }
  556. //
  557. // Move to the next line.
  558. //
  559. ASSERT(m_textMetrics.tmHeight != DBG_UNINIT);
  560. pointPos.y += (m_textMetrics.tmHeight);
  561. }
  562. //
  563. // Do NOT draw focus if clipboard or printing
  564. //
  565. if (WasSelectedLocally() && (hDC == g_pDraw->m_hDCCached))
  566. {
  567. DrawRect();
  568. }
  569. //
  570. // Restore the old background mode.
  571. //
  572. ::SetBkMode(hDC, oldBkMode);
  573. g_pDraw->UnPrimeFont(hDC);
  574. //
  575. // If we are drawing on top of a remote pointer, draw it.
  576. //
  577. BitmapObj* remotePointer = NULL;
  578. WBPOSITION pos = NULL;
  579. remotePointer = g_pCurrentWorkspace->RectHitRemotePointer(&m_rect, GetPenThickness()/2, NULL);
  580. while(remotePointer)
  581. {
  582. remotePointer->Draw();
  583. remotePointer = g_pCurrentWorkspace->RectHitRemotePointer(&m_rect, GetPenThickness()/2, remotePointer->GetMyPosition());
  584. }
  585. }
  586. void TextObj::SetPenColor(COLORREF rgb, BOOL isPresent)
  587. {
  588. ChangedPenColor();
  589. m_bIsPenColorPresent = isPresent;
  590. if(!isPresent)
  591. {
  592. return;
  593. }
  594. m_penColor.rgbtRed = GetRValue(rgb);
  595. m_penColor.rgbtGreen = GetGValue(rgb);
  596. m_penColor.rgbtBlue = GetBValue(rgb);
  597. }
  598. BOOL TextObj::GetPenColor(COLORREF * rgb)
  599. {
  600. if(m_bIsPenColorPresent)
  601. {
  602. *rgb = RGB(m_penColor.rgbtRed, m_penColor.rgbtGreen, m_penColor.rgbtBlue);
  603. }
  604. return m_bIsPenColorPresent;
  605. }
  606. BOOL TextObj::GetPenColor(RGBTRIPLE* rgb)
  607. {
  608. if(m_bIsPenColorPresent)
  609. {
  610. *rgb = m_penColor;
  611. }
  612. return m_bIsPenColorPresent;
  613. }
  614. void TextObj::SetFillColor(COLORREF rgb, BOOL isPresent)
  615. {
  616. ChangedFillColor();
  617. m_bIsFillColorPresent = isPresent;
  618. if(!isPresent)
  619. {
  620. return;
  621. }
  622. m_fillColor.rgbtRed = GetRValue(rgb);
  623. m_fillColor.rgbtGreen = GetGValue(rgb);
  624. m_fillColor.rgbtBlue = GetBValue(rgb);
  625. }
  626. BOOL TextObj::GetFillColor(COLORREF* rgb)
  627. {
  628. if(m_bIsFillColorPresent && rgb !=NULL)
  629. {
  630. *rgb = RGB(m_fillColor.rgbtRed, m_fillColor.rgbtGreen, m_fillColor.rgbtBlue);
  631. }
  632. return m_bIsFillColorPresent;
  633. }
  634. BOOL TextObj::GetFillColor(RGBTRIPLE* rgb)
  635. {
  636. if(m_bIsFillColorPresent && rgb!= NULL)
  637. {
  638. *rgb = m_fillColor;
  639. }
  640. return m_bIsFillColorPresent;
  641. }
  642. //
  643. // Get the encoded buffer for Drawing Create PDU
  644. //
  645. void TextObj::GetEncodedCreatePDU(ASN1_BUF *pBuf)
  646. {
  647. SIPDU *sipdu = NULL;
  648. DBG_SAVE_FILE_LINE
  649. sipdu = (SIPDU *) new BYTE[sizeof(SIPDU)];
  650. if(sipdu)
  651. {
  652. sipdu->choice = siNonStandardPDU_chosen;
  653. CreateNonStandardPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction, NonStandardTextID);
  654. CreateTextPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction.data, textCreatePDU_chosen);
  655. ((MSTextPDU *)sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value)->header.nonStandardPDU = textCreatePDU_chosen;
  656. ASN1_BUF encodedPDU;
  657. g_pCoder->Encode(sipdu, pBuf);
  658. if(sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value)
  659. {
  660. delete sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value;
  661. }
  662. delete sipdu;
  663. }
  664. else
  665. {
  666. TRACE_MSG(("Failed to create penMenu"));
  667. ::PostMessage(g_pMain->m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
  668. }
  669. }
  670. void TextObj::SendTextPDU(UINT choice)
  671. {
  672. if(!g_pNMWBOBJ->CanDoText())
  673. {
  674. return;
  675. }
  676. SIPDU *sipdu = NULL;
  677. DBG_SAVE_FILE_LINE
  678. sipdu = (SIPDU *) new BYTE[sizeof(SIPDU)];
  679. if(sipdu)
  680. {
  681. sipdu->choice = siNonStandardPDU_chosen;
  682. CreateNonStandardPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction, NonStandardTextID);
  683. CreateTextPDU(&sipdu->u.siNonStandardPDU.nonStandardTransaction.data, choice);
  684. if(sipdu->u.siNonStandardPDU.nonStandardTransaction.data.value == NULL)
  685. {
  686. return;
  687. }
  688. T120Error rc = SendT126PDU(sipdu);
  689. if(rc == T120_NO_ERROR)
  690. {
  691. ResetAttrib();
  692. SIPDUCleanUp(sipdu);
  693. }
  694. }
  695. else
  696. {
  697. TRACE_MSG(("Failed to create sipdu"));
  698. ::PostMessage(g_pMain->m_hwnd, WM_USER_DISPLAY_ERROR, WBFE_RC_WINDOWS, 0);
  699. }
  700. }
  701. //
  702. // UI Created a new Drawing Object
  703. //
  704. void TextObj::SendNewObjectToT126Apps(void)
  705. {
  706. SendTextPDU(textCreatePDU_chosen);
  707. }
  708. //
  709. // UI Edited the Drawing Object
  710. //
  711. void TextObj::OnObjectEdit(void)
  712. {
  713. g_bContentsChanged = TRUE;
  714. SendTextPDU(textEditPDU_chosen);
  715. }
  716. //
  717. // UI Deleted the Drawing Object
  718. //
  719. void TextObj::OnObjectDelete(void)
  720. {
  721. g_bContentsChanged = TRUE;
  722. SendTextPDU(textDeletePDU_chosen);
  723. }
  724. //
  725. //
  726. // Function: TextObj::SetFont
  727. //
  728. // Purpose: Set the font to be used for drawing
  729. //
  730. //
  731. void TextObj::SetFont(HFONT hFont)
  732. {
  733. MLZ_EntryOut(ZONE_FUNCTION, "TextObj::SetFont");
  734. // Get the font details
  735. LOGFONT lfont;
  736. ::GetObject(hFont, sizeof(LOGFONT), &lfont);
  737. //
  738. // Pass the logical font into the SetFont() function
  739. //
  740. SetFont(&lfont);
  741. }
  742. //
  743. //
  744. // Function: TextObj::SetText
  745. //
  746. // Purpose: Set the text of the object
  747. //
  748. //
  749. void TextObj::SetText(TCHAR * strText)
  750. {
  751. // Remove all the current stored text
  752. strTextArray.SetSize(0);
  753. // Scan the text for carriage return and new-line characters
  754. int iNext = 0;
  755. int iLast = 0;
  756. int textSize = lstrlen(strText);
  757. TCHAR savedChar[1];
  758. //
  759. // In this case, we don't know how many lines there will be. So we
  760. // use Add() from the StrArray class.
  761. //
  762. while (iNext < textSize)
  763. {
  764. // Find the next carriage return or line feed
  765. iNext += StrCspn(strText + iNext, "\r\n");
  766. // Extract the text before the terminator
  767. // and add it to the current list of text lines.
  768. savedChar[0] = strText[iNext];
  769. strText[iNext] = 0;
  770. strTextArray.Add((strText+iLast));
  771. strText[iNext] = savedChar[0];
  772. if (iNext < textSize)
  773. {
  774. // Skip the carriage return
  775. if (strText[iNext] == '\r')
  776. iNext++;
  777. // Skip a following new line (if there is one)
  778. if (strText[iNext] == '\n')
  779. iNext++;
  780. // Update the index of the start of the next line
  781. iLast = iNext;
  782. }
  783. }
  784. if(textSize)
  785. {
  786. // Calculate the bounding rectangle for the new text
  787. CalculateBoundsRect();
  788. ChangedText();
  789. }
  790. }
  791. //
  792. //
  793. // Function: TextObj::SetText
  794. //
  795. // Purpose: Set the text of the object
  796. //
  797. //
  798. void TextObj::SetText(const StrArray& _strTextArray)
  799. {
  800. // Scan the text for carriage return and new-line characters
  801. int iSize = _strTextArray.GetSize();
  802. //
  803. // In this case we know how many lines, so set that # then use SetAt()
  804. // to stick text there.
  805. //
  806. strTextArray.RemoveAll();
  807. strTextArray.SetSize(iSize);
  808. int iNext = 0;
  809. for ( ; iNext < iSize; iNext++)
  810. {
  811. strTextArray.SetAt(iNext, _strTextArray[iNext]);
  812. }
  813. // Calculate the new bounding rectangle
  814. CalculateBoundsRect();
  815. }
  816. //
  817. //
  818. // Function: TextObj::SetFont(metrics)
  819. //
  820. // Purpose: Set the font to be used for drawing
  821. //
  822. //
  823. void TextObj::SetFont(LOGFONT *pLogFont, BOOL bReCalc )
  824. {
  825. HFONT hNewFont;
  826. MLZ_EntryOut(ZONE_FUNCTION, "TextObj::SetFont");
  827. // Ensure that the font can be resized by the zoom function
  828. // (proof quality prevents font scaling).
  829. pLogFont->lfQuality = DRAFT_QUALITY;
  830. //zap FontAssociation mode (bug 3258)
  831. pLogFont->lfClipPrecision |= CLIP_DFA_OVERRIDE;
  832. // Always work in cell coordinates to get scaling right
  833. TRACE_MSG(("Setting font height %d, width %d, face %s, family %d, precis %d",
  834. pLogFont->lfHeight,pLogFont->lfWidth,pLogFont->lfFaceName,
  835. pLogFont->lfPitchAndFamily, pLogFont->lfOutPrecision));
  836. hNewFont = ::CreateFontIndirect(pLogFont);
  837. if (!hNewFont)
  838. {
  839. // Could not create the font
  840. ERROR_OUT(("Failed to create font"));
  841. DefaultExceptionHandler(WBFE_RC_WINDOWS, 0);
  842. return;
  843. }
  844. // We are now guaranteed to be able to delete the old font
  845. if (m_hFont != NULL)
  846. {
  847. DeleteFont(m_hFont);
  848. }
  849. m_hFont = hNewFont;
  850. // Calculate the line height for this font
  851. ASSERT(g_pDraw);
  852. g_pDraw->PrimeFont(g_pDraw->GetCachedDC(), m_hFont, &m_textMetrics);
  853. // Set up the thumbnail font, forcing truetype if not currently TT
  854. if (!(m_textMetrics.tmPitchAndFamily & TMPF_TRUETYPE))
  855. {
  856. pLogFont->lfFaceName[0] = 0;
  857. pLogFont->lfOutPrecision = OUT_TT_PRECIS;
  858. TRACE_MSG(("Non-True type font"));
  859. }
  860. if (m_hFontThumb != NULL)
  861. {
  862. ::DeleteFont(m_hFontThumb);
  863. m_hFontThumb = NULL;
  864. }
  865. m_hFontThumb = ::CreateFontIndirect(pLogFont);
  866. if (!m_hFontThumb)
  867. {
  868. // Could not create the font
  869. ERROR_OUT(("Failed to create thumbnail font"));
  870. }
  871. // Calculate the bounding rectangle, accounting for the new font
  872. if( bReCalc )
  873. CalculateBoundsRect();
  874. ChangedFont();
  875. g_pDraw->UnPrimeFont(g_pDraw->m_hDCCached);
  876. }
  877. //
  878. //
  879. // Function: TextObj::CalculateRect
  880. //
  881. // Purpose: Calculate the bounding rectangle of a portion of the object
  882. //
  883. //
  884. void TextObj::CalculateRect(int iStartX,
  885. int iStartY,
  886. int iStopX,
  887. int iStopY,
  888. LPRECT lprcResult)
  889. {
  890. RECT rcResult;
  891. RECT rcT;
  892. int iIndex;
  893. MLZ_EntryOut(ZONE_FUNCTION, "TextObj::CalculateRect");
  894. //
  895. // NOTE:
  896. // We must use an intermediate rectangle, so as not to disturb the
  897. // contents of the passed-in one until done. lprcResult may be pointing
  898. // to the current bounds rect, and we call functions from here that
  899. // may need its current value.
  900. //
  901. // Initialize the result rectangle
  902. ::SetRectEmpty(&rcResult);
  903. if (!strTextArray.GetSize())
  904. {
  905. // Text is empty
  906. goto DoneCalc;
  907. }
  908. // Allow for special limit values and ensure that the start and stop
  909. // character positions are in range.
  910. if (iStopY == LAST_LINE)
  911. {
  912. iStopY = strTextArray.GetSize() - 1;
  913. }
  914. iStopY = min(iStopY, strTextArray.GetSize() - 1);
  915. iStopY = max(iStopY, 0);
  916. if (iStopX == LAST_CHAR)
  917. {
  918. iStopX = lstrlen(strTextArray[iStopY]);
  919. }
  920. iStopX = min(iStopX, lstrlen(strTextArray[iStopY]));
  921. iStopX = max(iStopX, 0);
  922. // Loop through the text strings, adding each to the rectangle
  923. for (iIndex = iStartY; iIndex <= iStopY; iIndex++)
  924. {
  925. int iLeftX = ((iIndex == iStartY) ? iStartX : 0);
  926. int iRightX = ((iIndex == iStopY)
  927. ? iStopX : lstrlen(strTextArray[iIndex]));
  928. GetTextRectangle(iIndex, iLeftX, iRightX, &rcT);
  929. ::UnionRect(&rcResult, &rcResult, &rcT);
  930. }
  931. DoneCalc:
  932. *lprcResult = rcResult;
  933. }
  934. //
  935. //
  936. // Function: TextObj::CalculateBoundsRect
  937. //
  938. // Purpose: Calculate the bounding rectangle of the object
  939. //
  940. //
  941. void TextObj::CalculateBoundsRect(void)
  942. {
  943. // Set the new bounding rectangle
  944. CalculateRect(0, 0, LAST_CHAR, LAST_LINE, &m_rect);
  945. }
  946. //
  947. //
  948. // Function: TextObj::GetTextABC
  949. //
  950. // Purpose: Calculate the ABC numbers for a string of text
  951. //
  952. // COMMENT BY RAND: The abc returned is for the whole string, not just one
  953. // char. I.e, ABC.abcA is the offset to the first glyph in
  954. // the string, ABC.abcB is the sum of all of the glyphs and
  955. // ABC.abcC is the trailing space after the last glyph.
  956. // ABC.abcA + ABC.abcB + ABC.abcC is the total rendered
  957. // length including overhangs.
  958. //
  959. // Note - we never use the A spacing so it is always 0
  960. //
  961. ABC TextObj::GetTextABC( LPCTSTR pText,
  962. int iStartX,
  963. int iStopX)
  964. {
  965. MLZ_EntryOut(ZONE_FUNCTION, "TextObj::GetTextABC");
  966. ABC abcResult;
  967. HDC hDC;
  968. BOOL rc = FALSE;
  969. ABC abcFirst;
  970. ABC abcLast;
  971. BOOL zoomed = g_pDraw->Zoomed();
  972. int nCharLast;
  973. int i;
  974. LPCTSTR pScanStr;
  975. ZeroMemory( (PVOID)&abcResult, sizeof abcResult );
  976. ZeroMemory( (PVOID)&abcFirst, sizeof abcFirst );
  977. ZeroMemory( (PVOID)&abcLast, sizeof abcLast );
  978. // Get the standard size measure of the text
  979. LPCTSTR pABC = (pText + iStartX);
  980. int pABCLength = iStopX - iStartX;
  981. hDC = g_pDraw->GetCachedDC();
  982. g_pDraw->PrimeFont(hDC, m_hFont, &m_textMetrics);
  983. //
  984. // We must temporarily unzoom if we are currently zoomed since the
  985. // weird Windows font handling will not give us the same answer for
  986. // the text extent in zoomed mode for some TrueType fonts
  987. //
  988. if (zoomed)
  989. {
  990. ::ScaleViewportExtEx(hDC, 1, g_pDraw->ZoomFactor(), 1, g_pDraw->ZoomFactor(), NULL);
  991. }
  992. DWORD size = ::GetTabbedTextExtent(hDC, pABC, pABCLength, 0, NULL);
  993. // We now have the advance width of the text
  994. abcResult.abcB = LOWORD(size);
  995. TRACE_MSG(("Basic text width is %d",abcResult.abcB));
  996. // Allow for C space (or overhang)
  997. if (iStopX > iStartX)
  998. {
  999. if (m_textMetrics.tmPitchAndFamily & TMPF_TRUETYPE)
  1000. {
  1001. if(GetSystemMetrics( SM_DBCSENABLED ))
  1002. {
  1003. // have to handle DBCS on both ends
  1004. if( IsDBCSLeadByte( (BYTE)pABC[0] ) )
  1005. {
  1006. // pack multi byte char into a WORD for GetCharABCWidths
  1007. WORD wMultiChar = MAKEWORD( pABC[1], pABC[0] );
  1008. rc = ::GetCharABCWidths(hDC, wMultiChar, wMultiChar, &abcFirst);
  1009. }
  1010. else
  1011. {
  1012. // first char is SBCS
  1013. rc = ::GetCharABCWidths(hDC, pABC[0], pABC[0], &abcFirst );
  1014. }
  1015. // Check for DBCS as last char. Have to scan whole string to be sure
  1016. pScanStr = pABC;
  1017. nCharLast = 0;
  1018. for( i=0; i<pABCLength; i++, pScanStr++ )
  1019. {
  1020. nCharLast = i;
  1021. if( IsDBCSLeadByte( (BYTE)*pScanStr ) )
  1022. {
  1023. i++;
  1024. pScanStr++;
  1025. }
  1026. }
  1027. if( IsDBCSLeadByte( (BYTE)pABC[nCharLast] ) )
  1028. {
  1029. // pack multi byte char into a WORD for GetCharABCWidths
  1030. ASSERT( (nCharLast+1) < pABCLength );
  1031. WORD wMultiChar = MAKEWORD( pABC[nCharLast+1], pABC[nCharLast] );
  1032. rc = ::GetCharABCWidths(hDC, wMultiChar, wMultiChar, &abcLast);
  1033. }
  1034. else
  1035. {
  1036. // last char is SBCS
  1037. rc = ::GetCharABCWidths(hDC, pABC[nCharLast], pABC[nCharLast], &abcLast );
  1038. }
  1039. }
  1040. else
  1041. {
  1042. // SBCS, no special fiddling, just call GetCharABCWidths()
  1043. rc = ::GetCharABCWidths(hDC, pABC[0], pABC[0], &abcFirst );
  1044. nCharLast = pABCLength-1;
  1045. rc = rc && ::GetCharABCWidths(hDC, pABC[nCharLast], pABC[nCharLast], &abcLast );
  1046. }
  1047. TRACE_MSG(("abcFirst: rc=%d, a=%d, b=%d, c=%d",
  1048. rc, abcFirst.abcA, abcFirst.abcB, abcFirst.abcC) );
  1049. TRACE_MSG(("abcLast: rc=%d, a=%d, b=%d, c=%d",
  1050. rc, abcLast.abcA, abcLast.abcB, abcLast.abcC) );
  1051. }
  1052. if( rc )
  1053. {
  1054. // The text was trutype and we got good abcwidths
  1055. // Give the C space of the last characters from
  1056. // the string as the C space of the text.
  1057. abcResult.abcA = abcFirst.abcA;
  1058. abcResult.abcC = abcLast.abcC;
  1059. }
  1060. else
  1061. {
  1062. //
  1063. // Mock up C value for a non TT font by taking some of overhang as
  1064. // the negative C value.
  1065. //
  1066. //TRACE_MSG(("Using overhang -%d as C space",m_textMetrics.tmOverhang/2));
  1067. // Adjust B by -overhang to make update rect schoot
  1068. // far enough to the left so that the toes of italic cap A's
  1069. // don't get clipped. Ignore comment above.
  1070. abcResult.abcB -= m_textMetrics.tmOverhang;
  1071. }
  1072. }
  1073. //
  1074. // If we temporarily unzoomed then restore it now
  1075. //
  1076. if (zoomed)
  1077. {
  1078. ::ScaleViewportExtEx(hDC, g_pDraw->ZoomFactor(), 1, g_pDraw->ZoomFactor(), 1, NULL);
  1079. }
  1080. TRACE_MSG(("Final text width is %d, C space %d",abcResult.abcB,abcResult.abcC));
  1081. return abcResult;
  1082. }
  1083. //
  1084. //
  1085. // Function: TextObj::GetTextRectangle
  1086. //
  1087. // Purpose: Calculate the bounding rectangle of a portion of the object
  1088. //
  1089. //
  1090. void TextObj::GetTextRectangle(int iStartY,
  1091. int iStartX,
  1092. int iStopX,
  1093. LPRECT lprc)
  1094. {
  1095. // ABC structures for text sizing
  1096. ABC abcText1;
  1097. ABC abcText2;
  1098. int iLeftOffset = 0;
  1099. MLZ_EntryOut(ZONE_FUNCTION, "TextObj::GetTextRectangle");
  1100. ASSERT(iStartY < strTextArray.GetSize());
  1101. // Here we calculate the width of the text glyphs in which we
  1102. // are interested. In case there are tabs involved we must start
  1103. // with position 0 and get two lengths then subtract them
  1104. abcText1 = GetTextABC(strTextArray[iStartY], 0, iStopX);
  1105. if (iStartX > 0)
  1106. {
  1107. // The third param used to be iStartX-1 which is WRONG. It
  1108. // has to point to the first char pos past the string
  1109. // we are using.
  1110. abcText2 = GetTextABC(strTextArray[iStartY], 0, iStartX);
  1111. // Just use B part for offset. Adding A snd/or C to it moves the update
  1112. // rectangle too far to the right and clips the char
  1113. iLeftOffset = abcText2.abcB;
  1114. }
  1115. else
  1116. {
  1117. ZeroMemory( &abcText2, sizeof abcText2 );
  1118. }
  1119. //
  1120. // We need to allow for A and C space in the bounding rectangle. Use
  1121. // ABS function just to make sure we get a large enough rectangle.
  1122. //
  1123. // Move A and C from original offset calc to here for width of update
  1124. // rectangle. Add in tmOverhang (non zero for non-tt fonts) to compensate
  1125. // for the kludge in GetTextABC()....THIS EDITBOX CODE HAS GOT TO GO...
  1126. abcText1.abcB = abcText1.abcB - iLeftOffset +
  1127. abs(abcText2.abcA) + abs(abcText2.abcC) +
  1128. abs(abcText1.abcA) + abs(abcText1.abcC) +
  1129. m_textMetrics.tmOverhang;
  1130. TRACE_DEBUG(("Left offset %d",iLeftOffset));
  1131. TRACE_DEBUG(("B width now %d",abcText1.abcB));
  1132. // Build the result rectangle.
  1133. // Note that we never return an empty rectangle. This allows for the
  1134. // fact that the Windows rectangle functions will ignore empty
  1135. // rectangles completely. This would cause the bounding rectangle
  1136. // calculation (for instance) to go wrong if the top or bottom lines
  1137. // in a text object were empty.
  1138. ASSERT(m_textMetrics.tmHeight != DBG_UNINIT);
  1139. int iLineHeight = m_textMetrics.tmHeight + m_textMetrics.tmExternalLeading;
  1140. lprc->left = 0;
  1141. lprc->top = 0;
  1142. lprc->right = max(1, abcText1.abcB);
  1143. lprc->bottom = iLineHeight;
  1144. ::OffsetRect(lprc, iLeftOffset, iLineHeight * iStartY);
  1145. // rect is the correct width at this point but it might need to be schooted to
  1146. // the left a bit to allow for kerning of 1st letter (bug 469)
  1147. if( abcText1.abcA < 0 )
  1148. {
  1149. ::OffsetRect(lprc, abcText1.abcA, 0);
  1150. m_nKerningOffset = -abcText1.abcA;
  1151. }
  1152. else
  1153. m_nKerningOffset = 0;
  1154. POINT pt;
  1155. GetAnchorPoint(&pt);
  1156. ::OffsetRect(lprc, pt.x, pt.y);
  1157. }