1 /* GG is a GUI for OpenGL.
2    Copyright (C) 2003-2008 T. Zachary Laine
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public License
6    as published by the Free Software Foundation; either version 2.1
7    of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA
18 
19    If you do not wish to comply with the terms of the LGPL please
20    contact the author as other terms are available for a fee.
21 
22    Zach Laine
23    whatwasthataddress@gmail.com */
24 
25 #include <GG/Wnd.h>
26 
27 #include <GG/GUI.h>
28 #include <GG/BrowseInfoWnd.h>
29 #include <GG/DrawUtil.h>
30 #include <GG/Layout.h>
31 #include <GG/WndEvent.h>
32 
33 #include <boost/multi_index_container.hpp>
34 #include <boost/multi_index/member.hpp>
35 #include <boost/multi_index/ordered_index.hpp>
36 
37 
38 using namespace GG;
39 
40 namespace {
41     namespace mi = boost::multi_index;
42     struct GridLayoutWnd
43     {
GridLayoutWnd__anon62aebdf20111::GridLayoutWnd44         GridLayoutWnd() {}
45 
GridLayoutWnd__anon62aebdf20111::GridLayoutWnd46         GridLayoutWnd(std::shared_ptr<Wnd>& wnd_, const Pt& ul_, const Pt& lr_) : wnd(wnd_), ul(ul_), lr(lr_) {}
47         std::shared_ptr<Wnd> wnd;
48         Pt ul;
49         Pt lr;
50     };
51     struct IsLeft
52     {
operator ()__anon62aebdf20111::IsLeft53         bool operator()(const Pt& lhs, const Pt& rhs) const {return lhs.x < rhs.x;}
operator ()__anon62aebdf20111::IsLeft54         bool operator()(X x, const Pt& pt) const            {return x < pt.x;}
operator ()__anon62aebdf20111::IsLeft55         bool operator()(const Pt& pt, X x) const            {return pt.x < x;}
56     };
57     struct IsTop
58     {
operator ()__anon62aebdf20111::IsTop59         bool operator()(const Pt& lhs, const Pt& rhs) const {return lhs.y < rhs.y;}
operator ()__anon62aebdf20111::IsTop60         bool operator()(Y y, const Pt& pt) const            {return y < pt.y;}
operator ()__anon62aebdf20111::IsTop61         bool operator()(const Pt& pt, Y y) const            {return pt.y < y;}
62     };
63     struct IsRight
64     {
operator ()__anon62aebdf20111::IsRight65         bool operator()(const Pt& lhs, const Pt& rhs) const {return rhs.x < lhs.x;}
operator ()__anon62aebdf20111::IsRight66         bool operator()(X x, const Pt& pt) const            {return pt.x < x;}
operator ()__anon62aebdf20111::IsRight67         bool operator()(const Pt& pt, X x) const            {return x < pt.x;}
68     };
69     struct IsBottom
70     {
operator ()__anon62aebdf20111::IsBottom71         bool operator()(const Pt& lhs, const Pt& rhs) const {return rhs.y < lhs.y;}
operator ()__anon62aebdf20111::IsBottom72         bool operator()(Y y, const Pt& pt) const            {return pt.y < y;}
operator ()__anon62aebdf20111::IsBottom73         bool operator()(const Pt& pt, Y y) const            {return y < pt.y;}
74     };
75     struct Pointer {};
76     struct LayoutLeft {};
77     struct LayoutTop {};
78     struct LayoutRight {};
79     struct LayoutBottom {};
80     typedef mi::multi_index_container<
81         GridLayoutWnd,
82         mi::indexed_by<
83             mi::ordered_unique<mi::tag<Pointer>,            mi::member<GridLayoutWnd, std::shared_ptr<Wnd>, &GridLayoutWnd::wnd>>,
84             mi::ordered_non_unique<mi::tag<LayoutLeft>,     mi::member<GridLayoutWnd, Pt,   &GridLayoutWnd::ul>, IsLeft>,
85             mi::ordered_non_unique<mi::tag<LayoutTop>,      mi::member<GridLayoutWnd, Pt,   &GridLayoutWnd::ul>, IsTop>,
86             mi::ordered_non_unique<mi::tag<LayoutRight>,    mi::member<GridLayoutWnd, Pt,   &GridLayoutWnd::lr>, IsRight>,
87             mi::ordered_non_unique<mi::tag<LayoutBottom>,   mi::member<GridLayoutWnd, Pt,   &GridLayoutWnd::lr>, IsBottom>
88         >
89     > GridLayoutWndContainer;
90     typedef GridLayoutWndContainer::index<Pointer>::type::iterator      PointerIter;
91     typedef GridLayoutWndContainer::index<LayoutLeft>::type::iterator   LeftIter;
92     typedef GridLayoutWndContainer::index<LayoutTop>::type::iterator    TopIter;
93     typedef GridLayoutWndContainer::index<LayoutRight>::type::iterator  RightIter;
94     typedef GridLayoutWndContainer::index<LayoutBottom>::type::iterator BottomIter;
95 
96     struct WndHorizontalLess
97     {
operator ()__anon62aebdf20111::WndHorizontalLess98         bool operator()(const std::shared_ptr<Wnd>& lhs, const std::shared_ptr<Wnd>& rhs) const
99             {return lhs->Left() < rhs->Left();}
100     };
101 
102     struct WndVerticalLess
103     {
operator ()__anon62aebdf20111::WndVerticalLess104         bool operator()(const std::shared_ptr<Wnd>& lhs, const std::shared_ptr<Wnd>& rhs) const
105             {return lhs->Top() < rhs->Top();}
106     };
107 
108     const int DEFAULT_LAYOUT_BORDER_MARGIN = 0;
109     const int DEFAULT_LAYOUT_CELL_MARGIN = 5;
110 
111     struct ForwardToParentException {};
112 }
113 
114 ///////////////////////////////////////
115 // WndFlags
116 ///////////////////////////////////////
117 const WndFlag GG::NO_WND_FLAGS       (0);
118 const WndFlag GG::INTERACTIVE        (1 << 0);
119 const WndFlag GG::REPEAT_BUTTON_DOWN (1 << 1);
120 const WndFlag GG::DRAGABLE           (1 << 2);
121 const WndFlag GG::RESIZABLE          (1 << 3);
122 const WndFlag GG::ONTOP              (1 << 4);
123 const WndFlag GG::MODAL              (1 << 5);
124 const WndFlag GG::REPEAT_KEY_PRESS   (1 << 6);
125 
126 GG_FLAGSPEC_IMPL(WndFlag);
127 
128 namespace {
RegisterWndFlags()129     bool RegisterWndFlags()
130     {
131         FlagSpec<WndFlag>& spec = FlagSpec<WndFlag>::instance();
132         spec.insert(NO_WND_FLAGS,       "NO_WND_FLAGS",         true);
133         spec.insert(INTERACTIVE,        "INTERACTIVE",          true);
134         spec.insert(REPEAT_BUTTON_DOWN, "REPEAT_BUTTON_DOWN",   true);
135         spec.insert(DRAGABLE,           "DRAGABLE",             true);
136         spec.insert(RESIZABLE,          "RESIZABLE",            true);
137         spec.insert(ONTOP,              "ONTOP",                true);
138         spec.insert(MODAL,              "MODAL",                true);
139         spec.insert(REPEAT_KEY_PRESS,   "REPEAT_KEY_PRESS",     true);
140         return true;
141     }
142     bool dummy = RegisterWndFlags();
143 }
144 
145 
146 ///////////////////////////////////////
147 // class GG::Wnd
148 ///////////////////////////////////////
149 // static(s)
150 unsigned int Wnd::s_default_browse_time = 1500;
151 std::shared_ptr<BrowseInfoWnd> Wnd::s_default_browse_info_wnd;
152 
Wnd()153 Wnd::Wnd() :
154     std::enable_shared_from_this<Wnd>(),
155     m_parent(),
156     m_child_clipping_mode(DontClip),
157     m_upperleft(X0, Y0),
158     m_lowerright(X1, Y1),
159     m_max_size(X(1 << 30), Y(1 << 30)),
160     m_layout(),
161     m_containing_layout(),
162     m_flags()
163 {
164     m_browse_modes.resize(1);
165     m_browse_modes[0].time = s_default_browse_time;
166     m_browse_modes[0].wnd = s_default_browse_info_wnd;
167 }
168 
Wnd(X x,Y y,X w,Y h,Flags<WndFlag> flags)169 Wnd::Wnd(X x, Y y, X w, Y h, Flags<WndFlag> flags/* = INTERACTIVE | DRAGABLE*/) :
170     Wnd()
171 {
172     m_upperleft = Pt(x, y);
173     m_lowerright = Pt(x + w, y + h);
174 
175     m_flags = flags;
176     ValidateFlags();
177 }
178 
~Wnd()179 Wnd::~Wnd()
180 {
181     // remove this-references from Wnds that this Wnd filters
182     for (auto& weak_filtered_wnd : m_filtering) {
183         auto filtering_wnd = weak_filtered_wnd.lock();
184         if (!filtering_wnd)
185             continue;
186 
187         // The weak pointer in the filtered window pointing to "this" will be
188         // expired, since we are in ~Wnd().  Remove all expired weak_ptr from the
189         // filtered window.
190         std::vector<std::weak_ptr<Wnd>> good_filters;
191         good_filters.reserve(filtering_wnd->m_filters.size());
192         for (const auto& weak_filtering_wnd : filtering_wnd->m_filters)
193             if (!weak_filtering_wnd.expired())
194                 good_filters.push_back(weak_filtering_wnd);
195         good_filters.swap(filtering_wnd->m_filters);
196     }
197 
198     // remove this-references from Wnds that filter this Wnd
199     for (auto& weak_filter_wnd : m_filters) {
200         auto filter_wnd = weak_filter_wnd.lock();
201         if (!filter_wnd)
202             continue;
203 
204         // The weak pointer in the filtering window pointing to "this" will be
205         // expired, since we are in ~Wnd().  Remove all expired weak_ptr from the
206         // filtered window.
207         auto it = filter_wnd->m_filtering.begin();
208         while (it != filter_wnd->m_filtering.end()) {
209             if (it->expired())
210                 it = filter_wnd->m_filtering.erase(it);
211             else
212                 ++it;
213         }
214     }
215 }
216 
Interactive() const217 bool Wnd::Interactive() const
218 { return m_flags & INTERACTIVE; }
219 
RepeatKeyPress() const220 bool Wnd::RepeatKeyPress() const
221 { return m_flags & REPEAT_KEY_PRESS; }
222 
RepeatButtonDown() const223 bool Wnd::RepeatButtonDown() const
224 { return m_flags & REPEAT_BUTTON_DOWN; }
225 
Dragable() const226 bool Wnd::Dragable() const
227 { return m_flags & DRAGABLE; }
228 
Resizable() const229 bool Wnd::Resizable() const
230 { return m_flags & RESIZABLE; }
231 
OnTop() const232 bool Wnd::OnTop() const
233 { return !Parent() && m_flags & ONTOP; }
234 
Modal() const235 bool Wnd::Modal() const
236 { return !Parent() && m_flags & MODAL; }
237 
GetChildClippingMode() const238 Wnd::ChildClippingMode Wnd::GetChildClippingMode() const
239 { return m_child_clipping_mode; }
240 
NonClientChild() const241 bool Wnd::NonClientChild() const
242 { return m_non_client_child; }
243 
Visible() const244 bool Wnd::Visible() const
245 { return m_visible; }
246 
PreRenderRequired() const247 bool Wnd::PreRenderRequired() const
248 {
249     if (m_needs_prerender)
250         return true;
251 
252     auto&& layout = GetLayout();
253 
254     return (layout && layout->m_needs_prerender);
255 }
256 
Name() const257 const std::string& Wnd::Name() const
258 { return m_name; }
259 
DragDropDataType() const260 const std::string& Wnd::DragDropDataType() const
261 { return m_drag_drop_data_type; }
262 
DropsAcceptable(DropsAcceptableIter first,DropsAcceptableIter last,const Pt & pt,Flags<ModKey> mod_keys) const263 void Wnd::DropsAcceptable(DropsAcceptableIter first, DropsAcceptableIter last,
264                           const Pt& pt, Flags<ModKey> mod_keys) const
265 {
266     // default reject all drops. derived classes can override to accept drops
267     for (auto& it = first; it != last; ++it)
268         it->second = false;
269 }
270 
UpperLeft() const271 Pt Wnd::UpperLeft() const
272 {
273     Pt retval = m_upperleft;
274     auto&& parent = Parent();
275     if (parent)
276         retval += parent->ClientUpperLeft();
277     return retval;
278 }
279 
Left() const280 X Wnd::Left() const
281 { return UpperLeft().x; }
282 
Top() const283 Y Wnd::Top() const
284 { return UpperLeft().y; }
285 
LowerRight() const286 Pt Wnd::LowerRight() const
287 {
288     Pt retval = m_lowerright;
289     auto&& parent = Parent();
290     if (parent)
291         retval += parent->ClientUpperLeft();
292     return retval;
293 }
294 
Right() const295 X Wnd::Right() const
296 { return LowerRight().x; }
297 
Bottom() const298 Y Wnd::Bottom() const
299 { return LowerRight().y; }
300 
RelativeUpperLeft() const301 Pt Wnd::RelativeUpperLeft() const
302 { return m_upperleft; }
303 
RelativeLowerRight() const304 Pt Wnd::RelativeLowerRight() const
305 { return m_lowerright; }
306 
Width() const307 X Wnd::Width() const
308 { return m_lowerright.x - m_upperleft.x; }
309 
Height() const310 Y Wnd::Height() const
311 { return m_lowerright.y - m_upperleft.y; }
312 
Size() const313 Pt Wnd::Size() const
314 { return Pt(m_lowerright.x - m_upperleft.x, m_lowerright.y - m_upperleft.y); }
315 
MinSize() const316 Pt Wnd::MinSize() const
317 { return m_min_size; }
318 
MaxSize() const319 Pt Wnd::MaxSize() const
320 { return m_max_size; }
321 
MinUsableSize() const322 Pt Wnd::MinUsableSize() const
323 {
324     auto&& layout = GetLayout();
325     return layout ? layout->MinUsableSize() : Size();
326 }
327 
ClientUpperLeft() const328 Pt Wnd::ClientUpperLeft() const
329 { return UpperLeft(); }
330 
ClientLowerRight() const331 Pt Wnd::ClientLowerRight() const
332 { return LowerRight(); }
333 
ClientSize() const334 Pt Wnd::ClientSize() const
335 { return ClientLowerRight() - ClientUpperLeft(); }
336 
ClientWidth() const337 X Wnd::ClientWidth() const
338 { return ClientLowerRight().x - ClientUpperLeft().x; }
339 
ClientHeight() const340 Y Wnd::ClientHeight() const
341 { return ClientLowerRight().y - ClientUpperLeft().y; }
342 
ScreenToWindow(const Pt & pt) const343 Pt Wnd::ScreenToWindow(const Pt& pt) const
344 { return pt - UpperLeft(); }
345 
ScreenToClient(const Pt & pt) const346 Pt Wnd::ScreenToClient(const Pt& pt) const
347 { return pt - ClientUpperLeft(); }
348 
InWindow(const Pt & pt) const349 bool Wnd::InWindow(const Pt& pt) const
350 { return pt >= UpperLeft() && pt < LowerRight(); }
351 
InClient(const Pt & pt) const352 bool Wnd::InClient(const Pt& pt) const
353 { return pt >= ClientUpperLeft() && pt < ClientLowerRight(); }
354 
Children() const355 const std::list<std::shared_ptr<Wnd>>& Wnd::Children() const
356 { return m_children; }
357 
Parent() const358 std::shared_ptr<Wnd> Wnd::Parent() const
359 { return LockAndResetIfExpired(m_parent); }
360 
IsAncestorOf(const std::shared_ptr<Wnd> & wnd) const361 bool Wnd::IsAncestorOf(const std::shared_ptr<Wnd>& wnd) const
362 {
363     // Is this Wnd one of wnd's (direct or indirect) parents?
364     if (!wnd)
365         return false;
366     auto&& parent_of_wnd = wnd->Parent();
367     while (parent_of_wnd) {
368         if (parent_of_wnd.get() == this)
369             return true;
370         parent_of_wnd = parent_of_wnd->Parent();
371     }
372     return false;
373 }
374 
RootParent() const375 std::shared_ptr<Wnd> Wnd::RootParent() const
376 {
377     auto&& parent = Parent();
378     auto&& gparent = parent ? parent->Parent() : nullptr;
379     while (gparent) {
380         parent = std::move(gparent);
381         gparent = parent->Parent();
382     }
383     return parent;
384 }
385 
GetLayout() const386 std::shared_ptr<Layout> Wnd::GetLayout() const
387 { return LockAndResetIfExpired(m_layout); }
388 
ContainingLayout() const389 Layout* Wnd::ContainingLayout() const
390 { return LockAndResetIfExpired(m_containing_layout).get(); }
391 
BrowseModes() const392 const std::vector<Wnd::BrowseInfoMode>& Wnd::BrowseModes() const
393 { return m_browse_modes; }
394 
BrowseInfoText(std::size_t mode) const395 const std::string& Wnd::BrowseInfoText(std::size_t mode) const
396 { return m_browse_modes.at(mode).text; }
397 
GetStyleFactory() const398 const std::shared_ptr<StyleFactory>& Wnd::GetStyleFactory() const
399 { return m_style_factory ? m_style_factory : GUI::GetGUI()->GetStyleFactory(); }
400 
WindowRegion(const Pt & pt) const401 WndRegion Wnd::WindowRegion(const Pt& pt) const
402 {
403     enum {LEFT = 0, MIDDLE = 1, RIGHT = 2};
404     enum {TOP = 0, BOTTOM = 2};
405 
406     // window regions look like this:
407     // 0111112
408     // 3444445   // 4 is client area, 0,2,6,8 are corners
409     // 3444445
410     // 6777778
411 
412     int x_pos = MIDDLE;   // default & typical case is that the mouse is over the (non-border) client area
413     int y_pos = MIDDLE;
414 
415     if (pt.x < ClientUpperLeft().x)
416         x_pos = LEFT;
417     else if (pt.x > ClientLowerRight().x)
418         x_pos = RIGHT;
419 
420     if (pt.y < ClientUpperLeft().y)
421         y_pos = TOP;
422     else if (pt.y > ClientLowerRight().y)
423         y_pos = BOTTOM;
424 
425     return (Resizable() ? WndRegion(x_pos + 3 * y_pos) : WR_NONE);
426 }
427 
ClampRectWithMinAndMaxSize(Pt & ul,Pt & lr) const428 void Wnd::ClampRectWithMinAndMaxSize(Pt& ul, Pt& lr) const
429 {
430     Pt min_sz = MinSize();
431     Pt max_sz = MaxSize();
432     auto&& layout = GetLayout();
433     if (layout) {
434         Pt layout_min_sz = layout->MinSize() + (Size() - ClientSize());
435         min_sz.x = std::max(min_sz.x, layout_min_sz.x);
436         min_sz.y = std::max(min_sz.y, layout_min_sz.y);
437     }
438     if (lr.x - ul.x < min_sz.x) {
439         if (ul.x != m_upperleft.x)
440             ul.x = lr.x - min_sz.x;
441         else
442             lr.x = ul.x + min_sz.x;
443     } else if (max_sz.x < lr.x - ul.x) {
444         if (lr.x != m_lowerright.x)
445             lr.x = ul.x + max_sz.x;
446         else
447             ul.x = lr.x - max_sz.x;
448     }
449     if (lr.y - ul.y < min_sz.y) {
450         if (ul.y != m_upperleft.y)
451             ul.y = lr.y - min_sz.y;
452         else
453             lr.y = ul.y + min_sz.y;
454     } else if (max_sz.y < lr.y - ul.y) {
455         if (lr.y != m_lowerright.y)
456             lr.y = ul.y + max_sz.y;
457         else
458             ul.y = lr.y - max_sz.y;
459     }
460 }
461 
SetDragDropDataType(const std::string & data_type)462 void Wnd::SetDragDropDataType(const std::string& data_type)
463 { m_drag_drop_data_type = data_type; }
464 
StartingChildDragDrop(const Wnd * wnd,const Pt & offset)465 void Wnd::StartingChildDragDrop(const Wnd* wnd, const Pt& offset)
466 {}
467 
AcceptDrops(const Pt & pt,std::vector<std::shared_ptr<Wnd>> wnds,Flags<ModKey> mod_keys)468 void Wnd::AcceptDrops(const Pt& pt, std::vector<std::shared_ptr<Wnd>> wnds, Flags<ModKey> mod_keys)
469 {
470     if (!Interactive() && Parent())
471         ForwardEventToParent();
472     // if dropped Wnds were accepted, but no handler take ownership they will be destroyed
473 }
474 
CancellingChildDragDrop(const std::vector<const Wnd * > & wnds)475 void Wnd::CancellingChildDragDrop(const std::vector<const Wnd*>& wnds)
476 {}
477 
ChildrenDraggedAway(const std::vector<Wnd * > & wnds,const Wnd * destination)478 void Wnd::ChildrenDraggedAway(const std::vector<Wnd*>& wnds, const Wnd* destination)
479 {
480     for (auto& wnd : wnds) {
481         DetachChild(wnd);
482     }
483 }
484 
SetName(const std::string & name)485 void Wnd::SetName(const std::string& name)
486 { m_name = name; }
487 
Hide()488 void Wnd::Hide()
489 {
490     m_visible = false;
491     for (auto& child : m_children)
492         child->Hide();
493 }
494 
Show()495 void Wnd::Show()
496 {
497     m_visible = true;
498     for (auto& child : m_children)
499         child->Show();
500 }
501 
ModalInit()502 void Wnd::ModalInit()
503 {}
504 
SetChildClippingMode(ChildClippingMode mode)505 void Wnd::SetChildClippingMode(ChildClippingMode mode)
506 { m_child_clipping_mode = mode; }
507 
NonClientChild(bool b)508 void Wnd::NonClientChild(bool b)
509 { m_non_client_child = b; }
510 
MoveTo(const Pt & pt)511 void Wnd::MoveTo(const Pt& pt)
512 { SizeMove(pt, pt + Size()); }
513 
OffsetMove(const Pt & pt)514 void Wnd::OffsetMove(const Pt& pt)
515 { SizeMove(m_upperleft + pt, m_lowerright + pt); }
516 
SizeMove(const Pt & ul_,const Pt & lr_)517 void Wnd::SizeMove(const Pt& ul_, const Pt& lr_)
518 {
519     Pt ul = ul_, lr = lr_;
520     Pt original_sz = Size();
521     bool resized = (original_sz != (lr - ul));
522     if (resized)
523         ClampRectWithMinAndMaxSize(ul, lr);
524 
525     m_upperleft = ul;
526     m_lowerright = lr;
527     if (resized) {
528         bool size_changed = Size() != original_sz;
529         auto&& layout = GetLayout();
530         if (layout && size_changed)
531             layout->Resize(ClientSize());
532         if (size_changed && !dynamic_cast<Layout*>(this))
533             if (const auto&& containing_layout = LockAndResetIfExpired(m_containing_layout))
534                 containing_layout->ChildSizeOrMinSizeChanged();
535     }
536 }
537 
Resize(const Pt & sz)538 void Wnd::Resize(const Pt& sz)
539 { SizeMove(m_upperleft, m_upperleft + sz); }
540 
SetMinSize(const Pt & sz)541 void Wnd::SetMinSize(const Pt& sz)
542 {
543     bool min_size_changed = m_min_size != sz;
544     m_min_size = sz;
545     if (Width() < m_min_size.x || Height() < m_min_size.y)
546         Resize(Pt(std::max(Width(), m_min_size.x), std::max(Height(), m_min_size.y)));
547     // The previous Resize() will call ChildSizeOrMinSizeChanged() itself if needed
548     else if (min_size_changed && !dynamic_cast<Layout*>(this)) {
549         if (auto&& containing_layout = LockAndResetIfExpired(m_containing_layout))
550             containing_layout->ChildSizeOrMinSizeChanged();
551     }
552 }
553 
SetMaxSize(const Pt & sz)554 void Wnd::SetMaxSize(const Pt& sz)
555 {
556     m_max_size = sz;
557     if (m_max_size.x < Width() || m_max_size.y < Height())
558         Resize(Pt(std::min(Width(), m_max_size.x), std::min(Height(), m_max_size.y)));
559 }
560 
AttachChild(std::shared_ptr<Wnd> wnd)561 void Wnd::AttachChild(std::shared_ptr<Wnd> wnd)
562 {
563     if (!wnd)
564         return;
565 
566     try {
567         // TODO use weak_from_this when converting to C++17
568         auto my_shared = shared_from_this();
569 
570         // Remove from previous parent.
571         if (auto&& parent = wnd->Parent())
572             parent->DetachChild(wnd.get());
573 
574         GUI::GetGUI()->Remove(wnd);
575         wnd->SetParent(my_shared);
576 
577         if (auto this_as_layout = std::dynamic_pointer_cast<Layout>(my_shared))
578             wnd->m_containing_layout = this_as_layout;
579 
580         m_children.push_back(std::forward<std::shared_ptr<Wnd>>(wnd));
581 
582     } catch (const std::bad_weak_ptr&) {
583         std::cerr << std::endl << "Wnd::AttachChild called either during the constructor "
584                   << "or after the destructor has run. Not attaching child."
585                   << std::endl << " parent = " << m_name << " child = " << wnd->m_name;
586         // Soft failure:
587         // Intentionally do nothing, to create minimal disruption to non-dev
588         // players if a dev accidentally puts an AttachChild in its own constructor.
589     }
590 }
591 
MoveChildUp(const std::shared_ptr<Wnd> & wnd)592 void Wnd::MoveChildUp(const std::shared_ptr<Wnd>& wnd)
593 { MoveChildUp(wnd.get()); }
594 
MoveChildUp(Wnd * wnd)595 void Wnd::MoveChildUp(Wnd* wnd)
596 {
597     if (!wnd)
598         return;
599     const auto it = std::find_if(m_children.begin(), m_children.end(),
600                                  [&wnd](const std::shared_ptr<Wnd>& x){ return x.get() == wnd; });
601     if (it == m_children.end())
602         return;
603     m_children.push_back(*it);
604     m_children.erase(it);
605 }
606 
MoveChildDown(const std::shared_ptr<Wnd> & wnd)607 void Wnd::MoveChildDown(const std::shared_ptr<Wnd>& wnd)
608 { MoveChildDown(wnd.get()); }
609 
MoveChildDown(Wnd * wnd)610 void Wnd::MoveChildDown(Wnd* wnd)
611 {
612     if (!wnd)
613         return;
614     auto found = std::find_if(m_children.begin(), m_children.end(),
615                               [&wnd](const std::shared_ptr<Wnd>& x){ return x.get() == wnd; });
616     if (found == m_children.end())
617         return;
618 
619     m_children.push_front(*found);
620     m_children.erase(found);
621 }
622 
DetachChild(const std::shared_ptr<Wnd> & wnd)623 void Wnd::DetachChild(const std::shared_ptr<Wnd>& wnd)
624 { DetachChild(wnd.get()); }
625 
DetachChild(Wnd * wnd)626 void Wnd::DetachChild(Wnd* wnd)
627 {
628     const auto it = std::find_if(m_children.begin(), m_children.end(),
629                                  [&wnd](const std::shared_ptr<Wnd>& x){ return x.get() == wnd; });
630     if (it == m_children.end())
631         return;
632 
633     DetachChildCore(wnd);
634 
635     m_children.erase(it);
636 }
637 
DetachChildCore(Wnd * wnd)638 void Wnd::DetachChildCore(Wnd* wnd)
639 {
640     if (!wnd)
641         return;
642 
643     wnd->m_parent.reset();
644 
645     auto&& layout = GetLayout();
646     if (layout && wnd == layout.get())
647         m_layout.reset();
648 
649     if (auto this_as_layout = dynamic_cast<Layout*>(this)) {
650         this_as_layout->Remove(wnd);
651         wnd->m_containing_layout.reset();
652     }
653 }
654 
DetachChildren()655 void Wnd::DetachChildren()
656 {
657     m_layout.reset();
658 
659     for (auto& wnd : m_children) {
660         DetachChildCore(wnd.get());
661     }
662     m_children.clear();
663 }
664 
InstallEventFilter(const std::shared_ptr<Wnd> & wnd)665 void Wnd::InstallEventFilter(const std::shared_ptr<Wnd>& wnd)
666 {
667     if (!wnd)
668         return;
669     RemoveEventFilter(wnd);
670     m_filters.push_back(std::weak_ptr<Wnd>(wnd));
671     wnd->m_filtering.insert(shared_from_this());
672 }
673 
RemoveEventFilter(const std::shared_ptr<Wnd> & wnd)674 void Wnd::RemoveEventFilter(const std::shared_ptr<Wnd>& wnd)
675 {
676     if (!wnd)
677         return;
678     const auto& it = std::find_if(m_filters.begin(), m_filters.end(),
679                                   [&wnd](const std::weak_ptr<Wnd>& x){ return x.lock() == wnd; });
680     if (it != m_filters.end())
681         m_filters.erase(it);
682     wnd->m_filtering.erase(shared_from_this());
683 }
684 
HorizontalLayout()685 void Wnd::HorizontalLayout()
686 {
687     RemoveLayout();
688 
689     std::multiset<std::shared_ptr<Wnd>, WndHorizontalLess> wnds;
690     Pt client_sz = ClientSize();
691     for (auto& child : m_children) {
692         Pt wnd_ul = child->RelativeUpperLeft(), wnd_lr = child->RelativeLowerRight();
693         if (wnd_ul.x < 0 || wnd_ul.y < 0 || client_sz.x < wnd_lr.x || client_sz.y < wnd_lr.y)
694             continue;
695         wnds.insert(child);
696     }
697 
698     auto layout = Wnd::Create<Layout>(X0, Y0, ClientSize().x, ClientSize().y,
699                                       1, wnds.size(),
700                                       DEFAULT_LAYOUT_BORDER_MARGIN, DEFAULT_LAYOUT_CELL_MARGIN);
701     m_layout = layout;
702     AttachChild(layout);
703 
704     int i = 0;
705     for (const auto& wnd : wnds) {
706         layout->Add(wnd, 0, i++);
707     }
708 }
709 
VerticalLayout()710 void Wnd::VerticalLayout()
711 {
712     RemoveLayout();
713 
714     std::multiset<std::shared_ptr<Wnd>, WndVerticalLess> wnds;
715     Pt client_sz = ClientSize();
716     for (auto& child : m_children) {
717         Pt wnd_ul = child->RelativeUpperLeft(), wnd_lr = child->RelativeLowerRight();
718         if (wnd_ul.x < 0 || wnd_ul.y < 0 || client_sz.x < wnd_lr.x || client_sz.y < wnd_lr.y)
719             continue;
720         wnds.insert(child);
721     }
722 
723     auto layout = Wnd::Create<Layout>(X0, Y0, ClientSize().x, ClientSize().y,
724                                       wnds.size(), 1,
725                                       DEFAULT_LAYOUT_BORDER_MARGIN, DEFAULT_LAYOUT_CELL_MARGIN);
726     m_layout = layout;
727     AttachChild(layout);
728 
729     int i = 0;
730     for (auto& wnd : wnds) {
731         layout->Add(wnd, i++, 0);
732     }
733 }
734 
GridLayout()735 void Wnd::GridLayout()
736 {
737     RemoveLayout();
738 
739     Pt client_sz = ClientSize();
740 
741     GridLayoutWndContainer grid_layout;
742 
743     // validate existing children and place them in a grid with one cell per pixel
744     for (auto it = m_children.begin(); it != m_children.end(); ++it) {
745         auto& wnd = *it;
746         Pt wnd_ul = wnd->RelativeUpperLeft(), wnd_lr = wnd->RelativeLowerRight();
747         if (wnd_ul.x < 0 || wnd_ul.y < 0 || client_sz.x < wnd_lr.x || client_sz.y < wnd_lr.y)
748             continue;
749 
750         auto it2 = it;
751         ++it2;
752         for (; it2 != m_children.end(); ++it2) {
753             Rect other_wnd_rect((*it2)->RelativeUpperLeft(), (*it2)->RelativeLowerRight());
754             if (other_wnd_rect.Contains(wnd_ul) || other_wnd_rect.Contains(wnd_lr - Pt(X1, Y1)))
755                 throw BadLayout("Wnd::GridLayout() : Two or more child windows overlap");
756         }
757 
758         grid_layout.insert(GridLayoutWnd(wnd, wnd_ul, wnd_lr));
759     }
760 
761 
762     // align left sides of windows
763     for (LeftIter it = grid_layout.get<LayoutLeft>().begin();
764          it != grid_layout.get<LayoutLeft>().end(); ++it)
765     {
766         Pt ul = it->ul;
767         for (X x = ul.x - 1; x >= 0; --x) {
768             if (grid_layout.get<LayoutRight>().count(x + 1, IsRight())) {
769                 break;
770             } else if (grid_layout.get<LayoutLeft>().count(x, IsLeft())) {
771                 GridLayoutWnd grid_wnd = *it;
772                 grid_wnd.ul.x = x;
773                 grid_layout.get<LayoutLeft>().replace(it, grid_wnd);
774                 break;
775             }
776         }
777     }
778 
779     // align right sides of windows
780     for (RightIter it = grid_layout.get<LayoutRight>().begin(); it != grid_layout.get<LayoutRight>().end(); ++it) {
781         Pt lr = it->lr;
782         for (X x = lr.x + 1; x < client_sz.x; ++x) {
783             if (grid_layout.get<LayoutLeft>().count(x - 1, IsLeft())) {
784                 break;
785             } else if (grid_layout.get<LayoutRight>().count(x, IsRight())) {
786                 GridLayoutWnd grid_wnd = *it;
787                 grid_wnd.lr.x = x;
788                 grid_layout.get<LayoutRight>().replace(it, grid_wnd);
789                 break;
790             }
791         }
792     }
793 
794     // align tops of windows
795     for (TopIter it = grid_layout.get<LayoutTop>().begin(); it != grid_layout.get<LayoutTop>().end(); ++it) {
796         Pt ul = it->ul;
797         for (Y y = ul.y - 1; y >= 0; --y) {
798             if (grid_layout.get<LayoutBottom>().count(y + 1, IsBottom())) {
799                 break;
800             } else if (grid_layout.get<LayoutTop>().count(y, IsTop())) {
801                 GridLayoutWnd grid_wnd = *it;
802                 grid_wnd.ul.y = y;
803                 grid_layout.get<LayoutTop>().replace(it, grid_wnd);
804                 break;
805             }
806         }
807     }
808 
809     // align bottoms of windows
810     for (BottomIter it = grid_layout.get<LayoutBottom>().begin(); it != grid_layout.get<LayoutBottom>().end(); ++it) {
811         Pt lr = it->lr;
812         for (Y y = lr.y + 1; y < client_sz.y; ++y) {
813             if (grid_layout.get<LayoutTop>().count(y - 1, IsTop())) {
814                 break;
815             } else if (grid_layout.get<LayoutBottom>().count(y, IsBottom())) {
816                 GridLayoutWnd grid_wnd = *it;
817                 grid_wnd.lr.y = y;
818                 grid_layout.get<LayoutBottom>().replace(it, grid_wnd);
819                 break;
820             }
821         }
822     }
823 
824     // create an actual layout with a more reasonable number of cells from the pixel-grid layout
825     std::set<X> unique_lefts;
826     std::set<Y> unique_tops;
827     for (const GridLayoutWnd& layout_wnd : grid_layout.get<LayoutLeft>()) {
828         unique_lefts.insert(layout_wnd.ul.x);
829     }
830     for (const GridLayoutWnd& layout_wnd : grid_layout.get<LayoutTop>()) {
831         unique_tops.insert(layout_wnd.ul.y);
832     }
833 
834     if (unique_lefts.empty() || unique_tops.empty())
835         return;
836 
837     auto layout = Wnd::Create<Layout>(X0, Y0, ClientSize().x, ClientSize().y,
838                                       unique_tops.size(), unique_lefts.size(),
839                                       DEFAULT_LAYOUT_BORDER_MARGIN, DEFAULT_LAYOUT_CELL_MARGIN);
840     m_layout = layout;
841     AttachChild(layout);
842 
843     // populate this new layout with the child windows, based on their placements in the pixel-grid layout
844     for (const GridLayoutWnd& layout_wnd : grid_layout.get<Pointer>()) {
845         auto& wnd = layout_wnd.wnd;
846         Pt ul = layout_wnd.ul;
847         Pt lr = layout_wnd.lr;
848         int left = std::distance(unique_lefts.begin(), unique_lefts.find(ul.x));
849         int top = std::distance(unique_tops.begin(), unique_tops.find(ul.y));
850         int right = std::distance(unique_lefts.begin(), unique_lefts.lower_bound(lr.x));
851         int bottom = std::distance(unique_tops.begin(), unique_tops.lower_bound(lr.y));
852         layout->Add(wnd, top, left, bottom - top, right - left);
853     }
854 }
855 
SetLayout(const std::shared_ptr<Layout> & layout)856 void Wnd::SetLayout(const std::shared_ptr<Layout>& layout)
857 {
858     auto&& mm_layout = GetLayout();
859     if (layout == mm_layout || layout == LockAndResetIfExpired(m_containing_layout))
860         throw BadLayout("Wnd::SetLayout() : Attempted to set a Wnd's layout to be its current layout or the layout that contains the Wnd");
861     RemoveLayout();
862     auto children = m_children;
863     DetachChildren();
864     Pt client_sz = ClientSize();
865     for (auto& wnd : children) {
866         Pt wnd_ul = wnd->RelativeUpperLeft(), wnd_lr = wnd->RelativeLowerRight();
867         if (wnd_ul.x < 0 || wnd_ul.y < 0 || client_sz.x < wnd_lr.x || client_sz.y < wnd_lr.y)
868             AttachChild(wnd);
869     }
870     AttachChild(layout);
871     m_layout = layout;
872     layout->SizeMove(Pt(), Pt(ClientWidth(), ClientHeight()));
873 }
874 
RemoveLayout()875 void Wnd::RemoveLayout()
876 {
877     auto&& layout = GetLayout();
878     m_layout.reset();
879     if (!layout)
880         return;
881 
882     auto layout_children = layout->Children();
883     layout->DetachAndResetChildren();
884     for (auto& wnd : layout_children) {
885         AttachChild(wnd);
886     }
887 }
888 
DetachLayout()889 std::shared_ptr<Layout> Wnd::DetachLayout()
890 {
891     auto&& layout = GetLayout();
892     DetachChild(layout.get());
893     return layout;
894 }
895 
SetLayoutBorderMargin(unsigned int margin)896 void Wnd::SetLayoutBorderMargin(unsigned int margin)
897 {
898     if (auto&& layout = GetLayout())
899         layout->SetBorderMargin(margin);
900 }
901 
SetLayoutCellMargin(unsigned int margin)902 void Wnd::SetLayoutCellMargin(unsigned int margin)
903 {
904     if (auto&& layout = GetLayout())
905         layout->SetCellMargin(margin);
906 }
907 
PreRender()908 void Wnd::PreRender()
909 {
910     m_needs_prerender = false;
911     auto&& layout = GetLayout();
912     if (!layout)
913         return;
914     if (layout->m_needs_prerender)
915         layout->PreRender();
916 }
917 
RequirePreRender()918 void Wnd::RequirePreRender()
919 { m_needs_prerender = true; }
920 
Render()921 void Wnd::Render() {}
922 
Run()923 bool Wnd::Run()
924 {
925     bool retval = false;
926     auto&& parent = Parent();
927     if (!parent && m_flags & MODAL) {
928         GUI* gui = GUI::GetGUI();
929         gui->RegisterModal(shared_from_this());
930         ModalInit();
931         m_done = false;
932         gui->RunModal(shared_from_this(), m_done);
933         gui->Remove(shared_from_this());
934         retval = true;
935     }
936     return retval;
937 }
938 
EndRun()939 void Wnd::EndRun()
940 { m_done = true; }
941 
SetBrowseModeTime(unsigned int time,std::size_t mode)942 void Wnd::SetBrowseModeTime(unsigned int time, std::size_t mode/* = 0*/)
943 {
944     if (m_browse_modes.size() <= mode) {
945         if (m_browse_modes.empty()) {
946             m_browse_modes.resize(mode + 1);
947             for (std::size_t i = 0; i < m_browse_modes.size() - 1; ++i) {
948                 m_browse_modes[i].time = time;
949             }
950         } else {
951             std::size_t original_size = m_browse_modes.size();
952             m_browse_modes.resize(mode + 1);
953             for (std::size_t i = original_size; i < m_browse_modes.size() - 1; ++i) {
954                 m_browse_modes[i].time = m_browse_modes[original_size - 1].time;
955             }
956         }
957     }
958     m_browse_modes[mode].time = time;
959 }
960 
SetBrowseInfoWnd(const std::shared_ptr<BrowseInfoWnd> & wnd,std::size_t mode)961 void Wnd::SetBrowseInfoWnd(const std::shared_ptr<BrowseInfoWnd>& wnd, std::size_t mode/* = 0*/)
962 { m_browse_modes.at(mode).wnd = wnd; }
963 
ClearBrowseInfoWnd(std::size_t mode)964 void Wnd::ClearBrowseInfoWnd(std::size_t mode/* = 0*/)
965 { m_browse_modes.at(mode).wnd.reset(); }
966 
SetBrowseText(const std::string & text,std::size_t mode)967 void Wnd::SetBrowseText(const std::string& text, std::size_t mode/* = 0*/)
968 { m_browse_modes.at(mode).text = text; }
969 
SetBrowseModes(const std::vector<BrowseInfoMode> & modes)970 void Wnd::SetBrowseModes(const std::vector<BrowseInfoMode>& modes)
971 { m_browse_modes = modes; }
972 
SetStyleFactory(const std::shared_ptr<StyleFactory> & factory)973 void Wnd::SetStyleFactory(const std::shared_ptr<StyleFactory>& factory)
974 { m_style_factory = factory; }
975 
DefaultBrowseTime()976 unsigned int Wnd::DefaultBrowseTime()
977 { return s_default_browse_time; }
978 
SetDefaultBrowseTime(unsigned int time)979 void Wnd::SetDefaultBrowseTime(unsigned int time)
980 { s_default_browse_time = time; }
981 
DefaultBrowseInfoWnd()982 const std::shared_ptr<BrowseInfoWnd>& Wnd::DefaultBrowseInfoWnd()
983 { return s_default_browse_info_wnd; }
984 
SetDefaultBrowseInfoWnd(const std::shared_ptr<BrowseInfoWnd> & browse_info_wnd)985 void Wnd::SetDefaultBrowseInfoWnd(const std::shared_ptr<BrowseInfoWnd>& browse_info_wnd)
986 { s_default_browse_info_wnd = browse_info_wnd; }
987 
GetDragDropRenderingState() const988 Wnd::DragDropRenderingState Wnd::GetDragDropRenderingState() const
989 {
990     DragDropRenderingState retval = NOT_DRAGGED;
991     if (GUI::GetGUI()->DragDropWnd(this)) {
992         if (!Dragable() && !GUI::GetGUI()->RenderingDragDropWnds())
993              retval = IN_PLACE_COPY;
994         else if (GUI::GetGUI()->AcceptedDragDropWnd(this))
995             retval = DRAGGED_OVER_ACCEPTING_DROP_TARGET;
996         else
997             retval = DRAGGED_OVER_UNACCEPTING_DROP_TARGET;
998     }
999     return retval;
1000 }
1001 
LButtonDown(const Pt & pt,Flags<ModKey> mod_keys)1002 void Wnd::LButtonDown(const Pt& pt, Flags<ModKey> mod_keys)
1003 { if (!Interactive()) ForwardEventToParent(); }
1004 
LDrag(const Pt & pt,const Pt & move,Flags<ModKey> mod_keys)1005 void Wnd::LDrag(const Pt& pt, const Pt& move, Flags<ModKey> mod_keys)
1006 {
1007     if (Dragable())
1008         OffsetMove(move);
1009     else if (!Interactive())
1010         ForwardEventToParent();
1011 }
1012 
LButtonUp(const Pt & pt,Flags<ModKey> mod_keys)1013 void Wnd::LButtonUp(const Pt& pt, Flags<ModKey> mod_keys)
1014 { if (!Interactive()) ForwardEventToParent(); }
1015 
LClick(const Pt & pt,Flags<ModKey> mod_keys)1016 void Wnd::LClick(const Pt& pt, Flags<ModKey> mod_keys)
1017 { if (!Interactive()) ForwardEventToParent(); }
1018 
LDoubleClick(const Pt & pt,Flags<ModKey> mod_keys)1019 void Wnd::LDoubleClick(const Pt& pt, Flags<ModKey> mod_keys)
1020 { LClick(pt, mod_keys); }
1021 
MButtonDown(const Pt & pt,Flags<ModKey> mod_keys)1022 void Wnd::MButtonDown(const Pt& pt, Flags<ModKey> mod_keys)
1023 { if (!Interactive()) ForwardEventToParent(); }
1024 
MDrag(const Pt & pt,const Pt & move,Flags<ModKey> mod_keys)1025 void Wnd::MDrag(const Pt& pt, const Pt& move, Flags<ModKey> mod_keys)
1026 { if (!Interactive()) ForwardEventToParent(); }
1027 
MButtonUp(const Pt & pt,Flags<ModKey> mod_keys)1028 void Wnd::MButtonUp(const Pt& pt, Flags<ModKey> mod_keys)
1029 { if (!Interactive()) ForwardEventToParent(); }
1030 
MClick(const Pt & pt,Flags<ModKey> mod_keys)1031 void Wnd::MClick(const Pt& pt, Flags<ModKey> mod_keys)
1032 { if (!Interactive()) ForwardEventToParent(); }
1033 
MDoubleClick(const Pt & pt,Flags<ModKey> mod_keys)1034 void Wnd::MDoubleClick(const Pt& pt, Flags<ModKey> mod_keys)
1035 { MClick(pt, mod_keys); }
1036 
RButtonDown(const Pt & pt,Flags<ModKey> mod_keys)1037 void Wnd::RButtonDown(const Pt& pt, Flags<ModKey> mod_keys)
1038 { if (!Interactive()) ForwardEventToParent(); }
1039 
RDrag(const Pt & pt,const Pt & move,Flags<ModKey> mod_keys)1040 void Wnd::RDrag(const Pt& pt, const Pt& move, Flags<ModKey> mod_keys)
1041 { if (!Interactive()) ForwardEventToParent(); }
1042 
RButtonUp(const Pt & pt,Flags<ModKey> mod_keys)1043 void Wnd::RButtonUp(const Pt& pt, Flags<ModKey> mod_keys)
1044 { if (!Interactive()) ForwardEventToParent(); }
1045 
RClick(const Pt & pt,Flags<ModKey> mod_keys)1046 void Wnd::RClick(const Pt& pt, Flags<ModKey> mod_keys)
1047 { if (!Interactive()) ForwardEventToParent(); }
1048 
RDoubleClick(const Pt & pt,Flags<ModKey> mod_keys)1049 void Wnd::RDoubleClick(const Pt& pt, Flags<ModKey> mod_keys)
1050 { RClick(pt, mod_keys); }
1051 
MouseEnter(const Pt & pt,Flags<ModKey> mod_keys)1052 void Wnd::MouseEnter(const Pt& pt, Flags<ModKey> mod_keys)
1053 { if (!Interactive()) ForwardEventToParent(); }
1054 
MouseHere(const Pt & pt,Flags<ModKey> mod_keys)1055 void Wnd::MouseHere(const Pt& pt, Flags<ModKey> mod_keys)
1056 { if (!Interactive()) ForwardEventToParent(); }
1057 
MouseLeave()1058 void Wnd::MouseLeave()
1059 { if (!Interactive()) ForwardEventToParent(); }
1060 
MouseWheel(const Pt & pt,int move,Flags<ModKey> mod_keys)1061 void Wnd::MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys)
1062 { if (!Interactive()) ForwardEventToParent(); }
1063 
DragDropEnter(const Pt & pt,std::map<const Wnd *,bool> & drop_wnds_acceptable,Flags<ModKey> mod_keys)1064 void Wnd::DragDropEnter(const Pt& pt, std::map<const Wnd*, bool>& drop_wnds_acceptable, Flags<ModKey> mod_keys)
1065 { if (!Interactive()) ForwardEventToParent(); }
1066 
DragDropHere(const Pt & pt,std::map<const Wnd *,bool> & drop_wnds_acceptable,Flags<ModKey> mod_keys)1067 void Wnd::DragDropHere(const Pt& pt, std::map<const Wnd*, bool>& drop_wnds_acceptable, Flags<ModKey> mod_keys)
1068 {
1069     if (!Interactive())
1070         ForwardEventToParent();
1071     this->DropsAcceptable(drop_wnds_acceptable.begin(), drop_wnds_acceptable.end(), pt, mod_keys);
1072 }
1073 
CheckDrops(const Pt & pt,std::map<const Wnd *,bool> & drop_wnds_acceptable,Flags<ModKey> mod_keys)1074 void Wnd::CheckDrops(const Pt& pt, std::map<const Wnd*, bool>& drop_wnds_acceptable,
1075                      Flags<ModKey> mod_keys)
1076 {
1077     if (!Interactive())
1078         ForwardEventToParent();
1079     this->DropsAcceptable(drop_wnds_acceptable.begin(), drop_wnds_acceptable.end(), pt, mod_keys);
1080 }
1081 
DragDropLeave()1082 void Wnd::DragDropLeave()
1083 { if (!Interactive()) ForwardEventToParent(); }
1084 
KeyPress(Key key,std::uint32_t key_code_point,Flags<ModKey> mod_keys)1085 void Wnd::KeyPress(Key key, std::uint32_t key_code_point, Flags<ModKey> mod_keys)
1086 { if (!Interactive()) ForwardEventToParent(); }
1087 
KeyRelease(Key key,std::uint32_t key_code_point,Flags<ModKey> mod_keys)1088 void Wnd::KeyRelease(Key key, std::uint32_t key_code_point, Flags<ModKey> mod_keys)
1089 { if (!Interactive()) ForwardEventToParent(); }
1090 
TextInput(const std::string * text)1091 void Wnd::TextInput(const std::string* text)
1092 { if (!Interactive()) ForwardEventToParent(); }
1093 
GainingFocus()1094 void Wnd::GainingFocus()
1095 {}
1096 
LosingFocus()1097 void Wnd::LosingFocus()
1098 {}
1099 
TimerFiring(unsigned int ticks,Timer * timer)1100 void Wnd::TimerFiring(unsigned int ticks, Timer* timer)
1101 {}
1102 
EventFilter(Wnd * w,const WndEvent & event)1103 bool Wnd::EventFilter(Wnd* w, const WndEvent& event)
1104 { return false; }
1105 
HandleEvent(const WndEvent & event)1106 void Wnd::HandleEvent(const WndEvent& event)
1107 {
1108     // Check if any of the filters block this event
1109     bool filtered = false;
1110     ProcessThenRemoveExpiredPtrs(
1111         m_filters,
1112         [&filtered, this, &event](std::shared_ptr<Wnd>& wnd)
1113         {
1114             if (!filtered)
1115                 filtered = wnd->EventFilter(this, event);
1116         });
1117 
1118     if (filtered)
1119         return;
1120 
1121     try {
1122         switch (event.Type()) {
1123         case WndEvent::LButtonDown:
1124             LButtonDown(event.Point(), event.ModKeys());
1125             break;
1126         case WndEvent::LDrag:
1127             LDrag(event.Point(), event.DragMove(), event.ModKeys());
1128             break;
1129         case WndEvent::LButtonUp:
1130             LButtonUp(event.Point(), event.ModKeys());
1131             break;
1132         case WndEvent::LClick:
1133             LClick(event.Point(), event.ModKeys());
1134             break;
1135         case WndEvent::LDoubleClick:
1136             LDoubleClick(event.Point(), event.ModKeys());
1137             break;
1138         case WndEvent::MButtonDown:
1139             MButtonDown(event.Point(), event.ModKeys());
1140             break;
1141         case WndEvent::MDrag:
1142             MDrag(event.Point(), event.DragMove(), event.ModKeys());
1143             break;
1144         case WndEvent::MButtonUp:
1145             MButtonUp(event.Point(), event.ModKeys());
1146             break;
1147         case WndEvent::MClick:
1148             MClick(event.Point(), event.ModKeys());
1149             break;
1150         case WndEvent::MDoubleClick:
1151             MDoubleClick(event.Point(), event.ModKeys());
1152             break;
1153         case WndEvent::RButtonDown:
1154             RButtonDown(event.Point(), event.ModKeys());
1155             break;
1156         case WndEvent::RDrag:
1157             RDrag(event.Point(), event.DragMove(), event.ModKeys());
1158             break;
1159         case WndEvent::RButtonUp:
1160             RButtonUp(event.Point(), event.ModKeys());
1161             break;
1162         case WndEvent::RClick:
1163             RClick(event.Point(), event.ModKeys());
1164             break;
1165         case WndEvent::RDoubleClick:
1166             RDoubleClick(event.Point(), event.ModKeys());
1167             break;
1168         case WndEvent::MouseEnter:
1169             MouseEnter(event.Point(), event.ModKeys());
1170             break;
1171         case WndEvent::MouseHere:
1172             MouseHere(event.Point(), event.ModKeys());
1173             break;
1174         case WndEvent::MouseLeave:
1175             MouseLeave();
1176             break;
1177         case WndEvent::DragDropEnter:
1178             DragDropEnter(event.Point(), event.GetAcceptableDropWnds(), event.ModKeys());
1179             break;
1180         case WndEvent::DragDropHere:
1181             DragDropHere(event.Point(), event.GetAcceptableDropWnds(), event.ModKeys());
1182             break;
1183         case WndEvent::CheckDrops:
1184             CheckDrops(event.Point(), event.GetAcceptableDropWnds(), event.ModKeys());
1185             break;
1186         case WndEvent::DragDropLeave:
1187             DragDropLeave();
1188             break;
1189         case WndEvent::DragDroppedOn:
1190             AcceptDrops(event.Point(), event.GetDragDropWnds(), event.ModKeys());
1191             break;
1192         case WndEvent::MouseWheel:
1193             MouseWheel(event.Point(), event.WheelMove(), event.ModKeys());
1194             break;
1195         case WndEvent::KeyPress:
1196             KeyPress(event.GetKey(), event.KeyCodePoint(), event.ModKeys());
1197             break;
1198         case WndEvent::KeyRelease:
1199             KeyRelease(event.GetKey(), event.KeyCodePoint(), event.ModKeys());
1200             break;
1201         case WndEvent::TextInput:
1202             TextInput(event.GetText());
1203             break;
1204         case WndEvent::GainingFocus:
1205             GainingFocus();
1206             break;
1207         case WndEvent::LosingFocus:
1208             LosingFocus();
1209             break;
1210         case WndEvent::TimerFiring:
1211             TimerFiring(event.Ticks(), event.GetTimer());
1212             break;
1213         default:
1214             break;
1215         }
1216     } catch (const ForwardToParentException&) {
1217         if (auto&& p = Parent())
1218             p->HandleEvent(event);
1219     }
1220 }
1221 
ForwardEventToParent()1222 void Wnd::ForwardEventToParent()
1223 { throw ForwardToParentException(); }
1224 
BeginClipping()1225 void Wnd::BeginClipping()
1226 {
1227     if (m_child_clipping_mode != DontClip)
1228         BeginClippingImpl(m_child_clipping_mode);
1229 }
1230 
EndClipping()1231 void Wnd::EndClipping()
1232 {
1233     if (m_child_clipping_mode != DontClip)
1234         EndClippingImpl(m_child_clipping_mode);
1235 }
1236 
BeginNonclientClipping()1237 void Wnd::BeginNonclientClipping()
1238 { BeginNonclientClippingImpl(); }
1239 
EndNonclientClipping()1240 void Wnd::EndNonclientClipping()
1241 { EndNonclientClippingImpl(); }
1242 
ValidateFlags()1243 void Wnd::ValidateFlags()
1244 {
1245     if ((m_flags & MODAL) && (m_flags & ONTOP))
1246         m_flags &= ~ONTOP;
1247 }
1248 
BeginClippingImpl(ChildClippingMode mode)1249 void Wnd::BeginClippingImpl(ChildClippingMode mode)
1250 {
1251     switch (mode) {
1252     case DontClip:
1253         assert(!"Wnd::BeginClippingImpl() called with mode == DontClip; this should never happen.");
1254         break;
1255     case ClipToClient:
1256     case ClipToClientAndWindowSeparately:
1257         BeginScissorClipping(ClientUpperLeft(), ClientLowerRight());
1258         break;
1259     case ClipToWindow:
1260         BeginScissorClipping(UpperLeft(), LowerRight());
1261         break;
1262     }
1263 }
1264 
EndClippingImpl(ChildClippingMode mode)1265 void Wnd::EndClippingImpl(ChildClippingMode mode)
1266 {
1267     switch (mode) {
1268     case DontClip:
1269         assert(!"Wnd::EndClippingImpl() called with mode == DontClip; this should never happen.");
1270         break;
1271     case ClipToClient:
1272     case ClipToWindow:
1273     case ClipToClientAndWindowSeparately:
1274         EndScissorClipping();
1275         break;
1276     }
1277 }
1278 
BeginNonclientClippingImpl()1279 void Wnd::BeginNonclientClippingImpl()
1280 { BeginStencilClipping(ClientUpperLeft(), ClientLowerRight(), UpperLeft(), LowerRight()); }
1281 
EndNonclientClippingImpl()1282 void Wnd::EndNonclientClippingImpl()
1283 { EndStencilClipping(); }
1284 
SetParent(const std::shared_ptr<Wnd> & wnd)1285 void Wnd::SetParent(const std::shared_ptr<Wnd>& wnd)
1286 { m_parent = wnd; }
1287