1// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "xfa/fxfa/app/xfa_fftext.h"
8
9#include "xfa/fwl/fwl_widgetdef.h"
10#include "xfa/fwl/fwl_widgethit.h"
11#include "xfa/fxfa/app/cxfa_linkuserdata.h"
12#include "xfa/fxfa/app/cxfa_pieceline.h"
13#include "xfa/fxfa/app/cxfa_textlayout.h"
14#include "xfa/fxfa/app/xfa_ffdraw.h"
15#include "xfa/fxfa/app/xfa_textpiece.h"
16#include "xfa/fxfa/xfa_ffapp.h"
17#include "xfa/fxfa/xfa_ffdoc.h"
18#include "xfa/fxfa/xfa_ffpageview.h"
19#include "xfa/fxfa/xfa_ffwidget.h"
20#include "xfa/fxgraphics/cfx_graphics.h"
21
22CXFA_FFText::CXFA_FFText(CXFA_WidgetAcc* pDataAcc) : CXFA_FFDraw(pDataAcc) {}
23
24CXFA_FFText::~CXFA_FFText() {}
25
26void CXFA_FFText::RenderWidget(CFX_Graphics* pGS,
27                               CFX_Matrix* pMatrix,
28                               uint32_t dwStatus) {
29  if (!IsMatchVisibleStatus(dwStatus))
30    return;
31
32  CFX_Matrix mtRotate = GetRotateMatrix();
33  if (pMatrix)
34    mtRotate.Concat(*pMatrix);
35
36  CXFA_FFWidget::RenderWidget(pGS, &mtRotate, dwStatus);
37
38  CXFA_TextLayout* pTextLayout = m_pDataAcc->GetTextLayout();
39  if (!pTextLayout)
40    return;
41
42  CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice();
43  CFX_RectF rtText = GetRectWithoutRotate();
44  if (CXFA_Margin mgWidget = m_pDataAcc->GetMargin()) {
45    CXFA_LayoutItem* pItem = this;
46    if (!pItem->GetPrev() && !pItem->GetNext()) {
47      XFA_RectWidthoutMargin(rtText, mgWidget);
48    } else {
49      FX_FLOAT fLeftInset;
50      FX_FLOAT fRightInset;
51      FX_FLOAT fTopInset = 0;
52      FX_FLOAT fBottomInset = 0;
53      mgWidget.GetLeftInset(fLeftInset);
54      mgWidget.GetRightInset(fRightInset);
55      if (!pItem->GetPrev())
56        mgWidget.GetTopInset(fTopInset);
57      else if (!pItem->GetNext())
58        mgWidget.GetBottomInset(fBottomInset);
59
60      rtText.Deflate(fLeftInset, fTopInset, fRightInset, fBottomInset);
61    }
62  }
63
64  CFX_Matrix mt(1, 0, 0, 1, rtText.left, rtText.top);
65  CFX_RectF rtClip = rtText;
66  mtRotate.TransformRect(rtClip);
67  mt.Concat(mtRotate);
68  pTextLayout->DrawString(pRenderDevice, mt, rtClip, GetIndex());
69}
70
71bool CXFA_FFText::IsLoaded() {
72  CXFA_TextLayout* pTextLayout = m_pDataAcc->GetTextLayout();
73  return pTextLayout && !pTextLayout->m_bHasBlock;
74}
75bool CXFA_FFText::PerformLayout() {
76  CXFA_FFDraw::PerformLayout();
77  CXFA_TextLayout* pTextLayout = m_pDataAcc->GetTextLayout();
78  if (!pTextLayout) {
79    return false;
80  }
81  if (!pTextLayout->m_bHasBlock) {
82    return true;
83  }
84  pTextLayout->m_Blocks.RemoveAll();
85  CXFA_LayoutItem* pItem = this;
86  if (!pItem->GetPrev() && !pItem->GetNext()) {
87    return true;
88  }
89  pItem = pItem->GetFirst();
90  while (pItem) {
91    CFX_RectF rtText = pItem->GetRect(false);
92    if (CXFA_Margin mgWidget = m_pDataAcc->GetMargin()) {
93      if (!pItem->GetPrev()) {
94        FX_FLOAT fTopInset;
95        mgWidget.GetTopInset(fTopInset);
96        rtText.height -= fTopInset;
97      } else if (!pItem->GetNext()) {
98        FX_FLOAT fBottomInset;
99        mgWidget.GetBottomInset(fBottomInset);
100        rtText.height -= fBottomInset;
101      }
102    }
103    pTextLayout->ItemBlocks(rtText, pItem->GetIndex());
104    pItem = pItem->GetNext();
105  }
106  pTextLayout->m_bHasBlock = false;
107  return true;
108}
109
110bool CXFA_FFText::OnLButtonDown(uint32_t dwFlags, const CFX_PointF& point) {
111  if (!GetRectWithoutRotate().Contains(point))
112    return false;
113
114  const FX_WCHAR* wsURLContent = GetLinkURLAtPoint(point);
115  if (!wsURLContent)
116    return false;
117
118  SetButtonDown(true);
119  return true;
120}
121
122bool CXFA_FFText::OnMouseMove(uint32_t dwFlags, const CFX_PointF& point) {
123  return GetRectWithoutRotate().Contains(point) && !!GetLinkURLAtPoint(point);
124}
125
126bool CXFA_FFText::OnLButtonUp(uint32_t dwFlags, const CFX_PointF& point) {
127  if (!IsButtonDown())
128    return false;
129
130  SetButtonDown(false);
131  const FX_WCHAR* wsURLContent = GetLinkURLAtPoint(point);
132  if (!wsURLContent)
133    return false;
134
135  CXFA_FFDoc* pDoc = GetDoc();
136  pDoc->GetDocEnvironment()->GotoURL(pDoc, wsURLContent);
137  return true;
138}
139
140FWL_WidgetHit CXFA_FFText::OnHitTest(const CFX_PointF& point) {
141  if (!GetRectWithoutRotate().Contains(point))
142    return FWL_WidgetHit::Unknown;
143  if (!GetLinkURLAtPoint(point))
144    return FWL_WidgetHit::Unknown;
145  return FWL_WidgetHit::HyperLink;
146}
147
148const FX_WCHAR* CXFA_FFText::GetLinkURLAtPoint(const CFX_PointF& point) {
149  CXFA_TextLayout* pTextLayout = m_pDataAcc->GetTextLayout();
150  if (!pTextLayout)
151    return nullptr;
152
153  CFX_RectF rect = GetRectWithoutRotate();
154  for (const auto& pPieceLine : *pTextLayout->GetPieceLines()) {
155    for (const auto& pPiece : pPieceLine->m_textPieces) {
156      if (pPiece->pLinkData &&
157          pPiece->rtPiece.Contains(point - rect.TopLeft())) {
158        return pPiece->pLinkData->GetLinkURL();
159      }
160    }
161  }
162  return nullptr;
163}
164