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/fwl/cfwl_notedriver.h"
8
9#include <algorithm>
10#include <utility>
11
12#include "core/fxcrt/fx_extension.h"
13#include "third_party/base/ptr_util.h"
14#include "third_party/base/stl_util.h"
15#include "xfa/fwl/cfwl_app.h"
16#include "xfa/fwl/cfwl_eventtarget.h"
17#include "xfa/fwl/cfwl_form.h"
18#include "xfa/fwl/cfwl_messagekey.h"
19#include "xfa/fwl/cfwl_messagekillfocus.h"
20#include "xfa/fwl/cfwl_messagemouse.h"
21#include "xfa/fwl/cfwl_messagemousewheel.h"
22#include "xfa/fwl/cfwl_messagesetfocus.h"
23#include "xfa/fwl/cfwl_noteloop.h"
24#include "xfa/fwl/cfwl_widgetmgr.h"
25
26CFWL_NoteDriver::CFWL_NoteDriver()
27    : m_pHover(nullptr),
28      m_pFocus(nullptr),
29      m_pGrab(nullptr),
30      m_pNoteLoop(pdfium::MakeUnique<CFWL_NoteLoop>()) {
31  PushNoteLoop(m_pNoteLoop.get());
32}
33
34CFWL_NoteDriver::~CFWL_NoteDriver() {}
35
36void CFWL_NoteDriver::SendEvent(CFWL_Event* pNote) {
37  for (const auto& pair : m_eventTargets) {
38    if (pair.second->IsValid())
39      pair.second->ProcessEvent(pNote);
40  }
41}
42
43void CFWL_NoteDriver::RegisterEventTarget(CFWL_Widget* pListener,
44                                          CFWL_Widget* pEventSource) {
45  uint32_t key = pListener->GetEventKey();
46  if (key == 0) {
47    do {
48      key = rand();
49    } while (key == 0 || pdfium::ContainsKey(m_eventTargets, key));
50    pListener->SetEventKey(key);
51  }
52  if (!m_eventTargets[key])
53    m_eventTargets[key] = pdfium::MakeUnique<CFWL_EventTarget>(pListener);
54
55  m_eventTargets[key]->SetEventSource(pEventSource);
56}
57
58void CFWL_NoteDriver::UnregisterEventTarget(CFWL_Widget* pListener) {
59  uint32_t key = pListener->GetEventKey();
60  if (key == 0)
61    return;
62
63  auto it = m_eventTargets.find(key);
64  if (it != m_eventTargets.end())
65    it->second->FlagInvalid();
66}
67
68void CFWL_NoteDriver::PushNoteLoop(CFWL_NoteLoop* pNoteLoop) {
69  m_NoteLoopQueue.push_back(pNoteLoop);
70}
71
72CFWL_NoteLoop* CFWL_NoteDriver::PopNoteLoop() {
73  if (m_NoteLoopQueue.empty())
74    return nullptr;
75
76  CFWL_NoteLoop* p = m_NoteLoopQueue.back();
77  m_NoteLoopQueue.pop_back();
78  return p;
79}
80
81bool CFWL_NoteDriver::SetFocus(CFWL_Widget* pFocus) {
82  if (m_pFocus == pFocus)
83    return true;
84
85  CFWL_Widget* pPrev = m_pFocus;
86  m_pFocus = pFocus;
87  if (pPrev) {
88    if (IFWL_WidgetDelegate* pDelegate = pPrev->GetDelegate()) {
89      CFWL_MessageKillFocus ms(pPrev, pPrev);
90      pDelegate->OnProcessMessage(&ms);
91    }
92  }
93  if (pFocus) {
94    CFWL_Widget* pWidget =
95        pFocus->GetOwnerApp()->GetWidgetMgr()->GetSystemFormWidget(pFocus);
96    CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
97    if (pForm)
98      pForm->SetSubFocus(pFocus);
99
100    if (IFWL_WidgetDelegate* pDelegate = pFocus->GetDelegate()) {
101      CFWL_MessageSetFocus ms(nullptr, pFocus);
102      pDelegate->OnProcessMessage(&ms);
103    }
104  }
105  return true;
106}
107
108void CFWL_NoteDriver::Run() {
109#if _FX_OS_ == _FX_OS_LINUX_ || _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
110  for (;;) {
111    CFWL_NoteLoop* pTopLoop = GetTopLoop();
112    if (!pTopLoop || !pTopLoop->ContinueModal())
113      break;
114    UnqueueMessageAndProcess(pTopLoop);
115  }
116#endif  // _FX_OS_ == _FX_OS_LINUX_ || _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
117}
118
119void CFWL_NoteDriver::NotifyTargetHide(CFWL_Widget* pNoteTarget) {
120  if (m_pFocus == pNoteTarget)
121    m_pFocus = nullptr;
122  if (m_pHover == pNoteTarget)
123    m_pHover = nullptr;
124  if (m_pGrab == pNoteTarget)
125    m_pGrab = nullptr;
126}
127
128void CFWL_NoteDriver::NotifyTargetDestroy(CFWL_Widget* pNoteTarget) {
129  if (m_pFocus == pNoteTarget)
130    m_pFocus = nullptr;
131  if (m_pHover == pNoteTarget)
132    m_pHover = nullptr;
133  if (m_pGrab == pNoteTarget)
134    m_pGrab = nullptr;
135
136  UnregisterEventTarget(pNoteTarget);
137
138  for (CFWL_Widget* pWidget : m_Forms) {
139    CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
140    if (!pForm)
141      continue;
142
143    CFWL_Widget* pSubFocus = pForm->GetSubFocus();
144    if (!pSubFocus)
145      return;
146
147    if (pSubFocus == pNoteTarget)
148      pForm->SetSubFocus(nullptr);
149  }
150}
151
152void CFWL_NoteDriver::RegisterForm(CFWL_Widget* pForm) {
153  if (!pForm || pdfium::ContainsValue(m_Forms, pForm))
154    return;
155
156  m_Forms.push_back(pForm);
157  if (m_Forms.size() == 1 && !m_NoteLoopQueue.empty() && m_NoteLoopQueue[0])
158    m_NoteLoopQueue[0]->SetMainForm(pForm);
159}
160
161void CFWL_NoteDriver::UnRegisterForm(CFWL_Widget* pForm) {
162  auto iter = std::find(m_Forms.begin(), m_Forms.end(), pForm);
163  if (iter != m_Forms.end())
164    m_Forms.erase(iter);
165}
166
167void CFWL_NoteDriver::QueueMessage(std::unique_ptr<CFWL_Message> pMessage) {
168  m_NoteQueue.push_back(std::move(pMessage));
169}
170
171void CFWL_NoteDriver::UnqueueMessageAndProcess(CFWL_NoteLoop* pNoteLoop) {
172  if (m_NoteQueue.empty())
173    return;
174
175  std::unique_ptr<CFWL_Message> pMessage = std::move(m_NoteQueue.front());
176  m_NoteQueue.pop_front();
177  if (!IsValidMessage(pMessage.get()))
178    return;
179
180  ProcessMessage(std::move(pMessage));
181}
182
183CFWL_NoteLoop* CFWL_NoteDriver::GetTopLoop() const {
184  return !m_NoteLoopQueue.empty() ? m_NoteLoopQueue.back() : nullptr;
185}
186
187void CFWL_NoteDriver::ProcessMessage(std::unique_ptr<CFWL_Message> pMessage) {
188  CFWL_WidgetMgr* pWidgetMgr =
189      pMessage->m_pDstTarget->GetOwnerApp()->GetWidgetMgr();
190  CFWL_Widget* pMessageForm = pWidgetMgr->IsFormDisabled()
191                                  ? pMessage->m_pDstTarget
192                                  : GetMessageForm(pMessage->m_pDstTarget);
193  if (!pMessageForm)
194    return;
195
196  if (!DispatchMessage(pMessage.get(), pMessageForm))
197    return;
198
199  if (pMessage->GetType() == CFWL_Message::Type::Mouse)
200    MouseSecondary(pMessage.get());
201}
202
203bool CFWL_NoteDriver::DispatchMessage(CFWL_Message* pMessage,
204                                      CFWL_Widget* pMessageForm) {
205  switch (pMessage->GetType()) {
206    case CFWL_Message::Type::SetFocus: {
207      if (!DoSetFocus(pMessage, pMessageForm))
208        return false;
209      break;
210    }
211    case CFWL_Message::Type::KillFocus: {
212      if (!DoKillFocus(pMessage, pMessageForm))
213        return false;
214      break;
215    }
216    case CFWL_Message::Type::Key: {
217      if (!DoKey(pMessage, pMessageForm))
218        return false;
219      break;
220    }
221    case CFWL_Message::Type::Mouse: {
222      if (!DoMouse(pMessage, pMessageForm))
223        return false;
224      break;
225    }
226    case CFWL_Message::Type::MouseWheel: {
227      if (!DoWheel(pMessage, pMessageForm))
228        return false;
229      break;
230    }
231    default:
232      break;
233  }
234  if (IFWL_WidgetDelegate* pDelegate = pMessage->m_pDstTarget->GetDelegate())
235    pDelegate->OnProcessMessage(pMessage);
236
237  return true;
238}
239
240bool CFWL_NoteDriver::DoSetFocus(CFWL_Message* pMessage,
241                                 CFWL_Widget* pMessageForm) {
242  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
243  if (pWidgetMgr->IsFormDisabled()) {
244    m_pFocus = pMessage->m_pDstTarget;
245    return true;
246  }
247
248  CFWL_Widget* pWidget = pMessage->m_pDstTarget;
249  if (!pWidget)
250    return false;
251
252  CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
253  CFWL_Widget* pSubFocus = pForm->GetSubFocus();
254  if (pSubFocus && ((pSubFocus->GetStates() & FWL_WGTSTATE_Focused) == 0)) {
255    pMessage->m_pDstTarget = pSubFocus;
256    if (m_pFocus != pMessage->m_pDstTarget) {
257      m_pFocus = pMessage->m_pDstTarget;
258      return true;
259    }
260  }
261  return false;
262}
263
264bool CFWL_NoteDriver::DoKillFocus(CFWL_Message* pMessage,
265                                  CFWL_Widget* pMessageForm) {
266  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
267  if (pWidgetMgr->IsFormDisabled()) {
268    if (m_pFocus == pMessage->m_pDstTarget)
269      m_pFocus = nullptr;
270    return true;
271  }
272
273  CFWL_Form* pForm = static_cast<CFWL_Form*>(pMessage->m_pDstTarget);
274  if (!pForm)
275    return false;
276
277  CFWL_Widget* pSubFocus = pForm->GetSubFocus();
278  if (pSubFocus && (pSubFocus->GetStates() & FWL_WGTSTATE_Focused)) {
279    pMessage->m_pDstTarget = pSubFocus;
280    if (m_pFocus == pMessage->m_pDstTarget) {
281      m_pFocus = nullptr;
282      return true;
283    }
284  }
285  return false;
286}
287
288bool CFWL_NoteDriver::DoKey(CFWL_Message* pMessage, CFWL_Widget* pMessageForm) {
289  CFWL_MessageKey* pMsg = static_cast<CFWL_MessageKey*>(pMessage);
290#if (_FX_OS_ != _FX_OS_MACOSX_)
291  if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown &&
292      pMsg->m_dwKeyCode == FWL_VKEY_Tab) {
293    CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
294    CFWL_Widget* pForm = GetMessageForm(pMsg->m_pDstTarget);
295    CFWL_Widget* pFocus = m_pFocus;
296    if (m_pFocus && pWidgetMgr->GetSystemFormWidget(m_pFocus) != pForm)
297      pFocus = nullptr;
298
299    bool bFind = false;
300    CFWL_Widget* pNextTabStop = pWidgetMgr->NextTab(pForm, pFocus, bFind);
301    if (!pNextTabStop) {
302      bFind = false;
303      pNextTabStop = pWidgetMgr->NextTab(pForm, nullptr, bFind);
304    }
305    if (pNextTabStop == pFocus)
306      return true;
307    if (pNextTabStop)
308      SetFocus(pNextTabStop);
309    return true;
310  }
311#endif
312
313  if (!m_pFocus) {
314    if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown &&
315        pMsg->m_dwKeyCode == FWL_VKEY_Return) {
316      CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
317      CFWL_Widget* defButton = pWidgetMgr->GetDefaultButton(pMessageForm);
318      if (defButton) {
319        pMsg->m_pDstTarget = defButton;
320        return true;
321      }
322    }
323    return false;
324  }
325  pMsg->m_pDstTarget = m_pFocus;
326  return true;
327}
328
329bool CFWL_NoteDriver::DoMouse(CFWL_Message* pMessage,
330                              CFWL_Widget* pMessageForm) {
331  CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
332  if (pMsg->m_dwCmd == FWL_MouseCommand::Leave ||
333      pMsg->m_dwCmd == FWL_MouseCommand::Hover ||
334      pMsg->m_dwCmd == FWL_MouseCommand::Enter) {
335    return !!pMsg->m_pDstTarget;
336  }
337  if (pMsg->m_pDstTarget != pMessageForm)
338    pMsg->m_pos = pMsg->m_pDstTarget->TransformTo(pMessageForm, pMsg->m_pos);
339  if (!DoMouseEx(pMsg, pMessageForm))
340    pMsg->m_pDstTarget = pMessageForm;
341  return true;
342}
343
344bool CFWL_NoteDriver::DoWheel(CFWL_Message* pMessage,
345                              CFWL_Widget* pMessageForm) {
346  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
347  if (!pWidgetMgr)
348    return false;
349
350  CFWL_MessageMouseWheel* pMsg = static_cast<CFWL_MessageMouseWheel*>(pMessage);
351  CFWL_Widget* pDst = pWidgetMgr->GetWidgetAtPoint(pMessageForm, pMsg->m_pos);
352  if (!pDst)
353    return false;
354
355  pMsg->m_pos = pMessageForm->TransformTo(pDst, pMsg->m_pos);
356  pMsg->m_pDstTarget = pDst;
357  return true;
358}
359
360bool CFWL_NoteDriver::DoMouseEx(CFWL_Message* pMessage,
361                                CFWL_Widget* pMessageForm) {
362  CFWL_WidgetMgr* pWidgetMgr = pMessageForm->GetOwnerApp()->GetWidgetMgr();
363  if (!pWidgetMgr)
364    return false;
365  CFWL_Widget* pTarget = nullptr;
366  if (m_pGrab)
367    pTarget = m_pGrab;
368
369  CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
370  if (!pTarget)
371    pTarget = pWidgetMgr->GetWidgetAtPoint(pMessageForm, pMsg->m_pos);
372  if (!pTarget)
373    return false;
374  if (pTarget && pMessageForm != pTarget)
375    pMsg->m_pos = pMessageForm->TransformTo(pTarget, pMsg->m_pos);
376
377  pMsg->m_pDstTarget = pTarget;
378  return true;
379}
380
381void CFWL_NoteDriver::MouseSecondary(CFWL_Message* pMessage) {
382  CFWL_Widget* pTarget = pMessage->m_pDstTarget;
383  if (pTarget == m_pHover)
384    return;
385
386  CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
387  if (m_pHover) {
388    CFWL_MessageMouse msLeave(nullptr, m_pHover);
389    msLeave.m_pos = pTarget->TransformTo(m_pHover, pMsg->m_pos);
390    msLeave.m_dwFlags = 0;
391    msLeave.m_dwCmd = FWL_MouseCommand::Leave;
392    DispatchMessage(&msLeave, nullptr);
393  }
394  if (pTarget->GetClassID() == FWL_Type::Form) {
395    m_pHover = nullptr;
396    return;
397  }
398  m_pHover = pTarget;
399
400  CFWL_MessageMouse msHover(nullptr, pTarget);
401  msHover.m_pos = pMsg->m_pos;
402  msHover.m_dwFlags = 0;
403  msHover.m_dwCmd = FWL_MouseCommand::Hover;
404  DispatchMessage(&msHover, nullptr);
405}
406
407bool CFWL_NoteDriver::IsValidMessage(CFWL_Message* pMessage) {
408  for (CFWL_NoteLoop* pNoteLoop : m_NoteLoopQueue) {
409    CFWL_Widget* pForm = pNoteLoop->GetForm();
410    if (pForm && pForm == pMessage->m_pDstTarget)
411      return true;
412  }
413  for (CFWL_Widget* pWidget : m_Forms) {
414    CFWL_Form* pForm = static_cast<CFWL_Form*>(pWidget);
415    if (pForm == pMessage->m_pDstTarget)
416      return true;
417  }
418  return false;
419}
420
421CFWL_Widget* CFWL_NoteDriver::GetMessageForm(CFWL_Widget* pDstTarget) {
422  if (m_NoteLoopQueue.empty())
423    return nullptr;
424
425  CFWL_Widget* pMessageForm = nullptr;
426  if (m_NoteLoopQueue.size() > 1)
427    pMessageForm = m_NoteLoopQueue.back()->GetForm();
428  else if (!pdfium::ContainsValue(m_Forms, pDstTarget))
429    pMessageForm = pDstTarget;
430
431  if (!pMessageForm && pDstTarget) {
432    CFWL_WidgetMgr* pWidgetMgr = pDstTarget->GetOwnerApp()->GetWidgetMgr();
433    if (!pWidgetMgr)
434      return nullptr;
435
436    pMessageForm = pWidgetMgr->GetSystemFormWidget(pDstTarget);
437  }
438  return pMessageForm;
439}
440
441void CFWL_NoteDriver::ClearEventTargets() {
442  auto it = m_eventTargets.begin();
443  while (it != m_eventTargets.end()) {
444    auto old = it++;
445    if (!old->second->IsValid())
446      m_eventTargets.erase(old);
447  }
448}
449