1 #include "filezilla.h"
2 #include "drop_target_ex.h"
3 
4 #include "listctrlex.h"
5 #include "treectrlex.h"
6 
7 template<class Control>
CScrollableDropTarget(Control * pCtrl)8 CScrollableDropTarget<Control>::CScrollableDropTarget(Control* pCtrl)
9 	: m_pCtrl(pCtrl)
10 {
11 	m_timer.SetOwner(this);
12 }
13 
14 
15 template<class Control>
OnDrop(wxCoord,wxCoord)16 bool CScrollableDropTarget<Control>::OnDrop(wxCoord, wxCoord)
17 {
18 	m_timer.Stop();
19 	return true;
20 }
21 
22 template<class Control>
OnDragOver(wxCoord x,wxCoord y,wxDragResult def)23 wxDragResult CScrollableDropTarget<Control>::OnDragOver(wxCoord x, wxCoord y, wxDragResult def)
24 {
25 	def = FixupDragResult(def);
26 	if (!m_timer.IsRunning() && IsScroll(wxPoint(x, y))) {
27 		m_timer.Start(100, true);
28 		m_count = 0;
29 	}
30 	return def;
31 }
32 
33 template<class Control>
OnLeave()34 void CScrollableDropTarget<Control>::OnLeave()
35 {
36 	m_timer.Stop();
37 }
38 
39 template<class Control>
OnEnter(wxCoord x,wxCoord y,wxDragResult def)40 wxDragResult CScrollableDropTarget<Control>::OnEnter(wxCoord x, wxCoord y, wxDragResult def)
41 {
42 	def = FixupDragResult(def);
43 	if (!m_timer.IsRunning() && IsScroll(wxPoint(x, y))) {
44 		m_timer.Start(100, true);
45 		m_count = 0;
46 	}
47 	return def;
48 }
49 
50 template<class Control>
IsScroll(wxPoint p) const51 bool CScrollableDropTarget<Control>::IsScroll(wxPoint p) const
52 {
53 	return IsTopScroll(p) || IsBottomScroll(p);
54 }
55 
56 template<class Control>
IsTopScroll(wxPoint p) const57 bool CScrollableDropTarget<Control>::IsTopScroll(wxPoint p) const
58 {
59 	if (!m_pCtrl->GetItemCount()) {
60 		return false;
61 	}
62 
63 	wxRect itemRect;
64 	if (!m_pCtrl->GetItemRect(m_pCtrl->GetTopItem(), itemRect)) {
65 		return false;
66 	}
67 
68 	wxRect windowRect = m_pCtrl->GetActualClientRect();
69 
70 	if (itemRect.GetTop() < 0) {
71 		itemRect.SetTop(0);
72 	}
73 	if (itemRect.GetHeight() > windowRect.GetHeight() / 4) {
74 		itemRect.SetHeight(wxMax(windowRect.GetHeight() / 4, 8));
75 	}
76 
77 	if (p.y < 0 || p.y >= itemRect.GetBottom()) {
78 		return false;
79 	}
80 
81 	if (p.x < 0 || p.x > windowRect.GetWidth()) {
82 		return false;
83 	}
84 
85 	auto top = m_pCtrl->GetTopItem();
86 	if (!m_pCtrl->Valid(top) || top == m_pCtrl->GetFirstItem()) {
87 		return false;
88 	}
89 
90 	wxASSERT(m_pCtrl->GetTopItem() != m_pCtrl->GetFirstItem());
91 
92 	return true;
93 }
94 
95 template<class Control>
IsBottomScroll(wxPoint p) const96 bool CScrollableDropTarget<Control>::IsBottomScroll(wxPoint p) const
97 {
98 	if (!m_pCtrl->GetItemCount()) {
99 		return false;
100 	}
101 
102 	wxRect itemRect;
103 	if (!m_pCtrl->GetItemRect(m_pCtrl->GetFirstItem(), itemRect)) {
104 		return false;
105 	}
106 
107 	wxRect const windowRect = m_pCtrl->GetActualClientRect();
108 
109 	int scrollHeight = itemRect.GetHeight();
110 	if (scrollHeight > windowRect.GetHeight() / 4) {
111 		scrollHeight = wxMax(windowRect.GetHeight() / 4, 8);
112 	}
113 
114 	if (p.y > windowRect.GetBottom() || p.y < windowRect.GetBottom() - scrollHeight) {
115 		return false;
116 	}
117 
118 	if (p.x < 0 || p.x > windowRect.GetWidth()) {
119 		return false;
120 	}
121 
122 	auto bottom = m_pCtrl->GetBottomItem();
123 	if (!m_pCtrl->Valid(bottom) || bottom == m_pCtrl->GetLastItem()) {
124 		return false;
125 	}
126 
127 	wxASSERT(m_pCtrl->GetLastItem() != m_pCtrl->GetBottomItem());
128 
129 	return true;
130 }
131 
132 template<class Control>
OnTimer(wxTimerEvent &)133 void CScrollableDropTarget<Control>::OnTimer(wxTimerEvent& /*event*/)
134 {
135 	if (!m_pCtrl->GetItemCount()) {
136 		return;
137 	}
138 
139 	wxPoint p = wxGetMousePosition();
140 	wxWindow* ctrl = m_pCtrl->GetMainWindow();
141 	p = ctrl->ScreenToClient(p);
142 
143 	if (IsTopScroll(p)) {
144 		auto top = m_pCtrl->GetTopItem();
145 		wxASSERT(m_pCtrl->Valid(top));
146 		wxASSERT(top != m_pCtrl->GetFirstItem());
147 		m_pCtrl->EnsureVisible(m_pCtrl->GetPrevItemSimple(top));
148 	}
149 	else if (IsBottomScroll(p)) {
150 		auto bottom = m_pCtrl->GetBottomItem();
151 		wxASSERT(m_pCtrl->Valid(bottom));
152 		wxASSERT(bottom != m_pCtrl->GetLastItem());
153 		m_pCtrl->EnsureVisible(m_pCtrl->GetNextItemSimple(bottom));
154 	}
155 	else {
156 		return;
157 	}
158 
159 	DisplayDropHighlight(p);
160 
161 	if (m_count < 90) {
162 		++m_count;
163 	}
164 	m_timer.Start(100 - m_count, true);
165 }
166 
167 template<class Control>
FixupDragResult(wxDragResult res)168 wxDragResult CScrollableDropTarget<Control>::FixupDragResult(wxDragResult res)
169 {
170 #ifdef __WXMAC__
171 	if (res == wxDragNone && wxGetKeyState(WXK_CONTROL)) {
172 		res = wxDragCopy;
173 	}
174 #endif
175 
176 	if (res == wxDragLink) {
177 		res = wxGetKeyState(WXK_CONTROL) ? wxDragCopy : wxDragMove;
178 	}
179 
180 	return res;
181 }
182 
183 BEGIN_EVENT_TABLE_TEMPLATE1(CScrollableDropTarget, wxEvtHandler, Control)
184 EVT_TIMER(wxID_ANY, CScrollableDropTarget::OnTimer)
185 END_EVENT_TABLE()
186 
187 template class CScrollableDropTarget<wxTreeCtrlEx>;
188 template class CScrollableDropTarget<wxListCtrlEx>;
189