// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "xfa/fwl/cfwl_widgetmgr.h"
#include <utility>
#include "third_party/base/ptr_util.h"
#include "xfa/fwl/cfwl_app.h"
#include "xfa/fwl/cfwl_form.h"
#include "xfa/fwl/cfwl_notedriver.h"
#include "xfa/fxfa/app/xfa_fwladapter.h"
#include "xfa/fxfa/xfa_ffapp.h"
namespace {
const int kNeedRepaintHitPoints = 12;
const int kNeedRepaintHitPiece = 3;
struct FWL_NEEDREPAINTHITDATA {
CFX_PointF hitPoint;
bool bNotNeedRepaint;
bool bNotContainByDirty;
};
} // namespace
CFWL_WidgetMgr::CFWL_WidgetMgr(CXFA_FFApp* pAdapterNative)
: m_dwCapability(0), m_pAdapter(pAdapterNative->GetWidgetMgr(this)) {
ASSERT(m_pAdapter);
m_mapWidgetItem[nullptr] = pdfium::MakeUnique<Item>();
#if (_FX_OS_ == _FX_WIN32_DESKTOP_) || (_FX_OS_ == _FX_WIN64_)
m_rtScreen.Reset();
#endif
}
CFWL_WidgetMgr::~CFWL_WidgetMgr() {}
CFWL_Widget* CFWL_WidgetMgr::GetParentWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
return pItem && pItem->pParent ? pItem->pParent->pWidget : nullptr;
}
CFWL_Widget* CFWL_WidgetMgr::GetOwnerWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
return pItem && pItem->pOwner ? pItem->pOwner->pWidget : nullptr;
}
CFWL_Widget* CFWL_WidgetMgr::GetFirstSiblingWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
if (!pItem)
return nullptr;
pItem = pItem->pPrevious;
while (pItem && pItem->pPrevious)
pItem = pItem->pPrevious;
return pItem ? pItem->pWidget : nullptr;
}
CFWL_Widget* CFWL_WidgetMgr::GetPriorSiblingWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
return pItem && pItem->pPrevious ? pItem->pPrevious->pWidget : nullptr;
}
CFWL_Widget* CFWL_WidgetMgr::GetNextSiblingWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
return pItem && pItem->pNext ? pItem->pNext->pWidget : nullptr;
}
CFWL_Widget* CFWL_WidgetMgr::GetFirstChildWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
return pItem && pItem->pChild ? pItem->pChild->pWidget : nullptr;
}
CFWL_Widget* CFWL_WidgetMgr::GetLastChildWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
if (!pItem)
return nullptr;
pItem = pItem->pChild;
while (pItem && pItem->pNext)
pItem = pItem->pNext;
return pItem ? pItem->pWidget : nullptr;
}
CFWL_Widget* CFWL_WidgetMgr::GetSystemFormWidget(CFWL_Widget* pWidget) const {
Item* pItem = GetWidgetMgrItem(pWidget);
while (pItem) {
if (IsAbleNative(pItem->pWidget))
return pItem->pWidget;
pItem = pItem->pParent;
}
return nullptr;
}
void CFWL_WidgetMgr::AppendWidget(CFWL_Widget* pWidget) {
Item* pItem = GetWidgetMgrItem(pWidget);
if (!pItem)
return;
if (!pItem->pParent)
return;
Item* pChild = pItem->pParent->pChild;
int32_t i = 0;
while (pChild) {
if (pChild == pItem) {
if (pChild->pPrevious)
pChild->pPrevious->pNext = pChild->pNext;
if (pChild->pNext)
pChild->pNext->pPrevious = pChild->pPrevious;
if (pItem->pParent->pChild == pItem)
pItem->pParent->pChild = pItem->pNext;
pItem->pNext = nullptr;
pItem->pPrevious = nullptr;
break;
}
if (!pChild->pNext)
break;
pChild = pChild->pNext;
++i;
}
pChild = pItem->pParent->pChild;
if (pChild) {
while (pChild->pNext)
pChild = pChild->pNext;
pChild->pNext = pItem;
pItem->pPrevious = pChild;
} else {
pItem->pParent->pChild = pItem;
pItem->pPrevious = nullptr;
}
pItem->pNext = nullptr;
}
void CFWL_WidgetMgr::RepaintWidget(CFWL_Widget* pWidget,
const CFX_RectF& rect) {
if (!m_pAdapter)
return;
CFWL_Widget* pNative = pWidget;
CFX_RectF transformedRect = rect;
if (IsFormDisabled()) {
CFWL_Widget* pOuter = pWidget->GetOuter();
while (pOuter) {
CFX_RectF rtTemp = pNative->GetWidgetRect();
transformedRect.left += rtTemp.left;
transformedRect.top += rtTemp.top;
pNative = pOuter;
pOuter = pOuter->GetOuter();
}
} else if (!IsAbleNative(pWidget)) {
pNative = GetSystemFormWidget(pWidget);
if (!pNative)
return;
CFX_PointF pos = pWidget->TransformTo(
pNative, CFX_PointF(transformedRect.left, transformedRect.top));
transformedRect.left = pos.x;
transformedRect.top = pos.y;
}
AddRedrawCounts(pNative);
m_pAdapter->RepaintWidget(pNative);
}
void CFWL_WidgetMgr::InsertWidget(CFWL_Widget* pParent, CFWL_Widget* pChild) {
Item* pParentItem = GetWidgetMgrItem(pParent);
if (!pParentItem) {
auto item = pdfium::MakeUnique<Item>(pParent);
pParentItem = item.get();
m_mapWidgetItem[pParent] = std::move(item);
pParentItem->pParent = GetWidgetMgrItem(nullptr);
AppendWidget(pParent);
}
Item* pItem = GetWidgetMgrItem(pChild);
if (!pItem) {
auto item = pdfium::MakeUnique<Item>(pChild);
pItem = item.get();
m_mapWidgetItem[pChild] = std::move(item);
}
if (pItem->pParent && pItem->pParent != pParentItem) {
if (pItem->pPrevious)
pItem->pPrevious->pNext = pItem->pNext;
if (pItem->pNext)
pItem->pNext->pPrevious = pItem->pPrevious;
if (pItem->pParent->pChild == pItem)
pItem->pParent->pChild = pItem->pNext;
}
pItem->pParent = pParentItem;
AppendWidget(pChild);
}
void CFWL_WidgetMgr::RemoveWidget(CFWL_Widget* pWidget) {
Item* pItem = GetWidgetMgrItem(pWidget);
if (!pItem)
return;
if (pItem->pPrevious)
pItem->pPrevious->pNext = pItem->pNext;
if (pItem->pNext)
pItem->pNext->pPrevious = pItem->pPrevious;
if (pItem->pParent && pItem->pParent->pChild == pItem)
pItem->pParent->pChild = pItem->pNext;
Item* pChild = pItem->pChild;
while (pChild) {
Item* pNext = pChild->pNext;
RemoveWidget(pChild->pWidget);
pChild = pNext;
}
m_mapWidgetItem.erase(pWidget);
}
void CFWL_WidgetMgr::SetOwner(CFWL_Widget* pOwner, CFWL_Widget* pOwned) {
Item* pParentItem = GetWidgetMgrItem(pOwner);
if (!pParentItem) {
auto item = pdfium::MakeUnique<Item>(pOwner);
pParentItem = item.get();
m_mapWidgetItem[pOwner] = std::move(item);
pParentItem->pParent = GetWidgetMgrItem(nullptr);
AppendWidget(pOwner);
}
Item* pItem = GetWidgetMgrItem(pOwned);
if (!pItem) {
auto item = pdfium::MakeUnique<Item>(pOwned);
pItem = item.get();
m_mapWidgetItem[pOwned] = std::move(item);
}
pItem->pOwner = pParentItem;
}
void CFWL_WidgetMgr::SetParent(CFWL_Widget* pParent, CFWL_Widget* pChild) {
Item* pParentItem = GetWidgetMgrItem(pParent);
Item* pItem = GetWidgetMgrItem(pChild);
if (!pItem)
return;
if (pItem->pParent && pItem->pParent != pParentItem) {
if (pItem->pPrevious)
pItem->pPrevious->pNext = pItem->pNext;
if (pItem->pNext)
pItem->pNext->pPrevious = pItem->pPrevious;
if (pItem->pParent->pChild == pItem)
pItem->pParent->pChild = pItem->pNext;
pItem->pNext = nullptr;
pItem->pPrevious = nullptr;
}
pItem->pParent = pParentItem;
AppendWidget(pChild);
}
CFWL_Widget* CFWL_WidgetMgr::GetWidgetAtPoint(CFWL_Widget* parent,
const CFX_PointF& point) const {
if (!parent)
return nullptr;
CFX_PointF pos;
CFWL_Widget* child = GetLastChildWidget(parent);
while (child) {
if ((child->GetStates() & FWL_WGTSTATE_Invisible) == 0) {
CFX_Matrix m;
m.SetIdentity();
CFX_Matrix matrixOnParent;
m.SetReverse(matrixOnParent);
pos = m.Transform(point);
CFX_RectF bounds = child->GetWidgetRect();
if (bounds.Contains(pos)) {
pos -= bounds.TopLeft();
return GetWidgetAtPoint(child, pos);
}
}
child = GetPriorSiblingWidget(child);
}
return parent;
}
CFWL_Widget* CFWL_WidgetMgr::NextTab(CFWL_Widget* parent,
CFWL_Widget* focus,
bool& bFind) {
CFWL_WidgetMgr* pMgr = parent->GetOwnerApp()->GetWidgetMgr();
CFWL_Widget* child = pMgr->GetFirstChildWidget(parent);
while (child) {
if (focus == child)
bFind = true;
CFWL_Widget* bRet = NextTab(child, focus, bFind);
if (bRet)
return bRet;
child = pMgr->GetNextSiblingWidget(child);
}
return nullptr;
}
int32_t CFWL_WidgetMgr::CountRadioButtonGroup(CFWL_Widget* pFirst) const {
int32_t iRet = 0;
CFWL_Widget* pChild = pFirst;
while (pChild) {
pChild = GetNextSiblingWidget(pChild);
++iRet;
}
return iRet;
}
CFWL_Widget* CFWL_WidgetMgr::GetRadioButtonGroupHeader(
CFWL_Widget* pRadioButton) const {
CFWL_Widget* pNext = pRadioButton;
if (pNext && (pNext->GetStyles() & FWL_WGTSTYLE_Group))
return pNext;
return nullptr;
}
std::vector<CFWL_Widget*> CFWL_WidgetMgr::GetSameGroupRadioButton(
CFWL_Widget* pRadioButton) const {
CFWL_Widget* pFirst = GetFirstSiblingWidget(pRadioButton);
if (!pFirst)
pFirst = pRadioButton;
if (CountRadioButtonGroup(pFirst) < 2)
return std::vector<CFWL_Widget*>();
std::vector<CFWL_Widget*> group;
group.push_back(GetRadioButtonGroupHeader(pRadioButton));
return group;
}
CFWL_Widget* CFWL_WidgetMgr::GetDefaultButton(CFWL_Widget* pParent) const {
if ((pParent->GetClassID() == FWL_Type::PushButton) &&
(pParent->GetStates() & (1 << (FWL_WGTSTATE_MAX + 2)))) {
return pParent;
}
CFWL_Widget* child =
pParent->GetOwnerApp()->GetWidgetMgr()->GetFirstChildWidget(pParent);
while (child) {
if ((child->GetClassID() == FWL_Type::PushButton) &&
(child->GetStates() & (1 << (FWL_WGTSTATE_MAX + 2)))) {
return child;
}
if (CFWL_Widget* find = GetDefaultButton(child))
return find;
child = child->GetOwnerApp()->GetWidgetMgr()->GetNextSiblingWidget(child);
}
return nullptr;
}
void CFWL_WidgetMgr::AddRedrawCounts(CFWL_Widget* pWidget) {
GetWidgetMgrItem(pWidget)->iRedrawCounter++;
}
void CFWL_WidgetMgr::ResetRedrawCounts(CFWL_Widget* pWidget) {
GetWidgetMgrItem(pWidget)->iRedrawCounter = 0;
}
CFWL_WidgetMgr::Item* CFWL_WidgetMgr::GetWidgetMgrItem(
CFWL_Widget* pWidget) const {
auto it = m_mapWidgetItem.find(pWidget);
return it != m_mapWidgetItem.end() ? static_cast<Item*>(it->second.get())
: nullptr;
}
bool CFWL_WidgetMgr::IsAbleNative(CFWL_Widget* pWidget) const {
if (!pWidget)
return false;
if (!pWidget->IsInstance(FWL_CLASS_Form))
return false;
uint32_t dwStyles = pWidget->GetStyles();
return ((dwStyles & FWL_WGTSTYLE_WindowTypeMask) ==
FWL_WGTSTYLE_OverLapper) ||
(dwStyles & FWL_WGTSTYLE_Popup);
}
void CFWL_WidgetMgr::GetAdapterPopupPos(CFWL_Widget* pWidget,
FX_FLOAT fMinHeight,
FX_FLOAT fMaxHeight,
const CFX_RectF& rtAnchor,
CFX_RectF& rtPopup) const {
m_pAdapter->GetPopupPos(pWidget, fMinHeight, fMaxHeight, rtAnchor, rtPopup);
}
void CFWL_WidgetMgr::OnSetCapability(uint32_t dwCapability) {
m_dwCapability = dwCapability;
}
void CFWL_WidgetMgr::OnProcessMessageToForm(CFWL_Message* pMessage) {
if (!pMessage)
return;
if (!pMessage->m_pDstTarget)
return;
CFWL_Widget* pDstWidget = pMessage->m_pDstTarget;
const CFWL_App* pApp = pDstWidget->GetOwnerApp();
if (!pApp)
return;
CFWL_NoteDriver* pNoteDriver =
static_cast<CFWL_NoteDriver*>(pApp->GetNoteDriver());
if (!pNoteDriver)
return;
if (IsFormDisabled())
pNoteDriver->ProcessMessage(pMessage->Clone());
else
pNoteDriver->QueueMessage(pMessage->Clone());
#if (_FX_OS_ == _FX_MACOSX_)
CFWL_NoteLoop* pTopLoop = pNoteDriver->GetTopLoop();
if (pTopLoop)
pNoteDriver->UnqueueMessageAndProcess(pTopLoop);
#endif
}
void CFWL_WidgetMgr::OnDrawWidget(CFWL_Widget* pWidget,
CFX_Graphics* pGraphics,
const CFX_Matrix* pMatrix) {
if (!pWidget || !pGraphics)
return;
CFX_RectF clipCopy(0, 0, pWidget->GetWidgetRect().Size());
CFX_RectF clipBounds;
#if _FX_OS_ == _FX_MACOSX_
if (IsFormDisabled()) {
#endif // _FX_OS_ == _FX_MACOSX_
pWidget->GetDelegate()->OnDrawWidget(pGraphics, pMatrix);
clipBounds = pGraphics->GetClipRect();
clipCopy = clipBounds;
#if _FX_OS_ == _FX_MACOSX_
} else {
clipBounds = CFX_RectF(pMatrix->a, pMatrix->b, pMatrix->c, pMatrix->d);
const_cast<CFX_Matrix*>(pMatrix)->SetIdentity(); // FIXME: const cast.
pWidget->GetDelegate()->OnDrawWidget(pGraphics, pMatrix);
}
#endif // _FX_OS_ == _FX_MACOSX_
if (!IsFormDisabled())
clipBounds.Intersect(pWidget->GetClientRect());
if (!clipBounds.IsEmpty())
DrawChild(pWidget, clipBounds, pGraphics, pMatrix);
GetWidgetMgrItem(pWidget)->iRedrawCounter = 0;
ResetRedrawCounts(pWidget);
}
void CFWL_WidgetMgr::DrawChild(CFWL_Widget* parent,
const CFX_RectF& rtClip,
CFX_Graphics* pGraphics,
const CFX_Matrix* pMatrix) {
if (!parent)
return;
bool bFormDisable = IsFormDisabled();
CFWL_Widget* pNextChild = GetFirstChildWidget(parent);
while (pNextChild) {
CFWL_Widget* child = pNextChild;
pNextChild = GetNextSiblingWidget(child);
if (child->GetStates() & FWL_WGTSTATE_Invisible)
continue;
CFX_RectF rtWidget = child->GetWidgetRect();
if (rtWidget.IsEmpty())
continue;
CFX_Matrix widgetMatrix;
CFX_RectF clipBounds(rtWidget);
if (!bFormDisable)
widgetMatrix = child->GetMatrix();
if (pMatrix)
widgetMatrix.Concat(*pMatrix);
if (!bFormDisable) {
CFX_PointF pos = widgetMatrix.Transform(clipBounds.TopLeft());
clipBounds.left = pos.x;
clipBounds.top = pos.y;
clipBounds.Intersect(rtClip);
if (clipBounds.IsEmpty())
continue;
pGraphics->SaveGraphState();
pGraphics->SetClipRect(clipBounds);
}
widgetMatrix.Translate(rtWidget.left, rtWidget.top, true);
if (IFWL_WidgetDelegate* pDelegate = child->GetDelegate()) {
if (IsFormDisabled() || IsNeedRepaint(child, &widgetMatrix, rtClip))
pDelegate->OnDrawWidget(pGraphics, &widgetMatrix);
}
if (!bFormDisable)
pGraphics->RestoreGraphState();
DrawChild(child, clipBounds, pGraphics,
bFormDisable ? &widgetMatrix : pMatrix);
child = GetNextSiblingWidget(child);
}
}
bool CFWL_WidgetMgr::IsNeedRepaint(CFWL_Widget* pWidget,
CFX_Matrix* pMatrix,
const CFX_RectF& rtDirty) {
Item* pItem = GetWidgetMgrItem(pWidget);
if (pItem && pItem->iRedrawCounter > 0) {
pItem->iRedrawCounter = 0;
return true;
}
CFX_RectF rtWidget(0, 0, pWidget->GetWidgetRect().Size());
pMatrix->TransformRect(rtWidget);
if (!rtWidget.IntersectWith(rtDirty))
return false;
CFWL_Widget* pChild =
pWidget->GetOwnerApp()->GetWidgetMgr()->GetFirstChildWidget(pWidget);
if (!pChild)
return true;
CFX_RectF rtChilds;
bool bChildIntersectWithDirty = false;
bool bOrginPtIntersectWidthChild = false;
bool bOrginPtIntersectWidthDirty = rtDirty.Contains(rtWidget.TopLeft());
static FWL_NEEDREPAINTHITDATA hitPoint[kNeedRepaintHitPoints];
FXSYS_memset(hitPoint, 0, sizeof(hitPoint));
FX_FLOAT fxPiece = rtWidget.width / kNeedRepaintHitPiece;
FX_FLOAT fyPiece = rtWidget.height / kNeedRepaintHitPiece;
hitPoint[2].hitPoint.x = hitPoint[6].hitPoint.x = rtWidget.left;
hitPoint[0].hitPoint.x = hitPoint[3].hitPoint.x = hitPoint[7].hitPoint.x =
hitPoint[10].hitPoint.x = fxPiece + rtWidget.left;
hitPoint[1].hitPoint.x = hitPoint[4].hitPoint.x = hitPoint[8].hitPoint.x =
hitPoint[11].hitPoint.x = fxPiece * 2 + rtWidget.left;
hitPoint[5].hitPoint.x = hitPoint[9].hitPoint.x =
rtWidget.width + rtWidget.left;
hitPoint[0].hitPoint.y = hitPoint[1].hitPoint.y = rtWidget.top;
hitPoint[2].hitPoint.y = hitPoint[3].hitPoint.y = hitPoint[4].hitPoint.y =
hitPoint[5].hitPoint.y = fyPiece + rtWidget.top;
hitPoint[6].hitPoint.y = hitPoint[7].hitPoint.y = hitPoint[8].hitPoint.y =
hitPoint[9].hitPoint.y = fyPiece * 2 + rtWidget.top;
hitPoint[10].hitPoint.y = hitPoint[11].hitPoint.y =
rtWidget.height + rtWidget.top;
do {
CFX_RectF rect = pChild->GetWidgetRect();
CFX_RectF r(rect.left + rtWidget.left, rect.top + rtWidget.top, rect.width,
rect.height);
if (r.IsEmpty())
continue;
if (r.Contains(rtDirty))
return false;
if (!bChildIntersectWithDirty && r.IntersectWith(rtDirty))
bChildIntersectWithDirty = true;
if (bOrginPtIntersectWidthDirty && !bOrginPtIntersectWidthChild)
bOrginPtIntersectWidthChild = rect.Contains(CFX_PointF(0, 0));
if (rtChilds.IsEmpty())
rtChilds = rect;
else if (!(pChild->GetStates() & FWL_WGTSTATE_Invisible))
rtChilds.Union(rect);
for (int32_t i = 0; i < kNeedRepaintHitPoints; i++) {
if (hitPoint[i].bNotContainByDirty || hitPoint[i].bNotNeedRepaint)
continue;
if (!rtDirty.Contains(hitPoint[i].hitPoint)) {
hitPoint[i].bNotContainByDirty = true;
continue;
}
if (r.Contains(hitPoint[i].hitPoint))
hitPoint[i].bNotNeedRepaint = true;
}
pChild =
pChild->GetOwnerApp()->GetWidgetMgr()->GetNextSiblingWidget(pChild);
} while (pChild);
if (!bChildIntersectWithDirty)
return true;
if (bOrginPtIntersectWidthDirty && !bOrginPtIntersectWidthChild)
return true;
if (rtChilds.IsEmpty())
return true;
int32_t repaintPoint = kNeedRepaintHitPoints;
for (int32_t i = 0; i < kNeedRepaintHitPoints; i++) {
if (hitPoint[i].bNotNeedRepaint)
repaintPoint--;
}
if (repaintPoint > 0)
return true;
pMatrix->TransformRect(rtChilds);
if (rtChilds.Contains(rtDirty) || rtChilds.Contains(rtWidget))
return false;
return true;
}
CFWL_WidgetMgr::Item::Item() : CFWL_WidgetMgr::Item(nullptr) {}
CFWL_WidgetMgr::Item::Item(CFWL_Widget* widget)
: pParent(nullptr),
pOwner(nullptr),
pChild(nullptr),
pPrevious(nullptr),
pNext(nullptr),
pWidget(widget),
iRedrawCounter(0)
#if (_FX_OS_ == _FX_WIN32_DESKTOP_) || (_FX_OS_ == _FX_WIN64_)
,
bOutsideChanged(false)
#endif
{
}
CFWL_WidgetMgr::Item::~Item() {}