Home | History | Annotate | Download | only in core
      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/src/foxitlib.h"
      8 #include "xfa/src/fwl/src/core/include/fwl_targetimp.h"
      9 #include "xfa/src/fwl/src/core/include/fwl_noteimp.h"
     10 #include "xfa/src/fwl/src/core/include/fwl_widgetmgrimp.h"
     11 #include "xfa/src/fwl/src/core/include/fwl_threadimp.h"
     12 #include "xfa/src/fwl/src/core/include/fwl_appimp.h"
     13 
     14 FX_BOOL FWL_UseOffscreen(IFWL_Widget* pWidget) {
     15 #if (_FX_OS_ == _FX_MACOSX_)
     16   return FALSE;
     17 #else
     18   return pWidget->GetStyles() & FWL_WGTSTYLE_Offscreen;
     19 #endif
     20 }
     21 IFWL_WidgetMgr* FWL_GetWidgetMgr() {
     22   IFWL_App* pApp = FWL_GetApp();
     23   if (!pApp)
     24     return NULL;
     25   return pApp->GetWidgetMgr();
     26 }
     27 CFWL_WidgetMgr::CFWL_WidgetMgr(IFWL_AdapterNative* pAdapterNative)
     28     : m_dwCapability(0) {
     29   m_pDelegate = new CFWL_WidgetMgrDelegate(this);
     30   m_pAdapter = pAdapterNative->GetWidgetMgr(m_pDelegate);
     31   FXSYS_assert(m_pAdapter);
     32   CFWL_WidgetMgrItem* pRoot = new CFWL_WidgetMgrItem;
     33   m_mapWidgetItem.SetAt(NULL, pRoot);
     34 #if (_FX_OS_ == _FX_WIN32_DESKTOP_) || (_FX_OS_ == _FX_WIN64_)
     35   m_rtScreen.Reset();
     36   IFWL_AdapterMonitorMgr* pMonitorMgr = pAdapterNative->GetMonitorMgr();
     37   if (pMonitorMgr) {
     38     FWL_HMONITOR monitor = pMonitorMgr->GetCurrentMonitor();
     39     if (monitor) {
     40       pMonitorMgr->GetMonitorSize(monitor, m_rtScreen.width, m_rtScreen.height);
     41     }
     42   }
     43 #endif
     44 }
     45 CFWL_WidgetMgr::~CFWL_WidgetMgr() {
     46   FX_POSITION ps = m_mapWidgetItem.GetStartPosition();
     47   while (ps) {
     48     void* pWidget;
     49     CFWL_WidgetMgrItem* pItem;
     50     m_mapWidgetItem.GetNextAssoc(ps, pWidget, (void*&)pItem);
     51     delete pItem;
     52   }
     53   m_mapWidgetItem.RemoveAll();
     54   if (m_pDelegate) {
     55     delete m_pDelegate;
     56     m_pDelegate = NULL;
     57   }
     58 }
     59 int32_t CFWL_WidgetMgr::CountWidgets(IFWL_Widget* pParent) {
     60   CFWL_WidgetMgrItem* pParentItem = GetWidgetMgrItem(pParent);
     61   return TravelWidgetMgr(pParentItem, NULL, NULL);
     62 }
     63 IFWL_Widget* CFWL_WidgetMgr::GetWidget(int32_t nIndex, IFWL_Widget* pParent) {
     64   CFWL_WidgetMgrItem* pParentItem = GetWidgetMgrItem(pParent);
     65   IFWL_Widget* pWidget = NULL;
     66   TravelWidgetMgr(pParentItem, &nIndex, NULL, &pWidget);
     67   return pWidget;
     68 }
     69 IFWL_Widget* CFWL_WidgetMgr::GetWidget(IFWL_Widget* pWidget,
     70                                        FWL_WGTRELATION eRelation) {
     71   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
     72   if (!pItem) {
     73     return NULL;
     74   }
     75   IFWL_Widget* pRet = NULL;
     76   switch (eRelation) {
     77     case FWL_WGTRELATION_Parent: {
     78       pRet = pItem->pParent ? pItem->pParent->pWidget : NULL;
     79       break;
     80     }
     81     case FWL_WGTRELATION_Owner: {
     82       pRet = pItem->pOwner ? pItem->pOwner->pWidget : NULL;
     83       break;
     84     }
     85     case FWL_WGTRELATION_FirstSibling: {
     86       pItem = pItem->pPrevious;
     87       while (pItem && pItem->pPrevious) {
     88         pItem = pItem->pPrevious;
     89       }
     90       pRet = pItem ? pItem->pWidget : NULL;
     91       break;
     92     }
     93     case FWL_WGTRELATION_PriorSibling: {
     94       pRet = pItem->pPrevious ? pItem->pPrevious->pWidget : NULL;
     95       break;
     96     }
     97     case FWL_WGTRELATION_NextSibling: {
     98       pRet = pItem->pNext ? pItem->pNext->pWidget : NULL;
     99       break;
    100     }
    101     case FWL_WGTRELATION_LastSibling: {
    102       pItem = pItem->pNext;
    103       while (pItem && pItem->pNext) {
    104         pItem = pItem->pNext;
    105       }
    106       pRet = pItem ? pItem->pWidget : NULL;
    107       break;
    108     }
    109     case FWL_WGTRELATION_FirstChild: {
    110       pRet = pItem->pChild ? pItem->pChild->pWidget : NULL;
    111       break;
    112     }
    113     case FWL_WGTRELATION_LastChild: {
    114       pItem = pItem->pChild;
    115       while (pItem && pItem->pNext) {
    116         pItem = pItem->pNext;
    117       }
    118       pRet = pItem ? pItem->pWidget : NULL;
    119       break;
    120     }
    121     case FWL_WGTRELATION_SystemForm: {
    122       while (pItem) {
    123         if (IsAbleNative(pItem->pWidget)) {
    124           pRet = pItem->pWidget;
    125           break;
    126         }
    127         pItem = pItem->pParent;
    128       }
    129       break;
    130     }
    131     default: {}
    132   }
    133   return pRet;
    134 }
    135 int32_t CFWL_WidgetMgr::GetWidgetIndex(IFWL_Widget* pWidget) {
    136   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
    137   if (!pItem)
    138     return -1;
    139   return TravelWidgetMgr(pItem->pParent, NULL, pItem);
    140 }
    141 FX_BOOL CFWL_WidgetMgr::SetWidgetIndex(IFWL_Widget* pWidget, int32_t nIndex) {
    142   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
    143   if (!pItem)
    144     return FALSE;
    145   if (!pItem->pParent)
    146     return FALSE;
    147   CFWL_WidgetMgrItem* pChild = pItem->pParent->pChild;
    148   int32_t i = 0;
    149   while (pChild) {
    150     if (pChild == pItem) {
    151       if (i == nIndex) {
    152         return TRUE;
    153       }
    154       if (pChild->pPrevious) {
    155         pChild->pPrevious->pNext = pChild->pNext;
    156       }
    157       if (pChild->pNext) {
    158         pChild->pNext->pPrevious = pChild->pPrevious;
    159       }
    160       if (pItem->pParent->pChild == pItem) {
    161         pItem->pParent->pChild = pItem->pNext;
    162       }
    163       pItem->pNext = NULL;
    164       pItem->pPrevious = NULL;
    165       break;
    166     }
    167     if (!pChild->pNext) {
    168       break;
    169     }
    170     pChild = pChild->pNext;
    171     ++i;
    172   }
    173   pChild = pItem->pParent->pChild;
    174   if (pChild) {
    175     if (nIndex < 0) {
    176       while (pChild->pNext) {
    177         pChild = pChild->pNext;
    178       }
    179       pChild->pNext = pItem;
    180       pItem->pPrevious = pChild;
    181       pItem->pNext = NULL;
    182       return TRUE;
    183     }
    184     i = 0;
    185     while (i < nIndex && pChild->pNext) {
    186       pChild = pChild->pNext;
    187       ++i;
    188     }
    189     if (!pChild->pNext) {
    190       pChild->pNext = pItem;
    191       pItem->pPrevious = pChild;
    192       pItem->pNext = NULL;
    193       return TRUE;
    194     }
    195     if (pChild->pPrevious) {
    196       pItem->pPrevious = pChild->pPrevious;
    197       pChild->pPrevious->pNext = pItem;
    198     }
    199     pChild->pPrevious = pItem;
    200     pItem->pNext = pChild;
    201     if (pItem->pParent->pChild == pChild) {
    202       pItem->pParent->pChild = pItem;
    203     }
    204   } else {
    205     pItem->pParent->pChild = pItem;
    206     pItem->pPrevious = NULL;
    207     pItem->pNext = NULL;
    208   }
    209   return TRUE;
    210 }
    211 FWL_ERR CFWL_WidgetMgr::RepaintWidget(IFWL_Widget* pWidget,
    212                                       const CFX_RectF* pRect) {
    213   if (!m_pAdapter)
    214     return FWL_ERR_Indefinite;
    215   IFWL_Widget* pNative = pWidget;
    216   CFX_RectF rect(*pRect);
    217   if (IsFormDisabled()) {
    218     IFWL_Widget* pOuter = pWidget->GetOuter();
    219     while (pOuter) {
    220       CFX_RectF rtTemp;
    221       pNative->GetWidgetRect(rtTemp);
    222       rect.left += rtTemp.left;
    223       rect.top += rtTemp.top;
    224       pNative = pOuter;
    225       pOuter = pOuter->GetOuter();
    226     }
    227   } else if (!IsAbleNative(pWidget)) {
    228     pNative = GetWidget(pWidget, FWL_WGTRELATION_SystemForm);
    229     if (!pNative)
    230       return FWL_ERR_Indefinite;
    231     pWidget->TransformTo(pNative, rect.left, rect.top);
    232   }
    233   AddRedrawCounts(pNative);
    234   return m_pAdapter->RepaintWidget(pNative, &rect);
    235 }
    236 void CFWL_WidgetMgr::AddWidget(IFWL_Widget* pWidget) {
    237   CFWL_WidgetMgrItem* pParentItem = GetWidgetMgrItem(NULL);
    238   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
    239   if (!pItem) {
    240     pItem = new CFWL_WidgetMgrItem;
    241     pItem->pWidget = pWidget;
    242     m_mapWidgetItem.SetAt(pWidget, pItem);
    243   }
    244   if (pItem->pParent && pItem->pParent != pParentItem) {
    245     if (pItem->pPrevious) {
    246       pItem->pPrevious->pNext = pItem->pNext;
    247     }
    248     if (pItem->pNext) {
    249       pItem->pNext->pPrevious = pItem->pPrevious;
    250     }
    251     if (pItem->pParent->pChild == pItem) {
    252       pItem->pParent->pChild = pItem->pNext;
    253     }
    254   }
    255   pItem->pParent = pParentItem;
    256   SetWidgetIndex(pWidget, -1);
    257 }
    258 void CFWL_WidgetMgr::InsertWidget(IFWL_Widget* pParent,
    259                                   IFWL_Widget* pChild,
    260                                   int32_t nIndex) {
    261   CFWL_WidgetMgrItem* pParentItem = GetWidgetMgrItem(pParent);
    262   if (!pParentItem) {
    263     pParentItem = new CFWL_WidgetMgrItem;
    264     pParentItem->pWidget = pParent;
    265     m_mapWidgetItem.SetAt(pParent, pParentItem);
    266     CFWL_WidgetMgrItem* pRoot = GetWidgetMgrItem(NULL);
    267     pParentItem->pParent = pRoot;
    268     SetWidgetIndex(pParent, -1);
    269   }
    270   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pChild);
    271   if (!pItem) {
    272     pItem = new CFWL_WidgetMgrItem;
    273     pItem->pWidget = pChild;
    274     m_mapWidgetItem.SetAt(pChild, pItem);
    275   }
    276   if (pItem->pParent && pItem->pParent != pParentItem) {
    277     if (pItem->pPrevious) {
    278       pItem->pPrevious->pNext = pItem->pNext;
    279     }
    280     if (pItem->pNext) {
    281       pItem->pNext->pPrevious = pItem->pPrevious;
    282     }
    283     if (pItem->pParent->pChild == pItem) {
    284       pItem->pParent->pChild = pItem->pNext;
    285     }
    286   }
    287   pItem->pParent = pParentItem;
    288   SetWidgetIndex(pChild, nIndex);
    289 }
    290 void CFWL_WidgetMgr::RemoveWidget(IFWL_Widget* pWidget) {
    291   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
    292   if (!pItem) {
    293     return;
    294   }
    295   if (pItem->pPrevious) {
    296     pItem->pPrevious->pNext = pItem->pNext;
    297   }
    298   if (pItem->pNext) {
    299     pItem->pNext->pPrevious = pItem->pPrevious;
    300   }
    301   if (pItem->pParent && pItem->pParent->pChild == pItem) {
    302     pItem->pParent->pChild = pItem->pNext;
    303   }
    304   CFWL_WidgetMgrItem* pChild = pItem->pChild;
    305   while (pChild) {
    306     CFWL_WidgetMgrItem* pNext = pChild->pNext;
    307     RemoveWidget(pChild->pWidget);
    308     pChild = pNext;
    309   }
    310   m_mapWidgetItem.RemoveKey(pWidget);
    311   delete pItem;
    312 }
    313 void CFWL_WidgetMgr::SetOwner(IFWL_Widget* pOwner, IFWL_Widget* pOwned) {
    314   CFWL_WidgetMgrItem* pParentItem = GetWidgetMgrItem(pOwner);
    315   if (!pParentItem) {
    316     pParentItem = new CFWL_WidgetMgrItem;
    317     pParentItem->pWidget = pOwner;
    318     m_mapWidgetItem.SetAt(pOwner, pParentItem);
    319     CFWL_WidgetMgrItem* pRoot = GetWidgetMgrItem(NULL);
    320     pParentItem->pParent = pRoot;
    321     SetWidgetIndex(pOwner, -1);
    322   }
    323   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pOwned);
    324   if (!pItem) {
    325     pItem = new CFWL_WidgetMgrItem;
    326     pItem->pWidget = pOwned;
    327     m_mapWidgetItem.SetAt(pOwned, pItem);
    328   }
    329   pItem->pOwner = pParentItem;
    330 }
    331 void CFWL_WidgetMgr::SetParent(IFWL_Widget* pParent, IFWL_Widget* pChild) {
    332   CFWL_WidgetMgrItem* pParentItem = GetWidgetMgrItem(pParent);
    333   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pChild);
    334   if (!pItem)
    335     return;
    336   if (pItem->pParent && pItem->pParent != pParentItem) {
    337     if (pItem->pPrevious) {
    338       pItem->pPrevious->pNext = pItem->pNext;
    339     }
    340     if (pItem->pNext) {
    341       pItem->pNext->pPrevious = pItem->pPrevious;
    342     }
    343     if (pItem->pParent->pChild == pItem) {
    344       pItem->pParent->pChild = pItem->pNext;
    345     }
    346     pItem->pNext = NULL;
    347     pItem->pPrevious = NULL;
    348   }
    349   pItem->pParent = pParentItem;
    350   SetWidgetIndex(pChild, -1);
    351   if (!m_pAdapter)
    352     return;
    353   m_pAdapter->SetParentWidget(pChild, pParent);
    354 }
    355 FX_BOOL CFWL_WidgetMgr::IsChild(IFWL_Widget* pChild, IFWL_Widget* pParent) {
    356   IFWL_Widget* pTemp = pChild;
    357   do {
    358     if (pTemp == pParent) {
    359       return TRUE;
    360     }
    361     pTemp = GetWidget(pTemp, FWL_WGTRELATION_Parent);
    362   } while (pTemp);
    363   return FALSE;
    364 }
    365 FWL_ERR CFWL_WidgetMgr::CreateWidget_Native(IFWL_Widget* pWidget) {
    366   if (!IsAbleNative(pWidget)) {
    367     return FWL_ERR_Succeeded;
    368   }
    369   return m_pAdapter->CreateWidget(pWidget, pWidget->GetOwner());
    370 }
    371 FWL_ERR CFWL_WidgetMgr::DestroyWidget_Native(IFWL_Widget* pWidget) {
    372   if (!IsAbleNative(pWidget)) {
    373     return FWL_ERR_Succeeded;
    374   }
    375   return m_pAdapter->DestroyWidget(pWidget);
    376 }
    377 FWL_ERR CFWL_WidgetMgr::GetWidgetRect_Native(IFWL_Widget* pWidget,
    378                                              CFX_RectF& rect) {
    379   if (!IsAbleNative(pWidget)) {
    380     return FWL_ERR_Succeeded;
    381   }
    382   return m_pAdapter->GetWidgetRect(pWidget, rect);
    383 }
    384 FWL_ERR CFWL_WidgetMgr::SetWidgetRect_Native(IFWL_Widget* pWidget,
    385                                              const CFX_RectF& rect) {
    386   if (FWL_UseOffscreen(pWidget)) {
    387     CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
    388     pItem->iRedrawCounter++;
    389     if (pItem->pOffscreen) {
    390       CFX_RenderDevice* pDevice = pItem->pOffscreen->GetRenderDevice();
    391       if (pDevice && pDevice->GetBitmap()) {
    392         CFX_DIBitmap* pBitmap = pDevice->GetBitmap();
    393         if (pBitmap->GetWidth() - rect.width > 1 ||
    394             pBitmap->GetHeight() - rect.height > 1) {
    395           delete pItem->pOffscreen;
    396           pItem->pOffscreen = NULL;
    397         }
    398       }
    399     }
    400 #if (_FX_OS_ == _FX_WIN32_DESKTOP_) || (_FX_OS_ == _FX_WIN64_)
    401     pItem->bOutsideChanged = !m_rtScreen.Contains(rect);
    402 #endif
    403   }
    404   return m_pAdapter->SetWidgetRect(pWidget, rect);
    405 }
    406 FWL_ERR CFWL_WidgetMgr::SetWidgetPosition_Native(IFWL_Widget* pWidget,
    407                                                  FX_FLOAT fx,
    408                                                  FX_FLOAT fy) {
    409   return m_pAdapter->SetWidgetPosition(pWidget, fx, fy);
    410 }
    411 FWL_ERR CFWL_WidgetMgr::SetWidgetIcon_Native(IFWL_Widget* pWidget,
    412                                              const CFX_DIBitmap* pIcon,
    413                                              FX_BOOL bBig) {
    414   return m_pAdapter->SetWidgetIcon(pWidget, pIcon, bBig);
    415 }
    416 FWL_ERR CFWL_WidgetMgr::SetWidgetCaption_Native(
    417     IFWL_Widget* pWidget,
    418     const CFX_WideStringC& wsCaption) {
    419   return m_pAdapter->SetWidgetCaption(pWidget, wsCaption);
    420 }
    421 FWL_ERR CFWL_WidgetMgr::SetBorderRegion_Native(IFWL_Widget* pWidget,
    422                                                CFX_Path* pPath) {
    423   return m_pAdapter->SetBorderRegion(pWidget, pPath);
    424 }
    425 FWL_ERR CFWL_WidgetMgr::ShowWidget_Native(IFWL_Widget* pWidget) {
    426   return m_pAdapter->ShowWidget(pWidget);
    427 }
    428 FWL_ERR CFWL_WidgetMgr::HideWidget_Native(IFWL_Widget* pWidget) {
    429   return m_pAdapter->HideWidget(pWidget);
    430 }
    431 FWL_ERR CFWL_WidgetMgr::SetNormal_Native(IFWL_Widget* pWidget) {
    432   return m_pAdapter->SetNormal(pWidget);
    433 }
    434 FWL_ERR CFWL_WidgetMgr::SetMaximize_Native(IFWL_Widget* pWidget) {
    435   return m_pAdapter->SetMaximize(pWidget);
    436 }
    437 FWL_ERR CFWL_WidgetMgr::SetMinimize_Native(IFWL_Widget* pWidget) {
    438   return m_pAdapter->SetMinimize(pWidget);
    439 }
    440 FX_BOOL CFWL_WidgetMgr::CheckMessage_Native() {
    441   return m_pAdapter->CheckMessage();
    442 }
    443 FWL_ERR CFWL_WidgetMgr::DispatchMessage_Native() {
    444   return m_pAdapter->DispatchMessage();
    445 }
    446 FX_BOOL CFWL_WidgetMgr::IsIdleMessage_Native() {
    447   return m_pAdapter->IsIdleMessage();
    448 }
    449 FWL_ERR CFWL_WidgetMgr::Exit_Native(int32_t iExitCode) {
    450   return m_pAdapter->Exit(iExitCode);
    451 }
    452 FWL_ERR CFWL_WidgetMgr::CreateWidgetWithNativeId_Native(IFWL_Widget* pWidget,
    453                                                         void* vp) {
    454   return m_pAdapter->CreateWidgetWithNativeId(pWidget, vp);
    455 }
    456 IFWL_Widget* CFWL_WidgetMgr::GetWidgetAtPoint(IFWL_Widget* parent,
    457                                               FX_FLOAT x,
    458                                               FX_FLOAT y) {
    459   if (!parent)
    460     return NULL;
    461   FX_FLOAT x1;
    462   FX_FLOAT y1;
    463   IFWL_Widget* child = GetWidget(parent, FWL_WGTRELATION_LastChild);
    464   while (child) {
    465     if ((child->GetStates() & FWL_WGTSTATE_Invisible) == 0) {
    466       x1 = x;
    467       y1 = y;
    468       CFX_Matrix matrixOnParent;
    469       child->GetMatrix(matrixOnParent);
    470       CFX_Matrix m;
    471       m.SetIdentity();
    472       m.SetReverse(matrixOnParent);
    473       m.TransformPoint(x1, y1);
    474       CFX_RectF bounds;
    475       child->GetWidgetRect(bounds);
    476       if (bounds.Contains(x1, y1)) {
    477         x1 -= bounds.left;
    478         y1 -= bounds.top;
    479         return GetWidgetAtPoint(child, x1, y1);
    480       }
    481     }
    482     child = GetWidget(child, FWL_WGTRELATION_PriorSibling);
    483   }
    484   return parent;
    485 }
    486 void CFWL_WidgetMgr::NotifySizeChanged(IFWL_Widget* pForm,
    487                                        FX_FLOAT fx,
    488                                        FX_FLOAT fy) {
    489   if (!FWL_UseOffscreen(pForm)) {
    490     return;
    491   }
    492   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pForm);
    493   if (pItem->pOffscreen) {
    494     delete pItem->pOffscreen;
    495     pItem->pOffscreen = NULL;
    496   }
    497 }
    498 IFWL_Widget* CFWL_WidgetMgr::nextTab(IFWL_Widget* parent,
    499                                      IFWL_Widget* focus,
    500                                      FX_BOOL& bFind) {
    501   IFWL_Widget* child =
    502       FWL_GetWidgetMgr()->GetWidget(parent, FWL_WGTRELATION_FirstChild);
    503   while (child) {
    504     if (focus == child) {
    505       bFind = TRUE;
    506     }
    507     if ((child->GetStyles() & FWL_WGTSTYLE_TabStop) &&
    508         (!focus || (focus != child && bFind))) {
    509       return child;
    510     }
    511     IFWL_Widget* bRet = nextTab(child, focus, bFind);
    512     if (bRet) {
    513       return bRet;
    514     }
    515     child = FWL_GetWidgetMgr()->GetWidget(child, FWL_WGTRELATION_NextSibling);
    516   }
    517   return NULL;
    518 }
    519 int32_t CFWL_WidgetMgr::CountRadioButtonGroup(IFWL_Widget* pFirst) {
    520   int32_t iRet = 0;
    521   IFWL_Widget* pChild = pFirst;
    522   while (pChild) {
    523     if ((pChild->GetStyles() & FWL_WGTSTYLE_Group) &&
    524         pChild->GetClassID() == 3811304691) {
    525       iRet++;
    526     }
    527     pChild = GetWidget(pChild, FWL_WGTRELATION_NextSibling);
    528   }
    529   return iRet;
    530 }
    531 IFWL_Widget* CFWL_WidgetMgr::GetSiblingRadioButton(IFWL_Widget* pWidget,
    532                                                    FX_BOOL bNext) {
    533   while ((pWidget = GetWidget(pWidget, bNext ? FWL_WGTRELATION_NextSibling
    534                                              : FWL_WGTRELATION_PriorSibling)) !=
    535          NULL) {
    536     if (pWidget->GetClassID() == 3811304691) {
    537       return pWidget;
    538     }
    539   }
    540   return NULL;
    541 }
    542 IFWL_Widget* CFWL_WidgetMgr::GetRadioButtonGroupHeader(
    543     IFWL_Widget* pRadioButton) {
    544   if (pRadioButton->GetStyles() & FWL_WGTSTYLE_Group) {
    545     return pRadioButton;
    546   }
    547   IFWL_Widget* pNext = pRadioButton;
    548   while ((pNext = GetSiblingRadioButton(pNext, FALSE)) != NULL) {
    549     if (pNext->GetStyles() & FWL_WGTSTYLE_Group) {
    550       return pNext;
    551     }
    552   }
    553   pNext = GetWidget(pRadioButton, FWL_WGTRELATION_LastSibling);
    554   if ((pNext->GetStyles() & FWL_WGTSTYLE_Group) &&
    555       pNext->GetClassID() == 3811304691) {
    556     return pNext;
    557   }
    558   while ((pNext = GetSiblingRadioButton(pNext, FALSE)) && pNext &&
    559          pNext != pRadioButton) {
    560     if (pNext->GetStyles() & FWL_WGTSTYLE_Group) {
    561       return pNext;
    562     }
    563   }
    564   pNext = GetWidget(pRadioButton, FWL_WGTRELATION_FirstSibling);
    565   if (pNext && (pNext->GetStyles() == FWL_WGTSTYLE_Group) &&
    566       pNext->GetClassID() == 3811304691) {
    567     return pNext;
    568   }
    569   return GetSiblingRadioButton(pNext, TRUE);
    570 }
    571 void CFWL_WidgetMgr::GetSameGroupRadioButton(IFWL_Widget* pRadioButton,
    572                                              CFX_PtrArray& group) {
    573   IFWL_Widget* pFirst = GetWidget(pRadioButton, FWL_WGTRELATION_FirstSibling);
    574   if (!pFirst) {
    575     pFirst = pRadioButton;
    576   }
    577   int32_t iGroup = CountRadioButtonGroup(pFirst);
    578   if (iGroup < 2) {
    579     if (pFirst->GetClassID() == 3811304691) {
    580       group.Add(pFirst);
    581     }
    582     IFWL_Widget* pNext = pFirst;
    583     while ((pNext = GetSiblingRadioButton(pNext, TRUE)) != NULL) {
    584       group.Add(pNext);
    585     }
    586     return;
    587   }
    588   IFWL_Widget* pNext = GetRadioButtonGroupHeader(pRadioButton);
    589   do {
    590     group.Add(pNext);
    591     pNext = GetSiblingRadioButton(pNext, TRUE);
    592     if (!pNext) {
    593       if (pFirst->GetClassID() == 3811304691) {
    594         pNext = pFirst;
    595       } else {
    596         pNext = GetSiblingRadioButton(pFirst, TRUE);
    597       }
    598     }
    599   } while (pNext && ((pNext->GetStyles() & FWL_WGTSTYLE_Group) == 0));
    600 }
    601 IFWL_Widget* CFWL_WidgetMgr::GetDefaultButton(IFWL_Widget* pParent) {
    602   if ((pParent->GetClassID() == 3521614244) &&
    603       (pParent->GetStates() & (1 << (FWL_WGTSTATE_MAX + 2)))) {
    604     return pParent;
    605   }
    606   IFWL_Widget* child =
    607       FWL_GetWidgetMgr()->GetWidget(pParent, FWL_WGTRELATION_FirstChild);
    608   while (child) {
    609     if ((child->GetClassID() == 3521614244) &&
    610         (child->GetStates() & (1 << (FWL_WGTSTATE_MAX + 2)))) {
    611       return child;
    612     }
    613     IFWL_Widget* find = GetDefaultButton(child);
    614     if (find) {
    615       return find;
    616     }
    617     child = FWL_GetWidgetMgr()->GetWidget(child, FWL_WGTRELATION_NextSibling);
    618   }
    619   return NULL;
    620 }
    621 void CFWL_WidgetMgr::AddRedrawCounts(IFWL_Widget* pWidget) {
    622   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
    623   (pItem->iRedrawCounter)++;
    624 }
    625 void CFWL_WidgetMgr::ResetRedrawCounts(IFWL_Widget* pWidget) {
    626   CFWL_WidgetMgrItem* pItem = GetWidgetMgrItem(pWidget);
    627   pItem->iRedrawCounter = 0;
    628 }
    629 CFWL_WidgetMgrItem* CFWL_WidgetMgr::GetWidgetMgrItem(IFWL_Widget* pWidget) {
    630   return static_cast<CFWL_WidgetMgrItem*>(m_mapWidgetItem.GetValueAt(pWidget));
    631 }
    632 int32_t CFWL_WidgetMgr::TravelWidgetMgr(CFWL_WidgetMgrItem* pParent,
    633                                         int32_t* pIndex,
    634                                         CFWL_WidgetMgrItem* pItem,
    635                                         IFWL_Widget** pWidget) {
    636   if (!pParent) {
    637     return 0;
    638   }
    639   int32_t iCount = 0;
    640   CFWL_WidgetMgrItem* pChild = pParent->pChild;
    641   while (pChild) {
    642     iCount++;
    643     if (pIndex) {
    644       if (*pIndex == 0) {
    645         *pWidget = pChild->pWidget;
    646         return iCount;
    647       }
    648       pIndex--;
    649     }
    650     if (pItem && pItem == pChild) {
    651       return iCount - 1;
    652     }
    653     pChild = pChild->pNext;
    654   }
    655   if (pIndex) {
    656     return 0;
    657   } else if (pItem) {
    658     return -1;
    659   }
    660   return iCount - 1;
    661 }
    662 FX_BOOL CFWL_WidgetMgr::IsAbleNative(IFWL_Widget* pWidget) {
    663   if (!pWidget)
    664     return FALSE;
    665   if (!pWidget->IsInstance(FX_WSTRC(FWL_CLASS_Form))) {
    666     return FALSE;
    667   }
    668   FX_DWORD dwStyles = pWidget->GetStyles();
    669   return ((dwStyles & FWL_WGTSTYLE_WindowTypeMask) ==
    670           FWL_WGTSTYLE_OverLapper) ||
    671          (dwStyles & FWL_WGTSTYLE_Popup);
    672 }
    673 FX_BOOL CFWL_WidgetMgr::IsThreadEnabled() {
    674   return !(m_dwCapability & FWL_WGTMGR_DisableThread);
    675 }
    676 FX_BOOL CFWL_WidgetMgr::IsFormDisabled() {
    677   return m_dwCapability & FWL_WGTMGR_DisableForm;
    678 }
    679 FX_BOOL CFWL_WidgetMgr::GetAdapterPopupPos(IFWL_Widget* pWidget,
    680                                            FX_FLOAT fMinHeight,
    681                                            FX_FLOAT fMaxHeight,
    682                                            const CFX_RectF& rtAnchor,
    683                                            CFX_RectF& rtPopup) {
    684   IFWL_AdapterWidgetMgr* pSDApapter = GetAdapterWidgetMgr();
    685   return pSDApapter->GetPopupPos(pWidget, fMinHeight, fMaxHeight, rtAnchor,
    686                                  rtPopup);
    687 }
    688 CFWL_WidgetMgrDelegate::CFWL_WidgetMgrDelegate(CFWL_WidgetMgr* pWidgetMgr)
    689     : m_pWidgetMgr(pWidgetMgr) {}
    690 FWL_ERR CFWL_WidgetMgrDelegate::OnSetCapability(FX_DWORD dwCapability) {
    691   m_pWidgetMgr->m_dwCapability = dwCapability;
    692   return FWL_ERR_Succeeded;
    693 }
    694 int32_t CFWL_WidgetMgrDelegate::OnProcessMessageToForm(CFWL_Message* pMessage) {
    695   if (!pMessage)
    696     return 0;
    697   if (!pMessage->m_pDstTarget)
    698     return 0;
    699   IFWL_Widget* pDstWidget = pMessage->m_pDstTarget;
    700   IFWL_NoteThread* pNoteThread = pDstWidget->GetOwnerThread();
    701   if (!pNoteThread)
    702     return 0;
    703   CFWL_NoteDriver* pNoteDriver =
    704       static_cast<CFWL_NoteDriver*>(pNoteThread->GetNoteDriver());
    705   if (!pNoteDriver)
    706     return 0;
    707   if (m_pWidgetMgr->IsThreadEnabled()) {
    708     pMessage = static_cast<CFWL_Message*>(pMessage->Clone());
    709   }
    710   if (m_pWidgetMgr->IsFormDisabled()) {
    711     pNoteDriver->ProcessMessage(pMessage);
    712   } else {
    713     pNoteDriver->QueueMessage(pMessage);
    714   }
    715 #if (_FX_OS_ == _FX_MACOSX_)
    716   CFWL_NoteLoop* pTopLoop = pNoteDriver->GetTopLoop();
    717   if (pTopLoop) {
    718     pNoteDriver->UnqueueMessage(pTopLoop);
    719   }
    720 #endif
    721   if (m_pWidgetMgr->IsThreadEnabled()) {
    722     pMessage->Release();
    723   }
    724   return FWL_ERR_Succeeded;
    725 }
    726 FWL_ERR CFWL_WidgetMgrDelegate::OnDrawWidget(IFWL_Widget* pWidget,
    727                                              CFX_Graphics* pGraphics,
    728                                              const CFX_Matrix* pMatrix) {
    729   if (!pWidget)
    730     return FWL_ERR_Indefinite;
    731   if (!pGraphics)
    732     return FWL_ERR_Indefinite;
    733   CFX_Graphics* pTemp = DrawWidgetBefore(pWidget, pGraphics, pMatrix);
    734   CFX_RectF clipCopy;
    735   pWidget->GetWidgetRect(clipCopy);
    736   clipCopy.left = clipCopy.top = 0;
    737   if (bUseOffscreenDirect(pWidget)) {
    738     DrawWidgetAfter(pWidget, pGraphics, clipCopy, pMatrix);
    739     return FWL_ERR_Succeeded;
    740   }
    741   CFX_RectF clipBounds;
    742 #if (_FX_OS_ == _FX_WIN32_DESKTOP_) || (_FX_OS_ == _FX_WIN64_) || \
    743     (_FX_OS_ == _FX_LINUX_DESKTOP_) || (_FX_OS_ == _FX_ANDROID_)
    744   IFWL_WidgetDelegate* pDelegate = pWidget->SetDelegate(NULL);
    745   pDelegate->OnDrawWidget(pTemp, pMatrix);
    746   pGraphics->GetClipRect(clipBounds);
    747   clipCopy = clipBounds;
    748 #elif(_FX_OS_ == _FX_MACOSX_)
    749   if (m_pWidgetMgr->IsFormDisabled()) {
    750     IFWL_WidgetDelegate* pDelegate = pWidget->SetDelegate(NULL);
    751     pDelegate->OnDrawWidget(pTemp, pMatrix);
    752     pGraphics->GetClipRect(clipBounds);
    753     clipCopy = clipBounds;
    754   } else {
    755     clipBounds.Set(pMatrix->a, pMatrix->b, pMatrix->c, pMatrix->d);
    756     const_cast<CFX_Matrix*>(pMatrix)->SetIdentity();  // FIXME: const cast.
    757 #ifdef FWL_UseMacSystemBorder
    758 #else
    759 #endif
    760     {
    761       IFWL_WidgetDelegate* pDelegate = pWidget->SetDelegate(NULL);
    762       pDelegate->OnDrawWidget(pTemp, pMatrix);
    763     }
    764   }
    765 #endif
    766   if (!m_pWidgetMgr->IsFormDisabled()) {
    767     CFX_RectF rtClient;
    768     pWidget->GetClientRect(rtClient);
    769     clipBounds.Intersect(rtClient);
    770   }
    771   if (!clipBounds.IsEmpty()) {
    772     DrawChild(pWidget, clipBounds, pTemp, pMatrix);
    773   }
    774   DrawWidgetAfter(pWidget, pGraphics, clipCopy, pMatrix);
    775   m_pWidgetMgr->ResetRedrawCounts(pWidget);
    776   return FWL_ERR_Succeeded;
    777 }
    778 void CFWL_WidgetMgrDelegate::DrawChild(IFWL_Widget* parent,
    779                                        const CFX_RectF& rtClip,
    780                                        CFX_Graphics* pGraphics,
    781                                        const CFX_Matrix* pMatrix) {
    782   if (!parent)
    783     return;
    784   FX_BOOL bFormDisable = m_pWidgetMgr->IsFormDisabled();
    785   IFWL_Widget* pNextChild =
    786       m_pWidgetMgr->GetWidget(parent, FWL_WGTRELATION_FirstChild);
    787   while (pNextChild) {
    788     IFWL_Widget* child = pNextChild;
    789     pNextChild = m_pWidgetMgr->GetWidget(child, FWL_WGTRELATION_NextSibling);
    790     if (child->GetStates() & FWL_WGTSTATE_Invisible) {
    791       continue;
    792     }
    793     CFX_RectF rtWidget;
    794     child->GetWidgetRect(rtWidget);
    795     if (rtWidget.IsEmpty()) {
    796       continue;
    797     }
    798     CFX_Matrix widgetMatrix;
    799     CFX_RectF clipBounds(rtWidget);
    800     if (!bFormDisable) {
    801       child->GetMatrix(widgetMatrix, TRUE);
    802     }
    803     if (pMatrix) {
    804       widgetMatrix.Concat(*pMatrix);
    805     }
    806     if (!bFormDisable) {
    807       widgetMatrix.TransformPoint(clipBounds.left, clipBounds.top);
    808       clipBounds.Intersect(rtClip);
    809       if (clipBounds.IsEmpty()) {
    810         continue;
    811       }
    812       pGraphics->SaveGraphState();
    813       pGraphics->SetClipRect(clipBounds);
    814     }
    815     widgetMatrix.Translate(rtWidget.left, rtWidget.top, TRUE);
    816     IFWL_WidgetDelegate* pDelegate = child->SetDelegate(NULL);
    817     if (pDelegate) {
    818       if (m_pWidgetMgr->IsFormDisabled() ||
    819           IsNeedRepaint(child, &widgetMatrix, rtClip)) {
    820         pDelegate->OnDrawWidget(pGraphics, &widgetMatrix);
    821       }
    822     }
    823     if (!bFormDisable) {
    824       pGraphics->RestoreGraphState();
    825     }
    826     DrawChild(child, clipBounds, pGraphics,
    827               bFormDisable ? &widgetMatrix : pMatrix);
    828     child = m_pWidgetMgr->GetWidget(child, FWL_WGTRELATION_NextSibling);
    829   }
    830 }
    831 CFX_Graphics* CFWL_WidgetMgrDelegate::DrawWidgetBefore(
    832     IFWL_Widget* pWidget,
    833     CFX_Graphics* pGraphics,
    834     const CFX_Matrix* pMatrix) {
    835   if (!FWL_UseOffscreen(pWidget)) {
    836     return pGraphics;
    837   }
    838   CFWL_WidgetMgrItem* pItem = m_pWidgetMgr->GetWidgetMgrItem(pWidget);
    839   if (!pItem->pOffscreen) {
    840     pItem->pOffscreen = new CFX_Graphics;
    841     CFX_RectF rect;
    842     pWidget->GetWidgetRect(rect);
    843     pItem->pOffscreen->Create((int32_t)rect.width, (int32_t)rect.height,
    844                               FXDIB_Argb);
    845   }
    846   CFX_RectF rect;
    847   pGraphics->GetClipRect(rect);
    848   pItem->pOffscreen->SetClipRect(rect);
    849   return pItem->pOffscreen;
    850 }
    851 void CFWL_WidgetMgrDelegate::DrawWidgetAfter(IFWL_Widget* pWidget,
    852                                              CFX_Graphics* pGraphics,
    853                                              CFX_RectF& rtClip,
    854                                              const CFX_Matrix* pMatrix) {
    855   if (FWL_UseOffscreen(pWidget)) {
    856     CFWL_WidgetMgrItem* pItem = m_pWidgetMgr->GetWidgetMgrItem(pWidget);
    857     pGraphics->Transfer(pItem->pOffscreen, rtClip.left, rtClip.top, rtClip,
    858                         pMatrix);
    859 #ifdef _WIN32
    860     pItem->pOffscreen->ClearClip();
    861 #endif
    862   }
    863   CFWL_WidgetMgrItem* pItem = m_pWidgetMgr->GetWidgetMgrItem(pWidget);
    864   pItem->iRedrawCounter = 0;
    865 }
    866 #define FWL_NEEDREPAINTHIT_Point 12
    867 #define FWL_NEEDREPAINTHIT_Piece 3
    868 typedef struct _FWL_NeedRepaintHitData {
    869   CFX_PointF hitPoint;
    870   FX_BOOL bNotNeedRepaint;
    871   FX_BOOL bNotContainByDirty;
    872 } FWL_NeedRepaintHitData;
    873 FX_BOOL CFWL_WidgetMgrDelegate::IsNeedRepaint(IFWL_Widget* pWidget,
    874                                               CFX_Matrix* pMatrix,
    875                                               const CFX_RectF& rtDirty) {
    876   CFWL_WidgetMgrItem* pItem = m_pWidgetMgr->GetWidgetMgrItem(pWidget);
    877   if (pItem && pItem->iRedrawCounter > 0) {
    878     pItem->iRedrawCounter = 0;
    879     return TRUE;
    880   }
    881   CFX_RectF rtWidget;
    882   pWidget->GetWidgetRect(rtWidget);
    883   rtWidget.left = rtWidget.top = 0;
    884   pMatrix->TransformRect(rtWidget);
    885   if (!rtWidget.IntersectWith(rtDirty)) {
    886     return FALSE;
    887   }
    888   IFWL_Widget* pChild =
    889       FWL_GetWidgetMgr()->GetWidget(pWidget, FWL_WGTRELATION_FirstChild);
    890   if (!pChild) {
    891     return TRUE;
    892   }
    893   if (pChild->GetClassID() == 3150298670) {
    894     CFX_RectF rtTemp;
    895     pChild->GetWidgetRect(rtTemp);
    896     if (rtTemp.width >= rtWidget.width && rtTemp.height >= rtWidget.height) {
    897       pChild =
    898           FWL_GetWidgetMgr()->GetWidget(pChild, FWL_WGTRELATION_FirstChild);
    899       if (!pChild) {
    900         return TRUE;
    901       }
    902     }
    903   }
    904   CFX_RectF rtChilds;
    905   rtChilds.Empty();
    906   FX_BOOL bChildIntersectWithDirty = FALSE;
    907   FX_BOOL bOrginPtIntersectWidthChild = FALSE;
    908   FX_BOOL bOrginPtIntersectWidthDirty =
    909       rtDirty.Contains(rtWidget.left, rtWidget.top);
    910   static FWL_NeedRepaintHitData hitPoint[FWL_NEEDREPAINTHIT_Point];
    911   static int32_t iSize = sizeof(FWL_NeedRepaintHitData);
    912   FXSYS_memset(hitPoint, 0, iSize);
    913   FX_FLOAT fxPiece = rtWidget.width / FWL_NEEDREPAINTHIT_Piece;
    914   FX_FLOAT fyPiece = rtWidget.height / FWL_NEEDREPAINTHIT_Piece;
    915   hitPoint[2].hitPoint.x = hitPoint[6].hitPoint.x = rtWidget.left;
    916   hitPoint[0].hitPoint.x = hitPoint[3].hitPoint.x = hitPoint[7].hitPoint.x =
    917       hitPoint[10].hitPoint.x = fxPiece + rtWidget.left;
    918   hitPoint[1].hitPoint.x = hitPoint[4].hitPoint.x = hitPoint[8].hitPoint.x =
    919       hitPoint[11].hitPoint.x = fxPiece * 2 + rtWidget.left;
    920   hitPoint[5].hitPoint.x = hitPoint[9].hitPoint.x =
    921       rtWidget.width + rtWidget.left;
    922   hitPoint[0].hitPoint.y = hitPoint[1].hitPoint.y = rtWidget.top;
    923   hitPoint[2].hitPoint.y = hitPoint[3].hitPoint.y = hitPoint[4].hitPoint.y =
    924       hitPoint[5].hitPoint.y = fyPiece + rtWidget.top;
    925   hitPoint[6].hitPoint.y = hitPoint[7].hitPoint.y = hitPoint[8].hitPoint.y =
    926       hitPoint[9].hitPoint.y = fyPiece * 2 + rtWidget.top;
    927   hitPoint[10].hitPoint.y = hitPoint[11].hitPoint.y =
    928       rtWidget.height + rtWidget.top;
    929   do {
    930     CFX_RectF rect;
    931     pChild->GetWidgetRect(rect);
    932     CFX_RectF r = rect;
    933     r.left += rtWidget.left;
    934     r.top += rtWidget.top;
    935     if (r.IsEmpty()) {
    936       continue;
    937     }
    938     if (r.Contains(rtDirty)) {
    939       return FALSE;
    940     }
    941     if (!bChildIntersectWithDirty && r.IntersectWith(rtDirty)) {
    942       bChildIntersectWithDirty = TRUE;
    943     }
    944     if (bOrginPtIntersectWidthDirty && !bOrginPtIntersectWidthChild) {
    945       bOrginPtIntersectWidthChild = rect.Contains(0, 0);
    946     }
    947     if (rtChilds.IsEmpty()) {
    948       rtChilds = rect;
    949     } else if (!(pChild->GetStates() & FWL_WGTSTATE_Invisible)) {
    950       rtChilds.Union(rect);
    951     }
    952     for (int32_t i = 0; i < FWL_NEEDREPAINTHIT_Point; i++) {
    953       if (hitPoint[i].bNotContainByDirty || hitPoint[i].bNotNeedRepaint) {
    954         continue;
    955       }
    956       if (!rtDirty.Contains(hitPoint[i].hitPoint)) {
    957         hitPoint[i].bNotContainByDirty = TRUE;
    958         continue;
    959       }
    960       if (r.Contains(hitPoint[i].hitPoint)) {
    961         hitPoint[i].bNotNeedRepaint = TRUE;
    962       }
    963     }
    964   } while ((pChild = FWL_GetWidgetMgr()->GetWidget(
    965                 pChild, FWL_WGTRELATION_NextSibling)) != NULL);
    966   if (!bChildIntersectWithDirty) {
    967     return TRUE;
    968   }
    969   if (bOrginPtIntersectWidthDirty && !bOrginPtIntersectWidthChild) {
    970     return TRUE;
    971   }
    972   if (rtChilds.IsEmpty()) {
    973     return TRUE;
    974   }
    975   int32_t repaintPoint = FWL_NEEDREPAINTHIT_Point;
    976   for (int32_t i = 0; i < FWL_NEEDREPAINTHIT_Point; i++) {
    977     if (hitPoint[i].bNotNeedRepaint) {
    978       repaintPoint--;
    979     }
    980   }
    981   if (repaintPoint > 0) {
    982     return TRUE;
    983   }
    984   pMatrix->TransformRect(rtChilds);
    985   if (rtChilds.Contains(rtDirty) || rtChilds.Contains(rtWidget)) {
    986     return FALSE;
    987   }
    988   return TRUE;
    989 }
    990 FX_BOOL CFWL_WidgetMgrDelegate::bUseOffscreenDirect(IFWL_Widget* pWidget) {
    991   CFWL_WidgetMgrItem* pItem = m_pWidgetMgr->GetWidgetMgrItem(pWidget);
    992   if (!FWL_UseOffscreen(pWidget) || !(pItem->pOffscreen)) {
    993     return FALSE;
    994   }
    995 #if (_FX_OS_ == _FX_WIN32_DESKTOP_) || (_FX_OS_ == _FX_WIN64_)
    996   if (pItem->bOutsideChanged) {
    997     CFX_RectF r;
    998     pWidget->GetWidgetRect(r);
    999     CFX_RectF temp(m_pWidgetMgr->m_rtScreen);
   1000     temp.Deflate(50, 50);
   1001     if (!temp.Contains(r)) {
   1002       return FALSE;
   1003     }
   1004     pItem->bOutsideChanged = FALSE;
   1005   }
   1006 #endif
   1007   return pItem->iRedrawCounter == 0;
   1008 }
   1009 static void FWL_WriteBMP(CFX_DIBitmap* pBitmap, const FX_CHAR* filename) {
   1010   FILE* file = fopen(filename, "wb");
   1011   if (file == NULL) {
   1012     return;
   1013   }
   1014   int size = 14 + 40 + pBitmap->GetPitch() * pBitmap->GetHeight();
   1015   unsigned char buffer[40];
   1016   buffer[0] = 'B';
   1017   buffer[1] = 'M';
   1018   buffer[2] = (unsigned char)size;
   1019   buffer[3] = (unsigned char)(size >> 8);
   1020   buffer[4] = (unsigned char)(size >> 16);
   1021   buffer[5] = (unsigned char)(size >> 24);
   1022   buffer[6] = buffer[7] = buffer[8] = buffer[9] = 0;
   1023   buffer[10] = 54;
   1024   buffer[11] = buffer[12] = buffer[13] = 0;
   1025   fwrite(buffer, 14, 1, file);
   1026   memset(buffer, 0, 40);
   1027   buffer[0] = 40;
   1028   buffer[4] = (unsigned char)pBitmap->GetWidth();
   1029   buffer[5] = (unsigned char)(pBitmap->GetWidth() >> 8);
   1030   buffer[6] = (unsigned char)(pBitmap->GetWidth() >> 16);
   1031   buffer[7] = (unsigned char)(pBitmap->GetWidth() >> 24);
   1032   buffer[8] = (unsigned char)(-pBitmap->GetHeight());
   1033   buffer[9] = (unsigned char)((-pBitmap->GetHeight()) >> 8);
   1034   buffer[10] = (unsigned char)((-pBitmap->GetHeight()) >> 16);
   1035   buffer[11] = (unsigned char)((-pBitmap->GetHeight()) >> 24);
   1036   buffer[12] = 1;
   1037   buffer[14] = pBitmap->GetBPP();
   1038   fwrite(buffer, 40, 1, file);
   1039   for (int row = 0; row < pBitmap->GetHeight(); row++) {
   1040     uint8_t* scan_line = pBitmap->GetBuffer() + row * pBitmap->GetPitch();
   1041     fwrite(scan_line, pBitmap->GetPitch(), 1, file);
   1042   }
   1043   fclose(file);
   1044 }
   1045 FWL_ERR FWL_WidgetMgrSnapshot(IFWL_Widget* pWidget,
   1046                               const CFX_WideString* saveFile,
   1047                               const CFX_Matrix* pMatrix) {
   1048   CFX_RectF r;
   1049   pWidget->GetWidgetRect(r);
   1050   CFX_Graphics gs;
   1051   gs.Create((int32_t)r.width, (int32_t)r.height, FXDIB_Argb);
   1052   CFWL_WidgetMgr* widgetMgr = static_cast<CFWL_WidgetMgr*>(FWL_GetWidgetMgr());
   1053   CFWL_WidgetMgrDelegate* delegate = widgetMgr->GetDelegate();
   1054   delegate->OnDrawWidget(pWidget, &gs, pMatrix);
   1055   CFX_DIBitmap* dib = gs.GetRenderDevice()->GetBitmap();
   1056   FWL_WriteBMP(dib, saveFile->UTF8Encode());
   1057   return FWL_ERR_Succeeded;
   1058 }
   1059 FX_BOOL FWL_WidgetIsChild(IFWL_Widget* parent, IFWL_Widget* find) {
   1060   if (!find) {
   1061     return FALSE;
   1062   }
   1063   IFWL_Widget* child =
   1064       FWL_GetWidgetMgr()->GetWidget(parent, FWL_WGTRELATION_FirstChild);
   1065   while (child) {
   1066     if (child == find) {
   1067       return TRUE;
   1068     }
   1069     if (FWL_WidgetIsChild(child, find)) {
   1070       return TRUE;
   1071     }
   1072     child = FWL_GetWidgetMgr()->GetWidget(child, FWL_WGTRELATION_NextSibling);
   1073   }
   1074   return FALSE;
   1075 }
   1076