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