/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "EditableListCtrl.h"
#include "EditableListCtrlCommands.h"
#include "FieldEditCtrl.h"
#include "General/AtlasWindowCommandProc.h"
#include "AtlasObject/AtlasObject.h"
#include "AtlasObject/AtlasObjectText.h"
#include "General/AtlasClipboard.h"
const int BlanksAtEnd = 2;
EditableListCtrl::EditableListCtrl(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
: wxListCtrl(parent, id, pos, size, style | wxLC_VIRTUAL, validator, name)
{
m_ListItemAttr[0].SetBackgroundColour(wxColor(0xff, 0xff, 0xff));
m_ListItemAttr[1].SetBackgroundColour(wxColor(0xee, 0xee, 0xee));
wxASSERT_MSG(style & wxLC_REPORT, _T("EditableListCtrl must be LC_REPORT"));
UpdateDisplay();
}
EditableListCtrl::~EditableListCtrl()
{
size_t count = m_ColumnTypes.size();
for (size_t n = 0; n < count; ++n)
delete (FieldEditCtrl*)m_ColumnTypes[n].ctrl;
m_ColumnTypes.clear();
}
void EditableListCtrl::AddColumnType(const wxString& title, int width, const char* objectkey, FieldEditCtrl* ctrl)
{
int n = GetColumnCount();
wxASSERT(m_ColumnTypes.size() == (size_t) n); // check internal consistency
InsertColumn(n, title, wxLIST_FORMAT_LEFT, width);
m_ColumnTypes.push_back(ColumnData(objectkey, ctrl));
}
void EditableListCtrl::OnMouseEvent(wxMouseEvent& event)
{
// Double-clicking/right-clicking on a cell lets the user edit it.
// The editing method depends on what column the cell is in.
if (event.LeftDClick() || event.RightDown())
{
// Work out what cell was clicked on:
wxPoint pt = event.GetPosition();
int col = GetColumnAtPosition(pt);
wxCHECK2(col >= 0 && col < (int)m_ColumnTypes.size(), return);
int flags;
long row = HitTest(pt, flags);
if (row != wxNOT_FOUND && (flags & wxLIST_HITTEST_ONITEM))
{
// Calculate the exact positioning of the clicked cell
wxRect rect;
GetCellRect(row, col, rect);
// Execute the appropriate FieldEditCtrl
FieldEditCtrl* editor = (FieldEditCtrl*)m_ColumnTypes[col].ctrl;
editor->StartEdit(this, rect, row, col);
}
}
}
void EditableListCtrl::OnKeyDown(wxKeyEvent& event)
{
// TODO: Don't use magic key-code numbers
// Check for Copy
if ((event.GetKeyCode() == 3) || // ctrl+c
(event.GetKeyCode() == WXK_INSERT && event.ControlDown())) // ctrl+insert
{
AtObj row;
long selection = GetSelection();
if (selection >= 0 && selection < (long)m_ListData.size())
row = m_ListData[selection];
AtlasClipboard::SetClipboard(row);
}
// Check for Paste
else
if ((event.GetKeyCode() == 22) || // ctrl+v
(event.GetKeyCode() == WXK_INSERT && event.ShiftDown())) // shift+insert
{
AtObj row;
if (AtlasClipboard::GetClipboard(row))
{
long selection = GetSelection();
AtlasWindowCommandProc* commandProc = AtlasWindowCommandProc::GetFromParentFrame(this);
commandProc->Submit(new PasteCommand(this, selection, row));
}
}
else
event.Skip();
}
int EditableListCtrl::GetColumnAtPosition(wxPoint& pos)
{
// Find the column which pos is in.
// Get the origin of the table, in case it's scrolled horizontally
wxRect rect;
GetItemRect(0, rect);
int x = rect.GetX();
// Loop through each column
int numCols = GetColumnCount();
for (int i = 0; i < numCols; ++i)
{
// Calculate the position of this column's right-hand edge
x += GetColumnWidth(i);
// Test if pos was within this column (and assume it wasn't in an earlier one)
if (pos.x <= x)
return i;
}
// Point is outside the table's right edge
return -1;
}
void EditableListCtrl::GetCellRect(long row, int col, wxRect& rect)
{
wxASSERT(col >= 0 && col < GetColumnCount());
wxASSERT(row >= 0 && row < GetItemCount());
GetItemRect(row, rect);
for (int i = 0; i < col; ++i)
rect.x += GetColumnWidth(i);
rect.width = GetColumnWidth(col);
}
bool EditableListCtrl::IsRowBlank(int n)
{
return ! m_ListData[n].hasContent();
}
void EditableListCtrl::TrimBlankEnds()
{
while (m_ListData.size() && !m_ListData.back().defined())
m_ListData.pop_back();
}
void EditableListCtrl::SetSelection(long item)
{
SetItemState(item, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
}
long EditableListCtrl::GetSelection()
{
for (long item = 0; item < GetItemCount(); ++item)
if (GetItemState(item, wxLIST_STATE_SELECTED))
return item;
return 0;
}
void EditableListCtrl::MakeSizeAtLeast(int n)
{
if ((int)m_ListData.size() < n)
m_ListData.resize(n);
}
void EditableListCtrl::AddRow(AtObj& obj)
{
m_ListData.push_back(obj);
}
void EditableListCtrl::AddRow(AtIter& iter)
{
AtObj obj = *iter;
AddRow(obj);
}
void EditableListCtrl::UpdateDisplay()
{
TrimBlankEnds();
SetItemCount((int)m_ListData.size() + BlanksAtEnd);
Refresh();
}
void EditableListCtrl::CloneListData(std::vector& out)
{
out = m_ListData;
}
void EditableListCtrl::SetListData(std::vector& in)
{
m_ListData = in;
}
void EditableListCtrl::DeleteData()
{
m_ListData.clear();
}
wxString EditableListCtrl::GetCellString(long item, long column) const
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), _T(""));
if (item >= (int)m_ListData.size())
return _T("");
AtObj cell = *m_ListData[item][m_ColumnTypes[column].key];
return AtlasObject::ConvertToString(cell).c_str();
}
AtObj EditableListCtrl::GetCellObject(long item, long column) const
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), AtObj());
if (item >= (int)m_ListData.size())
return AtObj();
return *m_ListData[item][m_ColumnTypes[column].key];
}
void EditableListCtrl::SetCellString(long item, long column, wxString& str)
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), );
MakeSizeAtLeast(item+1);
m_ListData[item].set(m_ColumnTypes[column].key, str);
}
void EditableListCtrl::SetCellObject(long item, long column, AtObj& obj)
{
wxCHECK(item >= 0 && column >= 0 && column < (int)m_ColumnTypes.size(), );
MakeSizeAtLeast(item+1);
m_ListData[item].set(m_ColumnTypes[column].key, obj);
}
wxString EditableListCtrl::OnGetItemText(long item, long column) const
{
return GetCellString(item, column);
}
wxListItemAttr* EditableListCtrl::OnGetItemAttr(long item) const
{
// Make the last two rows white
if (item >= (long)m_ListData.size())
return const_cast(&m_ListItemAttr[0]);
// Make the background colors of rows alternate
else
return const_cast(&m_ListItemAttr[item%2]);
}
void EditableListCtrl::ImportData(AtObj& in)
{
return DoImport(in);
}
AtObj EditableListCtrl::ExportData()
{
return DoExport();
}
void EditableListCtrl::ThawData(AtObj& in)
{
m_ListData.clear();
for (AtIter it = in["item"]; it.defined(); ++it)
m_ListData.push_back(*it);
UpdateDisplay();
}
AtObj EditableListCtrl::FreezeData()
{
AtObj out;
for (std::vector::iterator it = m_ListData.begin(); it != m_ListData.end(); ++it)
out.add("item", *it);
return out;
}
BEGIN_EVENT_TABLE(EditableListCtrl, wxListCtrl)
EVT_LEFT_DCLICK(EditableListCtrl::OnMouseEvent)
EVT_RIGHT_DOWN(EditableListCtrl::OnMouseEvent)
EVT_CHAR(EditableListCtrl::OnKeyDown)
END_EVENT_TABLE()