1 /* Copyright (C) 2014 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * 0 A.D. 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "precompiled.h"
19
20 #include "EditableListCtrl.h"
21
22 #include "EditableListCtrlCommands.h"
23 #include "FieldEditCtrl.h"
24 #include "General/AtlasWindowCommandProc.h"
25 #include "AtlasObject/AtlasObject.h"
26 #include "AtlasObject/AtlasObjectText.h"
27 #include "General/AtlasClipboard.h"
28
29 const int BlanksAtEnd = 2;
30
EditableListCtrl(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)31 EditableListCtrl::EditableListCtrl(wxWindow *parent,
32 wxWindowID id,
33 const wxPoint& pos,
34 const wxSize& size,
35 long style,
36 const wxValidator& validator,
37 const wxString& name)
38 : wxListCtrl(parent, id, pos, size, style | wxLC_VIRTUAL, validator, name)
39 {
40 m_ListItemAttr[0].SetBackgroundColour(wxColor(0xff, 0xff, 0xff));
41 m_ListItemAttr[1].SetBackgroundColour(wxColor(0xee, 0xee, 0xee));
42
43 wxASSERT_MSG(style & wxLC_REPORT, _T("EditableListCtrl must be LC_REPORT"));
44 UpdateDisplay();
45 }
46
~EditableListCtrl()47 EditableListCtrl::~EditableListCtrl()
48 {
49 size_t count = m_ColumnTypes.size();
50 for (size_t n = 0; n < count; ++n)
51 delete (FieldEditCtrl*)m_ColumnTypes[n].ctrl;
52
53 m_ColumnTypes.clear();
54 }
55
AddColumnType(const wxString & title,int width,const char * objectkey,FieldEditCtrl * ctrl)56 void EditableListCtrl::AddColumnType(const wxString& title, int width, const char* objectkey, FieldEditCtrl* ctrl)
57 {
58 int n = GetColumnCount();
59 wxASSERT(m_ColumnTypes.size() == (size_t) n); // check internal consistency
60
61 InsertColumn(n, title, wxLIST_FORMAT_LEFT, width);
62
63 m_ColumnTypes.push_back(ColumnData(objectkey, ctrl));
64 }
65
66
OnMouseEvent(wxMouseEvent & event)67 void EditableListCtrl::OnMouseEvent(wxMouseEvent& event)
68 {
69 // Double-clicking/right-clicking on a cell lets the user edit it.
70 // The editing method depends on what column the cell is in.
71
72 if (event.LeftDClick() || event.RightDown())
73 {
74 // Work out what cell was clicked on:
75
76 wxPoint pt = event.GetPosition();
77 int col = GetColumnAtPosition(pt);
78
79 wxCHECK2(col >= 0 && col < (int)m_ColumnTypes.size(), return);
80
81 int flags;
82 long row = HitTest(pt, flags);
83
84 if (row != wxNOT_FOUND && (flags & wxLIST_HITTEST_ONITEM))
85 {
86 // Calculate the exact positioning of the clicked cell
87 wxRect rect;
88 GetCellRect(row, col, rect);
89
90 // Execute the appropriate FieldEditCtrl
91 FieldEditCtrl* editor = (FieldEditCtrl*)m_ColumnTypes[col].ctrl;
92 editor->StartEdit(this, rect, row, col);
93 }
94 }
95 }
96
OnKeyDown(wxKeyEvent & event)97 void EditableListCtrl::OnKeyDown(wxKeyEvent& event)
98 {
99 // TODO: Don't use magic key-code numbers
100
101 // Check for Copy
102 if ((event.GetKeyCode() == 3) || // ctrl+c
103 (event.GetKeyCode() == WXK_INSERT && event.ControlDown())) // ctrl+insert
104 {
105 AtObj row;
106 long selection = GetSelection();
107 if (selection >= 0 && selection < (long)m_ListData.size())
108 row = m_ListData[selection];
109 AtlasClipboard::SetClipboard(row);
110 }
111
112 // Check for Paste
113 else
114 if ((event.GetKeyCode() == 22) || // ctrl+v
115 (event.GetKeyCode() == WXK_INSERT && event.ShiftDown())) // shift+insert
116 {
117 AtObj row;
118 if (AtlasClipboard::GetClipboard(row))
119 {
120 long selection = GetSelection();
121 AtlasWindowCommandProc* commandProc = AtlasWindowCommandProc::GetFromParentFrame(this);
122 commandProc->Submit(new PasteCommand(this, selection, row));
123 }
124 }
125 else
126 event.Skip();
127 }
128
GetColumnAtPosition(wxPoint & pos)129 int EditableListCtrl::GetColumnAtPosition(wxPoint& pos)
130 {
131 // Find the column which pos is in.
132
133 // Get the origin of the table, in case it's scrolled horizontally
134 wxRect rect;
135 GetItemRect(0, rect);
136 int x = rect.GetX();
137
138 // Loop through each column
139 int numCols = GetColumnCount();
140 for (int i = 0; i < numCols; ++i)
141 {
142 // Calculate the position of this column's right-hand edge
143 x += GetColumnWidth(i);
144
145 // Test if pos was within this column (and assume it wasn't in an earlier one)
146 if (pos.x <= x)
147 return i;
148 }
149
150 // Point is outside the table's right edge
151 return -1;
152 }
153
GetCellRect(long row,int col,wxRect & rect)154 void EditableListCtrl::GetCellRect(long row, int col, wxRect& rect)
155 {
156 wxASSERT(col >= 0 && col < GetColumnCount());
157 wxASSERT(row >= 0 && row < GetItemCount());
158
159 GetItemRect(row, rect);
160
161 for (int i = 0; i < col; ++i)
162 rect.x += GetColumnWidth(i);
163
164 rect.width = GetColumnWidth(col);
165 }
166
167
IsRowBlank(int n)168 bool EditableListCtrl::IsRowBlank(int n)
169 {
170 return ! m_ListData[n].hasContent();
171 }
172
TrimBlankEnds()173 void EditableListCtrl::TrimBlankEnds()
174 {
175 while (m_ListData.size() && !m_ListData.back().defined())
176 m_ListData.pop_back();
177 }
178
179
SetSelection(long item)180 void EditableListCtrl::SetSelection(long item)
181 {
182 SetItemState(item, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
183 }
184
GetSelection()185 long EditableListCtrl::GetSelection()
186 {
187 for (long item = 0; item < GetItemCount(); ++item)
188 if (GetItemState(item, wxLIST_STATE_SELECTED))
189 return item;
190 return 0;
191 }
192
193
MakeSizeAtLeast(int n)194 void EditableListCtrl::MakeSizeAtLeast(int n)
195 {
196 if ((int)m_ListData.size() < n)
197 m_ListData.resize(n);
198 }
199
AddRow(AtObj & obj)200 void EditableListCtrl::AddRow(AtObj& obj)
201 {
202 m_ListData.push_back(obj);
203 }
204
AddRow(AtIter & iter)205 void EditableListCtrl::AddRow(AtIter& iter)
206 {
207 AtObj obj = *iter;
208 AddRow(obj);
209 }
210
UpdateDisplay()211 void EditableListCtrl::UpdateDisplay()
212 {
213 TrimBlankEnds();
214 SetItemCount((int)m_ListData.size() + BlanksAtEnd);
215 Refresh();
216 }
217
218
CloneListData(std::vector<AtObj> & out)219 void EditableListCtrl::CloneListData(std::vector<AtObj>& out)
220 {
221 out = m_ListData;
222 }
223
SetListData(std::vector<AtObj> & in)224 void EditableListCtrl::SetListData(std::vector<AtObj>& in)
225 {
226 m_ListData = in;
227 }
228
DeleteData()229 void EditableListCtrl::DeleteData()
230 {
231 m_ListData.clear();
232 }
233
GetCellString(long item,long column) const234 wxString EditableListCtrl::GetCellString(long item, long column) const
235 {
236 wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), _T(""));
237
238 if (item >= (int)m_ListData.size())
239 return _T("");
240
241 AtObj cell = *m_ListData[item][m_ColumnTypes[column].key];
242 return AtlasObject::ConvertToString(cell).c_str();
243 }
244
GetCellObject(long item,long column) const245 AtObj EditableListCtrl::GetCellObject(long item, long column) const
246 {
247 wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), AtObj());
248
249 if (item >= (int)m_ListData.size())
250 return AtObj();
251
252 return *m_ListData[item][m_ColumnTypes[column].key];
253 }
254
SetCellString(long item,long column,wxString & str)255 void EditableListCtrl::SetCellString(long item, long column, wxString& str)
256 {
257 wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), );
258 MakeSizeAtLeast(item+1);
259 m_ListData[item].set(m_ColumnTypes[column].key, str);
260 }
261
SetCellObject(long item,long column,AtObj & obj)262 void EditableListCtrl::SetCellObject(long item, long column, AtObj& obj)
263 {
264 wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), );
265 MakeSizeAtLeast(item+1);
266 m_ListData[item].set(m_ColumnTypes[column].key, obj);
267 }
268
OnGetItemText(long item,long column) const269 wxString EditableListCtrl::OnGetItemText(long item, long column) const
270 {
271 return GetCellString(item, column);
272 }
273
OnGetItemAttr(long item) const274 wxListItemAttr* EditableListCtrl::OnGetItemAttr(long item) const
275 {
276 // Make the last two rows white
277 if (item >= (long)m_ListData.size())
278 return const_cast<wxListItemAttr*>(&m_ListItemAttr[0]);
279
280 // Make the background colors of rows alternate
281 else
282 return const_cast<wxListItemAttr*>(&m_ListItemAttr[item%2]);
283 }
284
ImportData(AtObj & in)285 void EditableListCtrl::ImportData(AtObj& in)
286 {
287 return DoImport(in);
288 }
289
ExportData()290 AtObj EditableListCtrl::ExportData()
291 {
292 return DoExport();
293 }
294
295
ThawData(AtObj & in)296 void EditableListCtrl::ThawData(AtObj& in)
297 {
298 m_ListData.clear();
299 for (AtIter it = in["item"]; it.defined(); ++it)
300 m_ListData.push_back(*it);
301 UpdateDisplay();
302 }
303
FreezeData()304 AtObj EditableListCtrl::FreezeData()
305 {
306 AtObj out;
307 for (std::vector<AtObj>::iterator it = m_ListData.begin(); it != m_ListData.end(); ++it)
308 out.add("item", *it);
309 return out;
310 }
311
312
313
314 BEGIN_EVENT_TABLE(EditableListCtrl, wxListCtrl)
315 EVT_LEFT_DCLICK(EditableListCtrl::OnMouseEvent)
316 EVT_RIGHT_DOWN(EditableListCtrl::OnMouseEvent)
317 EVT_CHAR(EditableListCtrl::OnKeyDown)
318 END_EVENT_TABLE()
319