1 #include "filezilla.h"
2 #include "customheightlistctrl.h"
3
4 #include <algorithm>
5
BEGIN_EVENT_TABLE(wxCustomHeightListCtrl,wxScrolledWindow)6 BEGIN_EVENT_TABLE(wxCustomHeightListCtrl, wxScrolledWindow)
7 EVT_MOUSE_EVENTS(wxCustomHeightListCtrl::OnMouseEvent)
8 EVT_SIZE(wxCustomHeightListCtrl::OnSize)
9 END_EVENT_TABLE()
10
11 wxCustomHeightListCtrl::wxCustomHeightListCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name)
12 : wxScrolledWindow(parent, id, pos, size, style, name)
13 {
14 }
15
SetLineHeight(int height)16 void wxCustomHeightListCtrl::SetLineHeight(int height)
17 {
18 m_lineHeight = height;
19
20 int posx, posy;
21 GetViewStart(&posx, &posy);
22 SetScrollbars(0, m_lineHeight, 0, m_rows.size(), 0, posy);
23
24 Refresh();
25 }
26
AdjustView()27 void wxCustomHeightListCtrl::AdjustView()
28 {
29 int posx, posy;
30 GetViewStart(&posx, &posy);
31
32 #ifdef __WXGTK__
33 // When decreasing scrollbar range, wxGTK does not seem to adjust child position
34 // if viewport gets moved
35 wxPoint old_view;
36 GetViewStart(&old_view.x, &old_view.y);
37 #endif
38
39 SetScrollbars(0, m_lineHeight, 0, m_rows.size(), 0, posy);
40
41 #ifdef __WXGTK__
42 wxPoint new_view;
43 GetViewStart(&new_view.x, &new_view.y);
44 int delta_y = m_lineHeight *(old_view.y - new_view.y);
45
46 if (delta_y) {
47 wxWindowList::compatibility_iterator iter = GetChildren().GetFirst();
48 while (iter) {
49 wxWindow* child = iter->GetData();
50 wxPoint pos = child->GetPosition();
51 pos.y -= delta_y;
52 child->SetPosition(pos);
53
54 iter = iter->GetNext();
55 }
56 }
57 #endif
58 }
59
SetFocus()60 void wxCustomHeightListCtrl::SetFocus()
61 {
62 wxWindow::SetFocus();
63 }
64
OnDraw(wxDC & dc)65 void wxCustomHeightListCtrl::OnDraw(wxDC& dc)
66 {
67 wxSize size = GetClientSize();
68
69 dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
70 dc.SetPen(*wxTRANSPARENT_PEN);
71
72 for (auto const& selected : m_selectedLines) {
73 if (selected == m_focusedLine) {
74 dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxPENSTYLE_DOT));
75 }
76 else {
77 dc.SetPen(*wxTRANSPARENT_PEN);
78 }
79 dc.DrawRectangle(0, m_lineHeight * selected, size.GetWidth(), m_lineHeight);
80 }
81 if (m_focusedLine != npos && m_selectedLines.find(m_focusedLine) == m_selectedLines.end()) {
82 dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)));
83 dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxPENSTYLE_DOT));
84 dc.DrawRectangle(0, m_lineHeight * m_focusedLine, size.GetWidth(), m_lineHeight);
85 }
86 }
87
OnMouseEvent(wxMouseEvent & event)88 void wxCustomHeightListCtrl::OnMouseEvent(wxMouseEvent& event)
89 {
90 bool changed = false;
91 if (event.ButtonDown() && m_allow_selection) {
92 wxPoint pos = event.GetPosition();
93 int x, y;
94 CalcUnscrolledPosition(pos.x, pos.y, &x, &y);
95 if (y < 0 || y > static_cast<int>(m_lineHeight * m_rows.size())) {
96 m_focusedLine = npos;
97 m_selectedLines.clear();
98 changed = true;
99 }
100 else {
101 size_t line = static_cast<size_t>(y / m_lineHeight);
102
103 if (event.ShiftDown()) {
104 if (m_focusedLine == npos) {
105 changed |= m_selectedLines.insert(line).second;
106 }
107 else if (line < m_focusedLine) {
108 for (size_t i = line; i <= m_focusedLine; ++i) {
109 changed |= m_selectedLines.insert(i).second;
110 }
111 }
112 else {
113 for (size_t i = line; i >= m_focusedLine && i != npos; --i) {
114 changed |= m_selectedLines.insert(i).second;
115 }
116 }
117 }
118 else if (event.ControlDown()) {
119 if (m_selectedLines.find(line) == m_selectedLines.end()) {
120 m_selectedLines.insert(line);
121 }
122 else {
123 m_selectedLines.erase(line);
124 }
125 changed = true;
126 }
127 else {
128 m_selectedLines.clear();
129 m_selectedLines.insert(line);
130 changed = true;
131 }
132
133 m_focusedLine = line;
134 }
135 Refresh();
136 }
137
138 event.Skip();
139
140 if (changed) {
141 wxCommandEvent evt(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
142 ProcessEvent(evt);
143 }
144 }
145
ClearSelection()146 void wxCustomHeightListCtrl::ClearSelection()
147 {
148 m_selectedLines.clear();
149 m_focusedLine = npos;
150
151 AdjustView();
152 Refresh();
153 }
154
GetSelection() const155 std::set<size_t> wxCustomHeightListCtrl::GetSelection() const
156 {
157 return m_selectedLines;
158 }
159
GetRowCount() const160 size_t wxCustomHeightListCtrl::GetRowCount() const
161 {
162 return m_rows.size();
163 }
164
SelectLine(size_t line)165 void wxCustomHeightListCtrl::SelectLine(size_t line)
166 {
167 if (!m_allow_selection) {
168 return;
169 }
170
171 m_selectedLines.clear();
172 m_focusedLine = line;
173 if (line != npos) {
174 m_selectedLines.insert(line);
175 }
176
177 Refresh();
178 }
179
AllowSelection(bool allow_selection)180 void wxCustomHeightListCtrl::AllowSelection(bool allow_selection)
181 {
182 m_allow_selection = allow_selection;
183 if (!allow_selection) {
184 ClearSelection();
185 }
186 }
187
InsertRow(wxSizer * sizer,size_t pos)188 void wxCustomHeightListCtrl::InsertRow(wxSizer* sizer, size_t pos)
189 {
190 assert(sizer);
191 assert(pos <= m_rows.size());
192 m_rows.insert(m_rows.begin() + pos, sizer);
193
194 sizer->SetContainingWindow(this);
195
196 AdjustView();
197
198 int left = 0;
199 int top = 0;
200 CalcScrolledPosition(0, 0, &left, &top);
201
202 int const width = GetClientSize().GetWidth();
203 for (size_t i = pos; i < m_rows.size(); ++i) {
204 m_rows[i]->SetDimension(left, m_lineHeight * i + top, width, m_lineHeight);
205 }
206
207 Refresh();
208
209 #ifdef __WXGTK__
210 // Needed for Ubuntu's shitty overlay scrollbar, it changes the layout asynchronously...
211 CallAfter([this](){
212 wxSizeEvent ev;
213 OnSize(ev);
214 });
215 #endif
216 }
217
DeleteRow(wxSizer * row)218 void wxCustomHeightListCtrl::DeleteRow(wxSizer *row)
219 {
220 auto it = std::find(m_rows.begin(), m_rows.end(), row);
221 if (it != m_rows.end()) {
222 DeleteRow(it - m_rows.begin());
223 }
224 }
225
DeleteRow(size_t pos)226 void wxCustomHeightListCtrl::DeleteRow(size_t pos)
227 {
228 assert(pos < m_rows.size());
229 m_rows[pos]->SetContainingWindow(0);
230 m_rows.erase(m_rows.begin() + pos);
231
232 std::set<size_t> selectedLines;
233 m_selectedLines.swap(selectedLines);
234 for (auto const& selected : selectedLines) {
235 if (selected < m_rows.size()) {
236 m_selectedLines.insert(selected);
237 }
238 }
239
240 AdjustView();
241
242 if (m_focusedLine >= m_rows.size()) {
243 m_focusedLine = npos;
244 }
245
246 int left = 0;
247 int top = 0;
248 CalcScrolledPosition(0, 0, &left, &top);
249
250 int const width = GetClientSize().GetWidth();
251
252 // Intentionally update all: if y position changes, by the time OnSize is called in
253 // response to AdjustView, internal state isn't quite correct
254 for (size_t i = 0; i < m_rows.size(); ++i) {
255 m_rows[i]->SetDimension(left, m_lineHeight * i + top, width, m_lineHeight);
256 }
257
258 Refresh();
259 }
260
ClearRows()261 void wxCustomHeightListCtrl::ClearRows()
262 {
263 m_rows.clear();
264 }
265
OnSize(wxSizeEvent & event)266 void wxCustomHeightListCtrl::OnSize(wxSizeEvent& event)
267 {
268 event.Skip();
269
270 int const width = GetClientSize().GetWidth();
271
272 int left = 0;
273 int top = 0;
274 CalcScrolledPosition(0, 0, &left, &top);
275 for (size_t i = 0; i < m_rows.size(); ++i) {
276 m_rows[i]->SetDimension(left, m_lineHeight * i + top, width, m_lineHeight);
277 }
278 }
279