Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / xfa / src / fdp / src / tto / fde_textout.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../../foxitlib.h"\r
8 #include "fde_textout.h"\r
9 IFDE_TextOut* IFDE_TextOut::Create()\r
10 {\r
11     return FDE_New CFDE_TextOut;\r
12 }\r
13 CFDE_TextOut::CFDE_TextOut()\r
14     : m_pFont(NULL)\r
15     , m_fFontSize(12.0f)\r
16     , m_fLineSpace(m_fFontSize)\r
17     , m_fLinePos(0.0f)\r
18     , m_fTolerance(0.0f)\r
19     , m_iAlignment(0)\r
20     , m_iTxtBkAlignment(0)\r
21     , m_pCharWidths(NULL)\r
22     , m_iChars(0)\r
23     , m_pEllCharWidths(NULL)\r
24     , m_iEllChars(0)\r
25     , m_wParagraphBkChar(L'\n')\r
26     , m_TxtColor(0xFF000000)\r
27     , m_dwStyles(0)\r
28     , m_dwTxtBkStyles(0)\r
29     , m_bElliChanged(FALSE)\r
30     , m_iEllipsisWidth(0)\r
31     , m_ttoLines(5)\r
32     , m_iCurLine(0)\r
33     , m_iTotalLines(0)\r
34     , m_iCurPiece(0)\r
35     , m_pCharPos(NULL)\r
36     , m_iCharPosSize(0)\r
37     , m_pRenderDevice(NULL)\r
38 {\r
39     m_pTxtBreak = IFX_TxtBreak::Create(FX_TXTBREAKPOLICY_None);\r
40     FXSYS_assert(m_pTxtBreak != NULL);\r
41     m_Matrix.Reset();\r
42     m_rtClip.Reset();\r
43     m_rtLogicClip.Reset();\r
44 }\r
45 CFDE_TextOut::~CFDE_TextOut()\r
46 {\r
47     if (m_pTxtBreak != NULL) {\r
48         m_pTxtBreak->Release();\r
49     }\r
50     if (m_pCharWidths != NULL) {\r
51         FDE_Free(m_pCharWidths);\r
52     }\r
53     if (m_pEllCharWidths != NULL) {\r
54         FDE_Free(m_pEllCharWidths);\r
55     }\r
56     if (m_pRenderDevice != NULL) {\r
57         m_pRenderDevice->Release();\r
58     }\r
59     if (m_pCharPos != NULL) {\r
60         FDE_Free(m_pCharPos);\r
61     }\r
62     m_ttoLines.RemoveAll();\r
63 }\r
64 void CFDE_TextOut::SetFont(IFX_Font *pFont)\r
65 {\r
66     FXSYS_assert(pFont != NULL);\r
67     m_pFont = pFont;\r
68     m_pTxtBreak->SetFont(pFont);\r
69 }\r
70 void CFDE_TextOut::SetFontSize(FX_FLOAT fFontSize)\r
71 {\r
72     FXSYS_assert(fFontSize > 0);\r
73     m_fFontSize = fFontSize;\r
74     m_pTxtBreak->SetFontSize(fFontSize);\r
75 }\r
76 void CFDE_TextOut::SetTextColor(FX_ARGB color)\r
77 {\r
78     m_TxtColor = color;\r
79 }\r
80 void CFDE_TextOut::SetStyles(FX_DWORD dwStyles)\r
81 {\r
82     m_dwStyles = dwStyles;\r
83     m_dwTxtBkStyles = 0;\r
84     if (dwStyles & FDE_TTOSTYLE_SingleLine) {\r
85         m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_SingleLine;\r
86     }\r
87     if (dwStyles & FDE_TTOSTYLE_ExpandTab) {\r
88         m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ExpandTab;\r
89     }\r
90     if (dwStyles & FDE_TTOSTYLE_ArabicShapes) {\r
91         m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicShapes;\r
92     }\r
93     if (dwStyles & FDE_TTOSTYLE_RTL) {\r
94         m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_RTLReadingOrder;\r
95     }\r
96     if (dwStyles & FDE_TTOSTYLE_ArabicContext) {\r
97         m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicContext;\r
98     }\r
99     if (dwStyles & FDE_TTOSTYLE_VerticalLayout) {\r
100         m_dwTxtBkStyles |= (FX_TXTLAYOUTSTYLE_VerticalChars | FX_TXTLAYOUTSTYLE_VerticalLayout);\r
101     }\r
102     m_pTxtBreak->SetLayoutStyles(m_dwTxtBkStyles);\r
103 }\r
104 void CFDE_TextOut::SetTabWidth(FX_FLOAT fTabWidth)\r
105 {\r
106     FXSYS_assert(fTabWidth > 1.0f);\r
107     m_pTxtBreak->SetTabWidth(fTabWidth, FALSE);\r
108 }\r
109 void CFDE_TextOut::SetEllipsisString(const CFX_WideString &wsEllipsis)\r
110 {\r
111     m_bElliChanged      = TRUE;\r
112     m_wsEllipsis        = wsEllipsis;\r
113 }\r
114 void CFDE_TextOut::SetParagraphBreakChar(FX_WCHAR wch)\r
115 {\r
116     m_wParagraphBkChar = wch;\r
117     m_pTxtBreak->SetParagraphBreakChar(wch);\r
118 }\r
119 void CFDE_TextOut::SetAlignment(FX_INT32 iAlignment)\r
120 {\r
121     m_iAlignment = iAlignment;\r
122     switch (m_iAlignment) {\r
123         case FDE_TTOALIGNMENT_TopCenter:\r
124         case FDE_TTOALIGNMENT_Center:\r
125         case FDE_TTOALIGNMENT_BottomCenter:\r
126             m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Center;\r
127             break;\r
128         case FDE_TTOALIGNMENT_TopRight:\r
129         case FDE_TTOALIGNMENT_CenterRight:\r
130         case FDE_TTOALIGNMENT_BottomRight:\r
131             m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Right;\r
132             break;\r
133         default:\r
134             m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Left;\r
135             break;\r
136     }\r
137     m_pTxtBreak->SetAlignment(m_iTxtBkAlignment);\r
138 }\r
139 void CFDE_TextOut::SetLineSpace(FX_FLOAT fLineSpace)\r
140 {\r
141     FXSYS_assert(fLineSpace > 1.0f);\r
142     m_fLineSpace = fLineSpace;\r
143 }\r
144 void CFDE_TextOut::SetDIBitmap(CFX_DIBitmap *pDIB)\r
145 {\r
146     FXSYS_assert(pDIB != NULL);\r
147     if (m_pRenderDevice != NULL) {\r
148         m_pRenderDevice->Release();\r
149     }\r
150     m_pRenderDevice = IFDE_RenderDevice::Create(pDIB);\r
151 }\r
152 void CFDE_TextOut::SetRenderDevice(CFX_RenderDevice *pDevice)\r
153 {\r
154     FXSYS_assert(pDevice != NULL);\r
155     if (m_pRenderDevice != NULL) {\r
156         m_pRenderDevice->Release();\r
157     }\r
158     m_pRenderDevice = IFDE_RenderDevice::Create(pDevice);\r
159 }\r
160 void CFDE_TextOut::SetClipRect(const CFX_Rect &rtClip)\r
161 {\r
162     m_rtClip.Set((FX_FLOAT)rtClip.left, (FX_FLOAT)rtClip.top, (FX_FLOAT)rtClip.Width(), (FX_FLOAT)rtClip.Height());\r
163 }\r
164 void CFDE_TextOut::SetClipRect(const CFX_RectF &rtClip)\r
165 {\r
166     m_rtClip = rtClip;\r
167 }\r
168 void CFDE_TextOut::SetLogicClipRect(const CFX_RectF &rtClip)\r
169 {\r
170     m_rtLogicClip = rtClip;\r
171 }\r
172 void CFDE_TextOut::SetMatrix(const CFX_Matrix &matrix)\r
173 {\r
174     m_Matrix = matrix;\r
175 }\r
176 void CFDE_TextOut::SetLineBreakTolerance(FX_FLOAT fTolerance)\r
177 {\r
178     m_fTolerance = fTolerance;\r
179     m_pTxtBreak->SetLineBreakTolerance(m_fTolerance);\r
180 }\r
181 FX_INT32 CFDE_TextOut::GetTotalLines()\r
182 {\r
183     return m_iTotalLines;\r
184 }\r
185 void CFDE_TextOut::CalcSize(FX_LPCWSTR pwsStr, FX_INT32 iLength, CFX_Size &size)\r
186 {\r
187     CFX_RectF rtText;\r
188     rtText.Set(0.0f, 0.0f, (FX_FLOAT)size.x, (FX_FLOAT)size.y);\r
189     CalcSize(pwsStr, iLength, rtText);\r
190     size.x = (FX_INT32)rtText.Width();\r
191     size.y = (FX_INT32)rtText.Height();\r
192 }\r
193 void CFDE_TextOut::CalcSize(FX_LPCWSTR pwsStr, FX_INT32 iLength, CFX_SizeF &size)\r
194 {\r
195     CFX_RectF rtText;\r
196     rtText.Set(0.0f, 0.0f, size.x, size.y);\r
197     CalcSize(pwsStr, iLength, rtText);\r
198     size.x = rtText.Width();\r
199     size.y = rtText.Height();\r
200 }\r
201 void CFDE_TextOut::CalcSize(FX_LPCWSTR pwsStr, FX_INT32 iLength, CFX_Rect &rect)\r
202 {\r
203     CFX_RectF rtText;\r
204     rtText.Set((FX_FLOAT)rect.left, (FX_FLOAT)rect.top, (FX_FLOAT)rect.Width(), (FX_FLOAT)rect.Height());\r
205     CalcSize(pwsStr, iLength, rtText);\r
206     rect.Set((FX_INT32)rtText.left, (FX_INT32)rtText.top, (FX_INT32)rtText.Width(), (FX_INT32)rtText.Height());\r
207 }\r
208 void CFDE_TextOut::CalcSize(FX_LPCWSTR pwsStr, FX_INT32 iLength, CFX_RectF &rect)\r
209 {\r
210     if (pwsStr == NULL || iLength < 1) {\r
211         rect.width      = 0.0f;\r
212         rect.height     = 0.0f;\r
213     } else {\r
214         CFX_Matrix rm;\r
215         rm.SetReverse(m_Matrix);\r
216         rm.TransformRect(rect);\r
217         CalcTextSize(pwsStr, iLength, rect);\r
218         m_Matrix.TransformRect(rect);\r
219     }\r
220 }\r
221 void CFDE_TextOut::CalcLogicSize(FX_LPCWSTR pwsStr, FX_INT32 iLength, CFX_SizeF &size)\r
222 {\r
223     CFX_RectF rtText;\r
224     rtText.Set(0.0f, 0.0f, size.x, size.y);\r
225     CalcLogicSize(pwsStr, iLength, rtText);\r
226     size.x = rtText.Width();\r
227     size.y = rtText.Height();\r
228 }\r
229 void CFDE_TextOut::CalcLogicSize(FX_LPCWSTR pwsStr, FX_INT32 iLength, CFX_RectF &rect)\r
230 {\r
231     if (pwsStr == NULL || iLength < 1) {\r
232         rect.width      = 0.0f;\r
233         rect.height = 0.0f;\r
234     } else {\r
235         CalcTextSize(pwsStr, iLength, rect);\r
236     }\r
237 }\r
238 void CFDE_TextOut::CalcTextSize(FX_LPCWSTR pwsStr, FX_INT32 iLength, CFX_RectF &rect)\r
239 {\r
240     FXSYS_assert(m_pFont != NULL && m_fFontSize >= 1.0f);\r
241     SetLineWidth(rect);\r
242     m_iTotalLines = 0;\r
243     FX_LPCWSTR pStr             = pwsStr;\r
244     FX_BOOL     bHotKey         = (m_dwStyles & FDE_TTOSTYLE_HotKey) ? TRUE : FALSE;\r
245     FX_BOOL bVertical   = (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) ? TRUE : FALSE;\r
246     FX_FLOAT fWidth             = 0.0f;\r
247     FX_FLOAT fHeight    = 0.0f;\r
248     FX_FLOAT fStartPos  = bVertical ? rect.bottom() : rect.right();\r
249     FX_DWORD dwBreakStatus = 0;\r
250     FX_WCHAR wPreChar   = 0;\r
251     FX_WCHAR wch;\r
252     FX_WCHAR wBreak = 0;\r
253     while (iLength-- > 0) {\r
254         wch = *pStr++;\r
255         if (wBreak == 0 && (wch == L'\n' || wch == L'\r')) {\r
256             wBreak = wch;\r
257             m_pTxtBreak->SetParagraphBreakChar(wch);\r
258         }\r
259         if (bHotKey && wch == L'&' && wPreChar != L'&') {\r
260             wPreChar = wch;\r
261             continue;\r
262         }\r
263         dwBreakStatus = m_pTxtBreak->AppendChar(wch);\r
264         if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {\r
265             RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight);\r
266         }\r
267         wPreChar = 0;\r
268     }\r
269     dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);\r
270     if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {\r
271         RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight);\r
272     }\r
273     m_pTxtBreak->Reset();\r
274     FX_FLOAT fInc = rect.Height() - fHeight;\r
275     if (bVertical) {\r
276         fInc = rect.Width() - fHeight;\r
277     }\r
278     if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft && m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) {\r
279         fInc /= 2.0f;\r
280     } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) {\r
281         fInc = 0.0f;\r
282     }\r
283     if (bVertical) {\r
284         rect.top += fStartPos;\r
285         rect.left += fInc;\r
286         rect.width      = fHeight;\r
287         rect.height = FX_MIN(fWidth, rect.Height());\r
288     } else {\r
289         rect.left += fStartPos;\r
290         rect.top += fInc;\r
291         rect.width      = FX_MIN(fWidth, rect.Width());\r
292         rect.height = fHeight;\r
293         if (m_dwStyles & FDE_TTOSTYLE_LastLineHeight) {\r
294             rect.height -= m_fLineSpace - m_fFontSize;\r
295         }\r
296     }\r
297 }\r
298 void CFDE_TextOut::SetLineWidth(CFX_RectF &rect)\r
299 {\r
300     if ((m_dwStyles & FDE_TTOSTYLE_SingleLine) == 0) {\r
301         FX_FLOAT fLineWidth = 0.0f;\r
302         if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) {\r
303             if (rect.Height() < 1.0f) {\r
304                 rect.height = m_fFontSize * 1000.0f;\r
305             }\r
306             fLineWidth = rect.Height();\r
307         } else {\r
308             if (rect.Width() < 1.0f) {\r
309                 rect.width = m_fFontSize * 1000.0f;\r
310             }\r
311             fLineWidth = rect.Width();\r
312         }\r
313         m_pTxtBreak->SetLineWidth(fLineWidth);\r
314     }\r
315 }\r
316 FX_BOOL CFDE_TextOut::RetrieveLineWidth(FX_DWORD dwBreakStatus, FX_FLOAT &fStartPos, FX_FLOAT &fWidth, FX_FLOAT &fHeight)\r
317 {\r
318     if (dwBreakStatus <= FX_TXTBREAK_PieceBreak) {\r
319         return FALSE;\r
320     }\r
321     FX_FLOAT fLineStep  = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;\r
322     FX_BOOL bLineWrap   = (m_dwStyles & FDE_TTOSTYLE_LineWrap) ? TRUE : FALSE;\r
323     FX_FLOAT fLineWidth = 0.0f;\r
324     FX_INT32 iCount             = m_pTxtBreak->CountBreakPieces();\r
325     for (FX_INT32 i = 0; i < iCount; i++) {\r
326         const CFX_TxtPiece *pPiece = m_pTxtBreak->GetBreakPiece(i);\r
327         fLineWidth += (FX_FLOAT)pPiece->m_iWidth / 20000.0f;\r
328         fStartPos = FX_MIN(fStartPos, (FX_FLOAT)pPiece->m_iStartPos / 20000.0f);\r
329     }\r
330     m_pTxtBreak->ClearBreakPieces();\r
331     if (dwBreakStatus == FX_TXTBREAK_ParagraphBreak) {\r
332         m_pTxtBreak->Reset();\r
333     }\r
334     if (!bLineWrap && dwBreakStatus == FX_TXTBREAK_LineBreak) {\r
335         fWidth += fLineWidth;\r
336     } else {\r
337         fWidth = FX_MAX(fWidth, fLineWidth);\r
338         fHeight += fLineStep;\r
339     }\r
340     m_iTotalLines++;\r
341     return TRUE;\r
342 }\r
343 void CFDE_TextOut::DrawText(FX_LPCWSTR pwsStr, FX_INT32 iLength, FX_INT32 x, FX_INT32 y)\r
344 {\r
345     CFX_RectF rtText;\r
346     rtText.Set((FX_FLOAT)x, (FX_FLOAT)y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f);\r
347     DrawText(pwsStr, iLength, rtText);\r
348 }\r
349 void CFDE_TextOut::DrawText(FX_LPCWSTR pwsStr, FX_INT32 iLength, FX_FLOAT x, FX_FLOAT y)\r
350 {\r
351     CFX_RectF rtText;\r
352     rtText.Set(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f);\r
353     DrawText(pwsStr, iLength, rtText);\r
354 }\r
355 void CFDE_TextOut::DrawText(FX_LPCWSTR pwsStr, FX_INT32 iLength, const CFX_Rect &rect)\r
356 {\r
357     CFX_RectF rtText;\r
358     rtText.Set((FX_FLOAT)rect.left, (FX_FLOAT)rect.top, (FX_FLOAT)rect.width, (FX_FLOAT)rect.height);\r
359     DrawText(pwsStr, iLength, rtText);\r
360 }\r
361 void CFDE_TextOut::DrawText(FX_LPCWSTR pwsStr, FX_INT32 iLength, const CFX_RectF &rect)\r
362 {\r
363     CFX_RectF rtText;\r
364     rtText.Set(rect.left, rect.top, rect.width, rect.height);\r
365     CFX_Matrix rm;\r
366     rm.SetReverse(m_Matrix);\r
367     rm.TransformRect(rtText);\r
368     DrawText(pwsStr, iLength, rtText, m_rtClip);\r
369 }\r
370 void CFDE_TextOut::DrawLogicText(FX_LPCWSTR pwsStr, FX_INT32 iLength, FX_FLOAT x, FX_FLOAT y)\r
371 {\r
372     CFX_RectF rtText;\r
373     rtText.Set(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f);\r
374     DrawLogicText(pwsStr, iLength, rtText);\r
375 }\r
376 void CFDE_TextOut::DrawLogicText(FX_LPCWSTR pwsStr, FX_INT32 iLength, const CFX_RectF &rect)\r
377 {\r
378     CFX_RectF rtClip;\r
379     rtClip.Set(m_rtLogicClip.left, m_rtLogicClip.top, m_rtLogicClip.width, m_rtLogicClip.height);\r
380     m_Matrix.TransformRect(rtClip);\r
381     DrawText(pwsStr, iLength, rect, rtClip);\r
382 }\r
383 void CFDE_TextOut::DrawText(FX_LPCWSTR pwsStr, FX_INT32 iLength, const CFX_RectF &rect, const CFX_RectF &rtClip)\r
384 {\r
385     FXSYS_assert(m_pFont != NULL && m_fFontSize >= 1.0f);\r
386     if (pwsStr == NULL || iLength < 1) {\r
387         return;\r
388     }\r
389     if (rect.width < m_fFontSize || rect.height < m_fFontSize) {\r
390         return;\r
391     }\r
392     FX_FLOAT fLineWidth = rect.width;\r
393     if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) {\r
394         fLineWidth = rect.height;\r
395     }\r
396     m_pTxtBreak->SetLineWidth(fLineWidth);\r
397     m_ttoLines.RemoveAll(TRUE);\r
398     m_wsText.Empty();\r
399     LoadText(pwsStr, iLength, rect);\r
400     if (m_dwStyles & FDE_TTOSTYLE_Ellipsis) {\r
401         ReplaceWidthEllipsis();\r
402     }\r
403     Reload(rect);\r
404     DoAlignment(rect);\r
405     OnDraw(rtClip);\r
406 }\r
407 void CFDE_TextOut::ExpandBuffer(FX_INT32 iSize, FX_INT32 iType)\r
408 {\r
409     switch (iType) {\r
410         case 0:\r
411             if (m_pCharWidths == NULL) {\r
412                 m_pCharWidths = (FX_INT32*)FDE_Alloc(iSize * sizeof(FX_INT32));\r
413                 m_iChars = iSize;\r
414             } else if (m_iChars < iSize) {\r
415                 m_pCharWidths = (FX_INT32*)FDE_Realloc(m_pCharWidths, iSize * sizeof(FX_INT32));\r
416                 m_iChars = iSize;\r
417             }\r
418             FXSYS_memset(m_pCharWidths, 0, iSize);\r
419             break;\r
420         case 1:\r
421             if (m_pEllCharWidths == NULL) {\r
422                 m_pEllCharWidths = (FX_INT32*)FDE_Alloc(iSize * sizeof(FX_INT32));\r
423                 m_iEllChars = iSize;\r
424             } else if (m_iEllChars < iSize) {\r
425                 m_pEllCharWidths = (FX_INT32*)FDE_Realloc(m_pEllCharWidths, iSize * sizeof(FX_INT32));\r
426                 m_iEllChars = iSize;\r
427             }\r
428             FXSYS_memset(m_pEllCharWidths, 0, iSize);\r
429             break;\r
430         case 2:\r
431             if (m_pCharPos == NULL) {\r
432                 m_pCharPos = (FXTEXT_CHARPOS*)FDE_Alloc(iSize * sizeof(FXTEXT_CHARPOS));\r
433                 m_iCharPosSize = iSize;\r
434             } else if (m_iCharPosSize < iSize) {\r
435                 m_pCharPos = (FXTEXT_CHARPOS*)FDE_Realloc(m_pCharPos, iSize * sizeof(FXTEXT_CHARPOS));\r
436                 m_iCharPosSize = iSize;\r
437             }\r
438             break;\r
439     }\r
440 }\r
441 void CFDE_TextOut::LoadEllipsis()\r
442 {\r
443     if (!m_bElliChanged) {\r
444         return;\r
445     }\r
446     m_bElliChanged = FALSE;\r
447     m_iEllipsisWidth = 0;\r
448     FX_INT32 iLength = m_wsEllipsis.GetLength();\r
449     if (iLength < 1) {\r
450         return;\r
451     }\r
452     ExpandBuffer(iLength, 1);\r
453     FX_LPCWSTR pStr = (FX_LPCWSTR)m_wsEllipsis;\r
454     FX_INT32 *pCharWidths = m_pEllCharWidths;\r
455     FX_DWORD dwBreakStatus;\r
456     FX_WCHAR wch;\r
457     while (iLength-- > 0) {\r
458         wch = *pStr++;\r
459         dwBreakStatus = m_pTxtBreak->AppendChar(wch);\r
460         if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {\r
461             RetrieveEllPieces(pCharWidths);\r
462         }\r
463     }\r
464     dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);\r
465     if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {\r
466         RetrieveEllPieces(pCharWidths);\r
467     }\r
468     m_pTxtBreak->Reset();\r
469 }\r
470 void CFDE_TextOut::RetrieveEllPieces(FX_INT32 *&pCharWidths)\r
471 {\r
472     FX_INT32 iCount = m_pTxtBreak->CountBreakPieces();\r
473     CFX_Char *pTC;\r
474     for (FX_INT32 i = 0; i < iCount; i++) {\r
475         const CFX_TxtPiece *pPiece = m_pTxtBreak->GetBreakPiece(i);\r
476         FX_INT32 iPieceChars = pPiece->GetLength();\r
477         for (FX_INT32 j = 0; j < iPieceChars; j++) {\r
478             pTC = pPiece->GetCharPtr(j);\r
479             if (pTC->m_iCharWidth <= 0) {\r
480                 *pCharWidths = 0;\r
481             } else {\r
482                 *pCharWidths = pTC->m_iCharWidth;\r
483             }\r
484             m_iEllipsisWidth += *pCharWidths;\r
485             pCharWidths++;\r
486         }\r
487     }\r
488     m_pTxtBreak->ClearBreakPieces();\r
489 }\r
490 void CFDE_TextOut::LoadText(FX_LPCWSTR pwsStr, FX_INT32 iLength, const CFX_RectF &rect)\r
491 {\r
492     FX_LPWSTR pStr              = m_wsText.GetBuffer(iLength);\r
493     FX_INT32 iTxtLength = iLength;\r
494     ExpandBuffer(iTxtLength, 0);\r
495     FX_BOOL bHotKey             = (m_dwStyles & FDE_TTOSTYLE_HotKey) ? TRUE : FALSE;\r
496     FX_BOOL bVertical   = (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) ? TRUE : FALSE;\r
497     FX_BOOL bLineWrap   = (m_dwStyles & FDE_TTOSTYLE_LineWrap) ? TRUE : FALSE;\r
498     FX_FLOAT fLineStep  = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;\r
499     FX_FLOAT fLineStop  = bVertical ? rect.left : rect.bottom();\r
500     m_fLinePos  = bVertical ? rect.right() : rect.top;\r
501     if (bVertical) {\r
502         fLineStep = -fLineStep;\r
503     }\r
504     m_hotKeys.RemoveAll();\r
505     FX_INT32 iStartChar = 0;\r
506     FX_INT32 iChars             = 0;\r
507     FX_INT32 iPieceWidths = 0;\r
508     FX_DWORD dwBreakStatus;\r
509     FX_WCHAR wch;\r
510     FX_BOOL bRet = FALSE;\r
511     while (iTxtLength-- > 0) {\r
512         wch = *pwsStr++;\r
513         if (wch == L'&' && bHotKey && (pStr - 1) != NULL && *(pStr - 1) != L'&') {\r
514             if (iTxtLength > 0) {\r
515                 m_hotKeys.Add(iChars);\r
516             }\r
517             continue;\r
518         }\r
519         *pStr++ = wch;\r
520         iChars++;\r
521         dwBreakStatus = m_pTxtBreak->AppendChar(wch);\r
522         if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {\r
523             FX_BOOL bEndofLine = RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, FALSE, rect);\r
524             if (bEndofLine && (bLineWrap || (dwBreakStatus > FX_TXTBREAK_LineBreak && !bLineWrap))) {\r
525                 iPieceWidths = 0;\r
526                 m_iCurLine++;\r
527                 m_fLinePos += fLineStep;\r
528             }\r
529             if ((bVertical && m_fLinePos + fLineStep < fLineStop) || (!bVertical && m_fLinePos + fLineStep > fLineStop)) {\r
530                 FX_INT32 iCurLine = m_iCurLine;\r
531                 if (bEndofLine) {\r
532                     iCurLine--;\r
533                 }\r
534                 CFDE_TTOLine *pLine = m_ttoLines.GetPtrAt(iCurLine);\r
535                 pLine->m_bNewReload = TRUE;\r
536                 bRet = TRUE;\r
537                 break;\r
538             }\r
539         }\r
540     }\r
541     dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);\r
542     if (dwBreakStatus > FX_TXTBREAK_PieceBreak && !bRet) {\r
543         RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, FALSE, rect);\r
544     }\r
545     m_pTxtBreak->ClearBreakPieces();\r
546     m_pTxtBreak->Reset();\r
547     m_wsText.ReleaseBuffer(iLength);\r
548 }\r
549 FX_BOOL CFDE_TextOut::RetriecePieces(FX_DWORD dwBreakStatus, FX_INT32 &iStartChar, FX_INT32 &iPieceWidths, FX_BOOL bReload,\r
550                                      const CFX_RectF &rect)\r
551 {\r
552     FX_BOOL bSingleLine = (m_dwStyles & FDE_TTOSTYLE_SingleLine) ? TRUE : FALSE;\r
553     FX_BOOL bLineWrap   = (m_dwStyles & FDE_TTOSTYLE_LineWrap) ? TRUE : FALSE;\r
554     FX_BOOL bVertical   = (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) ? TRUE : FALSE;\r
555     FX_FLOAT fLineStep  = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;\r
556     if (bVertical) {\r
557         fLineStep = -fLineStep;\r
558     }\r
559     CFX_Char *pTC               = NULL;\r
560     FX_BOOL bNeedReload = FALSE;\r
561     FX_FLOAT fLineWidth = bVertical ? rect.Height() : rect.Width();\r
562     FX_INT32 iLineWidth = FXSYS_round(fLineWidth * 20000.0f);\r
563     FX_INT32 iCount             = m_pTxtBreak->CountBreakPieces();\r
564     for (FX_INT32 i = 0; i < iCount; i++) {\r
565         const CFX_TxtPiece *pPiece = m_pTxtBreak->GetBreakPiece(i);\r
566         FX_INT32 iPieceChars = pPiece->GetLength();\r
567         FX_INT32 iChar  = iStartChar;\r
568         FX_INT32 iWidth = 0;\r
569         FX_INT32 j = 0;\r
570         for (; j < iPieceChars; j++) {\r
571             pTC = pPiece->GetCharPtr(j);\r
572             FX_INT32 iCurCharWidth = pTC->m_iCharWidth > 0 ? pTC->m_iCharWidth : 0;\r
573             if (bSingleLine || !bLineWrap) {\r
574                 if (iLineWidth - iPieceWidths - iWidth < iCurCharWidth) {\r
575                     bNeedReload = TRUE;\r
576                     break;\r
577                 }\r
578             }\r
579             iWidth += iCurCharWidth;\r
580             m_pCharWidths[iChar++] = iCurCharWidth;\r
581         }\r
582         if (j == 0 && !bReload) {\r
583             CFDE_TTOLine *pLine = m_ttoLines.GetPtrAt(m_iCurLine);\r
584             pLine->m_bNewReload = TRUE;\r
585         } else if (j > 0) {\r
586             CFX_RectF rtPiece;\r
587             if (bVertical) {\r
588                 rtPiece.left    = m_fLinePos;\r
589                 rtPiece.top             = rect.top + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f;\r
590                 rtPiece.width   = fLineStep;\r
591                 rtPiece.height  = iWidth / 20000.0f;\r
592             } else {\r
593                 rtPiece.left    = rect.left + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f;\r
594                 rtPiece.top             = m_fLinePos;\r
595                 rtPiece.width   = iWidth / 20000.0f;\r
596                 rtPiece.height  = fLineStep;\r
597             }\r
598             FDE_TTOPIECE ttoPiece;\r
599             ttoPiece.iStartChar = iStartChar;\r
600             ttoPiece.iChars             = j;\r
601             ttoPiece.rtPiece    = rtPiece;\r
602             ttoPiece.dwCharStyles = pPiece->m_dwCharStyles;\r
603             if (FX_IsOdd(pPiece->m_iBidiLevel)) {\r
604                 ttoPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;\r
605             }\r
606             AppendPiece(ttoPiece, bNeedReload, (bReload && i == iCount - 1));\r
607         }\r
608         iStartChar += iPieceChars;\r
609         iPieceWidths += iWidth;\r
610     }\r
611     m_pTxtBreak->ClearBreakPieces();\r
612     FX_BOOL bRet = bSingleLine || bLineWrap || (!bLineWrap && bNeedReload) || dwBreakStatus == FX_TXTBREAK_ParagraphBreak;\r
613     return bRet;\r
614 }\r
615 void CFDE_TextOut::AppendPiece(const FDE_TTOPIECE &ttoPiece, FX_BOOL bNeedReload, FX_BOOL bEnd)\r
616 {\r
617     if (m_iCurLine >= m_ttoLines.GetSize()) {\r
618         CFDE_TTOLine ttoLine;\r
619         ttoLine.m_bNewReload = bNeedReload;\r
620         m_iCurPiece     = ttoLine.AddPiece(m_iCurPiece, ttoPiece);\r
621         m_iCurLine      = m_ttoLines.Add(ttoLine);\r
622     } else {\r
623         CFDE_TTOLine *pLine     = m_ttoLines.GetPtrAt(m_iCurLine);\r
624         pLine->m_bNewReload     = bNeedReload;\r
625         m_iCurPiece = pLine->AddPiece(m_iCurPiece, ttoPiece);\r
626         if (bEnd) {\r
627             FX_INT32 iPieces = pLine->GetSize();\r
628             if (m_iCurPiece < iPieces) {\r
629                 pLine->RemoveLast(iPieces - m_iCurPiece - 1);\r
630             }\r
631         }\r
632     }\r
633     if (!bEnd && bNeedReload) {\r
634         m_iCurPiece = 0;\r
635     }\r
636 }\r
637 void CFDE_TextOut::ReplaceWidthEllipsis()\r
638 {\r
639     LoadEllipsis();\r
640     FX_INT32 iLength = m_wsEllipsis.GetLength();\r
641     if (iLength < 1) {\r
642         return;\r
643     }\r
644     FX_INT32 iLines = m_ttoLines.GetSize();\r
645     for (FX_INT32 i = 0; i < iLines; i++) {\r
646         CFDE_TTOLine *pLine = m_ttoLines.GetPtrAt(i);\r
647         if (!pLine->m_bNewReload) {\r
648             continue;\r
649         }\r
650         FX_INT32 iEllipsisCharIndex = iLength - 1;\r
651         FX_INT32 iCharWidth = 0;\r
652         FX_INT32 iCharCount     = 0;\r
653         FX_INT32 iPiece = pLine->GetSize();\r
654         while (iPiece-- > 0) {\r
655             FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(iPiece);\r
656             if (pPiece == NULL) {\r
657                 break;\r
658             }\r
659             for (FX_INT32 j = pPiece->iChars - 1; j >= 0; j--) {\r
660                 if (iEllipsisCharIndex < 0) {\r
661                     break;\r
662                 }\r
663                 FX_INT32 index = pPiece->iStartChar + j;\r
664                 iCharWidth += m_pCharWidths[index];\r
665                 iCharCount++;\r
666                 if (iCharCount <= iLength) {\r
667                     m_wsText.SetAt(index, m_wsEllipsis.GetAt(iEllipsisCharIndex));\r
668                     m_pCharWidths[index] = m_pEllCharWidths[iEllipsisCharIndex];\r
669                 } else if (iCharWidth <= m_iEllipsisWidth) {\r
670                     m_wsText.SetAt(index, 0);\r
671                     m_pCharWidths[index] = 0;\r
672                 }\r
673                 iEllipsisCharIndex--;\r
674             }\r
675             if (iEllipsisCharIndex < 0) {\r
676                 break;\r
677             }\r
678         }\r
679     }\r
680 }\r
681 void CFDE_TextOut::Reload(const CFX_RectF &rect)\r
682 {\r
683     FX_INT32 iCount = m_ttoLines.GetSize();\r
684     for (FX_INT32 i = 0; i < iCount; i++) {\r
685         CFDE_TTOLine *pLine = m_ttoLines.GetPtrAt(i);\r
686         if (pLine == NULL || !pLine->m_bNewReload) {\r
687             continue;\r
688         }\r
689         m_iCurLine      = i;\r
690         m_iCurPiece = 0;\r
691         ReloadLinePiece(pLine, rect);\r
692     }\r
693 }\r
694 void CFDE_TextOut::ReloadLinePiece(CFDE_TTOLine *pLine, const CFX_RectF &rect)\r
695 {\r
696     FX_LPCWSTR pwsStr           = (FX_LPCWSTR)m_wsText;\r
697     FX_BOOL bVertical           = (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) ? TRUE : FALSE;\r
698     FX_INT32 iPieceWidths       = 0;\r
699     FDE_LPTTOPIECE pPiece       = pLine->GetPtrAt(0);\r
700     FX_INT32 iStartChar         = pPiece->iStartChar;\r
701     m_fLinePos  = bVertical ? pPiece->rtPiece.left : pPiece->rtPiece.top;\r
702     FX_INT32 iPieceCount        = pLine->GetSize();\r
703     FX_INT32 iPieceIndex        = 0;\r
704     FX_DWORD dwBreakStatus      = 0;\r
705     FX_WCHAR wch;\r
706     while (iPieceIndex < iPieceCount) {\r
707         FX_INT32 iStar  = iStartChar;\r
708         FX_INT32 iEnd   = pPiece->iChars + iStar;\r
709         while (iStar < iEnd) {\r
710             wch = *(pwsStr + iStar);\r
711             dwBreakStatus = m_pTxtBreak->AppendChar(wch);\r
712             if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {\r
713                 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, TRUE, rect);\r
714             }\r
715             iStar++;\r
716         }\r
717         iPieceIndex++;\r
718         pPiece = pLine->GetPtrAt(iPieceIndex);\r
719     }\r
720     dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);\r
721     if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {\r
722         RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, TRUE, rect);\r
723     }\r
724     m_pTxtBreak->Reset();\r
725 }\r
726 void CFDE_TextOut::DoAlignment(const CFX_RectF &rect)\r
727 {\r
728     FX_BOOL bVertical   = (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) ? TRUE : FALSE;\r
729     FX_FLOAT fLineStopS = bVertical ? rect.right() : rect.bottom();\r
730     FX_INT32 iLines             = m_ttoLines.GetSize();\r
731     if (iLines < 1) {\r
732         return;\r
733     }\r
734     CFDE_TTOLine *pLine = m_ttoLines.GetPtrAt(iLines - 1);\r
735     FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(0);\r
736     if (pPiece == NULL) {\r
737         return;\r
738     }\r
739     FX_FLOAT fLineStopD = bVertical ? pPiece->rtPiece.right() : pPiece->rtPiece.bottom();\r
740     FX_FLOAT fInc               = fLineStopS - fLineStopD;\r
741     if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft && m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) {\r
742         fInc /= 2.0f;\r
743     } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) {\r
744         fInc = 0.0f;\r
745     }\r
746     if (fInc < 1.0f) {\r
747         return;\r
748     }\r
749     for (FX_INT32 i = 0; i < iLines; i++) {\r
750         CFDE_TTOLine *pLine = m_ttoLines.GetPtrAt(i);\r
751         FX_INT32 iPieces = pLine->GetSize();\r
752         for (FX_INT32 j = 0; j < iPieces; j++) {\r
753             FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(j);\r
754             if (bVertical) {\r
755                 pPiece->rtPiece.left += fInc;\r
756             } else {\r
757                 pPiece->rtPiece.top      += fInc;\r
758             }\r
759         }\r
760     }\r
761 }\r
762 void CFDE_TextOut::OnDraw(const CFX_RectF &rtClip)\r
763 {\r
764     if (m_pRenderDevice == NULL) {\r
765         return;\r
766     }\r
767     FX_INT32 iLines = m_ttoLines.GetSize();\r
768     if (iLines < 1) {\r
769         return;\r
770     }\r
771     IFDE_SolidBrush *pBrush = (IFDE_SolidBrush*)IFDE_Brush::Create(FDE_BRUSHTYPE_Solid);\r
772     pBrush->SetColor(m_TxtColor);\r
773     IFDE_Pen *pPen = NULL;\r
774     FDE_HDEVICESTATE hDev = m_pRenderDevice->SaveState();\r
775     if (rtClip.Width() > 0.0f && rtClip.Height() > 0.0f) {\r
776         m_pRenderDevice->SetClipRect(rtClip);\r
777     }\r
778     for (FX_INT32 i = 0; i < iLines; i++) {\r
779         CFDE_TTOLine *pLine = m_ttoLines.GetPtrAt(i);\r
780         FX_INT32 iPieces = pLine->GetSize();\r
781         for (FX_INT32 j = 0; j < iPieces; j++) {\r
782             FDE_LPTTOPIECE pPiece = pLine->GetPtrAt(j);\r
783             if (pPiece == NULL) {\r
784                 continue;\r
785             }\r
786             FX_INT32 iCount = GetDisplayPos(pPiece);\r
787             if (iCount > 0) {\r
788                 m_pRenderDevice->DrawString(pBrush, m_pFont, m_pCharPos, iCount, m_fFontSize, &m_Matrix);\r
789             }\r
790             DrawLine(pPiece, pPen);\r
791         }\r
792     }\r
793     m_pRenderDevice->RestoreState(hDev);\r
794     if (pBrush) {\r
795         pBrush->Release();\r
796     }\r
797     if (pPen)   {\r
798         pPen->Release();\r
799     }\r
800 }\r
801 FX_INT32 CFDE_TextOut::GetDisplayPos(FDE_LPTTOPIECE pPiece)\r
802 {\r
803     FX_TXTRUN tr;\r
804     ToTextRun(pPiece, tr);\r
805     ExpandBuffer(tr.iLength, 2);\r
806     return m_pTxtBreak->GetDisplayPos(&tr, m_pCharPos);\r
807 }\r
808 FX_INT32 CFDE_TextOut::GetCharRects(FDE_LPTTOPIECE pPiece)\r
809 {\r
810     FX_TXTRUN tr;\r
811     ToTextRun(pPiece, tr);\r
812     m_rectArray.RemoveAll();\r
813     return m_pTxtBreak->GetCharRects(&tr, m_rectArray);\r
814 }\r
815 void CFDE_TextOut::ToTextRun(const FDE_LPTTOPIECE pPiece, FX_TXTRUN &tr)\r
816 {\r
817     tr.pAccess          = NULL;\r
818     tr.pIdentity        = NULL;\r
819     tr.pStr                     = (FX_LPCWSTR)m_wsText + pPiece->iStartChar;\r
820     tr.pWidths          = m_pCharWidths + pPiece->iStartChar;\r
821     tr.iLength          = pPiece->iChars;\r
822     tr.pFont            = m_pFont;\r
823     tr.fFontSize        = m_fFontSize;\r
824     tr.dwStyles         = m_dwTxtBkStyles;\r
825     tr.iCharRotation = 0;\r
826     tr.dwCharStyles     = pPiece->dwCharStyles;\r
827     tr.wLineBreakChar = m_wParagraphBkChar;\r
828     tr.pRect            = &pPiece->rtPiece;\r
829 }\r
830 void CFDE_TextOut::DrawLine(const FDE_LPTTOPIECE pPiece, IFDE_Pen *&pPen)\r
831 {\r
832     FX_BOOL bUnderLine = (m_dwStyles & FDE_TTOSTYLE_Underline) ? TRUE : FALSE;\r
833     FX_BOOL bStrikeOut = (m_dwStyles & FDE_TTOSTYLE_Strikeout) ? TRUE : FALSE;\r
834     FX_BOOL bHotKey        = (m_dwStyles & FDE_TTOSTYLE_HotKey) ? TRUE : FALSE;\r
835     FX_BOOL bVertical  = (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) ? TRUE : FALSE;\r
836     if (!bUnderLine && !bStrikeOut && !bHotKey) {\r
837         return;\r
838     }\r
839     if (pPen == NULL) {\r
840         pPen = IFDE_Pen::Create();\r
841         pPen->SetColor(m_TxtColor);\r
842     }\r
843     IFDE_Path *pPath = IFDE_Path::Create();\r
844     FX_INT32 iLineCount = 0;\r
845     CFX_RectF rtText = pPiece->rtPiece;\r
846     CFX_PointF pt1, pt2;\r
847     if (bUnderLine) {\r
848         if (bVertical) {\r
849             pt1.x = rtText.left;\r
850             pt1.y = rtText.top;\r
851             pt2.x = rtText.left;\r
852             pt2.y = rtText.bottom();\r
853         } else {\r
854             pt1.x = rtText.left;\r
855             pt1.y = rtText.bottom();\r
856             pt2.x = rtText.right();\r
857             pt2.y = rtText.bottom();\r
858         }\r
859         pPath->AddLine(pt1, pt2);\r
860         iLineCount++;\r
861     }\r
862     if (bStrikeOut) {\r
863         if (bVertical) {\r
864             pt1.x = rtText.left + rtText.width * 2.0f / 5.0f;\r
865             pt1.y = rtText.top;;\r
866             pt2.x = pt1.x;\r
867             pt2.y = rtText.bottom();\r
868         } else {\r
869             pt1.x = rtText.left;\r
870             pt1.y = rtText.bottom() - rtText.height * 2.0f / 5.0f;\r
871             pt2.x = rtText.right();\r
872             pt2.y = pt1.y;\r
873         }\r
874         pPath->AddLine(pt1, pt2);\r
875         iLineCount++;\r
876     }\r
877     if (bHotKey) {\r
878         FX_INT32 iHotKeys = m_hotKeys.GetSize();\r
879         FX_INT32 iCount   = GetCharRects(pPiece);\r
880         if (iCount > 0) {\r
881             for (FX_INT32 i = 0; i < iHotKeys; i++) {\r
882                 FX_INT32 iCharIndex = m_hotKeys.GetAt(i);\r
883                 if (iCharIndex >= pPiece->iStartChar && iCharIndex < pPiece->iStartChar + pPiece->iChars) {\r
884                     CFX_RectF rect = m_rectArray.GetAt(iCharIndex - pPiece->iStartChar);\r
885                     if (bVertical) {\r
886                         pt1.x = rect.left;\r
887                         pt1.y = rect.top;\r
888                         pt2.x = rect.left;\r
889                         pt2.y = rect.bottom();\r
890                     } else {\r
891                         pt1.x = rect.left;\r
892                         pt1.y = rect.bottom();\r
893                         pt2.x = rect.right();\r
894                         pt2.y = rect.bottom();\r
895                     }\r
896                     pPath->AddLine(pt1, pt2);\r
897                     iLineCount++;\r
898                 }\r
899             }\r
900         }\r
901     }\r
902     if (iLineCount > 0) {\r
903         m_pRenderDevice->DrawPath(pPen, 1, pPath, &m_Matrix);\r
904     }\r
905     pPath->Release();\r
906 }\r
907 CFDE_TTOLine::CFDE_TTOLine()\r
908     : m_bNewReload(FALSE)\r
909     , m_pieces(5)\r
910     , m_iPieceCount(0)\r
911 {\r
912 }\r
913 CFDE_TTOLine::CFDE_TTOLine(const CFDE_TTOLine &ttoLine)\r
914     : m_pieces(5)\r
915 {\r
916     m_bNewReload        = ttoLine.m_bNewReload;\r
917     m_iPieceCount       = ttoLine.m_iPieceCount;\r
918     m_pieces.Copy(ttoLine.m_pieces);\r
919 }\r
920 CFDE_TTOLine::~CFDE_TTOLine()\r
921 {\r
922 }\r
923 FX_INT32 CFDE_TTOLine::AddPiece(FX_INT32 index, const FDE_TTOPIECE &ttoPiece)\r
924 {\r
925     if (index >= m_iPieceCount) {\r
926         index = m_pieces.Add(ttoPiece) + 1;\r
927         m_iPieceCount++;\r
928     } else {\r
929         FDE_TTOPIECE &piece = m_pieces.GetAt(index);\r
930         piece = ttoPiece;\r
931     }\r
932     return index;\r
933 }\r
934 FX_INT32 CFDE_TTOLine::GetSize() const\r
935 {\r
936     return m_iPieceCount;\r
937 }\r
938 FDE_LPTTOPIECE CFDE_TTOLine::GetPtrAt(FX_INT32 index)\r
939 {\r
940     if (index >= m_iPieceCount) {\r
941         return NULL;\r
942     }\r
943     return m_pieces.GetPtrAt(index);\r
944 }\r
945 void CFDE_TTOLine::RemoveLast(FX_INT32 iCount)\r
946 {\r
947     m_pieces.RemoveLast(iCount);\r
948 }\r
949 void CFDE_TTOLine::RemoveAll(FX_BOOL bLeaveMemory)\r
950 {\r
951     m_pieces.RemoveAll(bLeaveMemory);\r
952 }\r