boinc/clientgui/BOINCGridCtrl.cpp

800 lines
24 KiB
C++

// Berkeley Open Infrastructure for Network Computing
// http://boinc.berkeley.edu
// Copyright (C) 2005 University of California
//
// This is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
//
// This software 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 Lesser General Public License for more details.
//
// To view the GNU Lesser General Public License visit
// http://www.gnu.org/copyleft/lesser.html
// or write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "BOINCGridCtrl.h"
#endif
#include "stdwx.h"
#include "BOINCGridCtrl.h"
#include "res/sortascending.xpm"
#include "res/sortdescending.xpm"
/* static compare functions and helper variable */
static bool reverseCompareOrder=false;
/* compare Strings that contains floats */
static int CompareFloatString(const wxString& first,const wxString& second) {
double dFirst;
double dSecond;
first.ToDouble(&dFirst);
second.ToDouble(&dSecond);
if(dFirst < dSecond) {
return reverseCompareOrder ? 1 : -1 ;
}
if(dSecond < dFirst) {
return reverseCompareOrder ? -1 : 1 ;
}
return 0;
}
/* compare Strings case insensitive */
static int CompareStringStringNoCase(const wxString& first,const wxString& second) {
int ret = first.CmpNoCase(second);
return reverseCompareOrder ? ret * (-1) : ret;
}
/* compare Strings that contains int/long numbers */
static int CompareLongString(const wxString& first,const wxString& second) {
long lFirst;
long lSecond;
first.ToLong(&lFirst);
second.ToLong(&lSecond);
if(lFirst < lSecond) {
return reverseCompareOrder ? 1 : -1 ;
}
if(lSecond < lFirst) {
return reverseCompareOrder ? -1 : 1 ;
}
return 0;
}
/* compare strings that contains time periods in format 00:00:00 */
static int CompareTimeString(const wxString& first,const wxString& second) {
long dtFirst,dtSecond;
wxString hours,minutes,seconds;
long lHours,lMinutes,lSeconds;
//converting the first string to long value
hours = first.BeforeFirst(':');
seconds = first.AfterLast(':');
minutes = first.AfterFirst(':').BeforeFirst(':');
hours.ToLong(&lHours);
minutes.ToLong(&lMinutes);
seconds.ToLong(&lSeconds);
dtFirst = lSeconds + lMinutes * 60 + lHours * 3600;
//converting the second string
hours = second.BeforeFirst(':');
seconds = second.AfterLast(':');
minutes = second.AfterFirst(':').BeforeFirst(':');
hours.ToLong(&lHours);
minutes.ToLong(&lMinutes);
seconds.ToLong(&lSeconds);
dtSecond = lSeconds + lMinutes * 60 + lHours * 3600;
if(dtFirst < dtSecond) {
return reverseCompareOrder ? 1 : -1 ;
}
if(dtSecond < dtFirst) {
return reverseCompareOrder ? -1 : 1 ;
}
return 0;
}
/* compare Strings that contains dateTime*/
static int CompareDateString(const wxString& first,const wxString& second) {
wxDateTime dtFirst,dtSecond;
//dtFirst.ParseDateTime(first);
//dtSecond.ParseDateTime(second);
dtFirst.ParseFormat(first,wxT(" %x %X"));
dtSecond.ParseFormat(second,wxT(" %x %X"));
if(dtFirst < dtSecond) {
return reverseCompareOrder ? 1 : -1 ;
}
if(dtSecond < dtFirst) {
return reverseCompareOrder ? -1 : 1 ;
}
return 0;
}
/* grid ctrl implementation */
BEGIN_EVENT_TABLE (CBOINCGridCtrl, wxGrid)
EVT_GRID_LABEL_LEFT_CLICK(CBOINCGridCtrl::OnLabelLClick)
END_EVENT_TABLE ()
CBOINCGridCtrl::CBOINCGridCtrl(wxWindow* parent, wxWindowID iGridWindowID) : wxGrid(parent, iGridWindowID) {
sortColumn=-1;
sortAscending=true;
//load sorting bitmaps
ascBitmap = wxBitmap(sortascending_xpm);
descBitmap = wxBitmap(sortdescending_xpm);
//make grid cursor invisible
SetCellHighlightPenWidth(0);
SetCellHighlightROPenWidth(0);
//change default selection colours
SetSelectionBackground(*wxLIGHT_GREY);
SetSelectionForeground(*wxBLACK);
//
SetRowLabelSize(1);//hide row labels
SetColLabelSize(20); //smaller as default
SetColLabelAlignment(wxALIGN_LEFT,wxALIGN_CENTER);
EnableGridLines(false);
EnableDragRowSize(false);//prevent the user from changing the row height with the mouse
EnableDragCell(false);
EnableEditing(false);
DisableCellEditControl();
SetDefaultCellBackgroundColour(*wxWHITE);
#ifdef __WXMAC__
SetLabelFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
SetDefaultCellFont(wxFont(12, wxSWISS, wxNORMAL, wxNORMAL, FALSE));
#else
SetLabelFont(*wxNORMAL_FONT);
#endif
}
CBOINCGridCtrl::~CBOINCGridCtrl() {
}
/* use this method instead of wxGrid::GetSelectedRows() because of a bug in wxWidgets not returning anything selected */
wxArrayInt CBOINCGridCtrl::GetSelectedRows2() {
wxArrayInt ret;
for(int i= 0; i< this->GetRows();i++) {
for(int j=0; j< this->GetCols();j++) {
if(this->IsInSelection(i,j)) {
ret.Add(i);
//break the inner loop here to prevent adding the same row twice
break;
}
}
}
return ret;
}
int CBOINCGridCtrl::GetFirstSelectedRow() {
int ret= -1;
//Row mode ?
wxArrayInt selRows = this->GetSelectedRows();
if(selRows.size() >0) {
return selRows[0];
}
//block mode ?
wxGridCellCoordsArray selBlocks = this->GetSelectionBlockTopLeft();
if(selBlocks.size()>0) {
return selBlocks[0].GetRow();
}
//cell mode
wxGridCellCoordsArray selCells = this->GetSelectionBlockTopLeft();
if(selCells.size()>0) {
return selCells[0].GetRow();
}
return ret;
}
/* saves column widths and sorting attributes */
bool CBOINCGridCtrl::OnSaveState(wxConfigBase* pConfig) {
wxString strBaseConfigLocation = wxEmptyString;
wxInt32 iIndex = 0;
wxInt32 iColumnCount = this->GetCols();
wxASSERT(pConfig);
// Retrieve the base location to store configuration information
// Should be in the following form: "/Projects/"
strBaseConfigLocation = pConfig->GetPath() + wxT("/");
// Cycle through the columns recording anything interesting
for (iIndex = 0; iIndex < iColumnCount; iIndex++) {
wxString label = this->GetColLabelValue(iIndex);
pConfig->SetPath(strBaseConfigLocation + label);
pConfig->Write(wxT("Width"), this->GetColumnWidth(iIndex));
}
//save sorting column
pConfig->SetPath(strBaseConfigLocation);
pConfig->Write(wxT("SortColumn"),this->sortColumn);
pConfig->Write(wxT("SortAscending"),this->sortAscending);
return true;
}
/* restores column widths and sorting attributes */
bool CBOINCGridCtrl::OnRestoreState(wxConfigBase* pConfig) {
wxString strBaseConfigLocation = wxEmptyString;
wxInt32 iIndex = 0;
wxInt32 iTempValue = 0;
wxInt32 iColumnCount = this->GetCols();
wxASSERT(pConfig);
// Retrieve the base location to store configuration information
// Should be in the following form: "/Projects/"
strBaseConfigLocation = pConfig->GetPath() + wxT("/");
// Cycle through the columns recording anything interesting
for (iIndex = 0; iIndex < iColumnCount; iIndex++) {
wxString label = this->GetColLabelValue(iIndex);
pConfig->SetPath(strBaseConfigLocation + label);
pConfig->Read(wxT("Width"), &iTempValue, -1);
if (-1 != iTempValue) {
this->SetColumnWidth(iIndex,iTempValue);
}
}
//read sorting
pConfig->SetPath(strBaseConfigLocation);
pConfig->Read(wxT("SortColumn"),&iTempValue,-1);
if(-1 != iTempValue) {
this->sortColumn = iTempValue;
}
pConfig->Read(wxT("SortAscending"),&iTempValue,-1);
if(-1 != iTempValue) {
this->sortAscending = iTempValue != 0 ? true : false;
}
return true;
}
/* for convienience purpose only */
void CBOINCGridCtrl::SetColAlignment(int col,int hAlign,int vAlign) {
wxGridCellAttr *attr = m_table->GetAttr(-1, col, wxGridCellAttr::Col );
if(!attr) {
attr = new wxGridCellAttr;
}
attr->SetAlignment(hAlign,vAlign);
SetColAttr(col, attr);
}
/* copied source code from wxGrid to make overloading possible */
void CBOINCGridCtrl::DrawTextRectangle( wxDC& dc,
const wxString& value,
const wxRect& rect,
int horizAlign,
int vertAlign,
int textOrientation )
{
wxArrayString lines;
StringToLines( value, lines );
//Forward to new API.
DrawTextRectangle( dc,
lines,
rect,
horizAlign,
vertAlign,
textOrientation );
}
/* draws text with ellipses instead cutting it */
void CBOINCGridCtrl::DrawTextRectangle( wxDC& dc,
const wxArrayString& lines,
const wxRect& rect,
int horizAlign,
int vertAlign,
int textOrientation )
{
long textWidth, textHeight;
long lineWidth, lineHeight;
unsigned int nLines;
dc.SetClippingRegion( rect );
nLines = (unsigned int)lines.GetCount();
if( nLines > 0 )
{
unsigned int l;
float x = 0.0, y = 0.0;
if( textOrientation == wxHORIZONTAL )
GetTextBoxSize(dc, lines, &textWidth, &textHeight);
else
GetTextBoxSize( dc, lines, &textHeight, &textWidth );
switch( vertAlign )
{
case wxALIGN_BOTTOM:
if( textOrientation == wxHORIZONTAL )
y = rect.y + (rect.height - textHeight - 1);
else
x = rect.x + rect.width - textWidth;
break;
case wxALIGN_CENTRE:
if( textOrientation == wxHORIZONTAL )
y = rect.y + ((rect.height - textHeight)/2);
else
x = rect.x + ((rect.width - textWidth)/2);
break;
case wxALIGN_TOP:
default:
if( textOrientation == wxHORIZONTAL )
y = rect.y + 1;
else
x = rect.x + 1;
break;
}
// Align each line of a multi-line label
for( l = 0; l < nLines; l++ )
{
dc.GetTextExtent(lines[l], &lineWidth, &lineHeight);
switch( horizAlign )
{
case wxALIGN_RIGHT:
if( textOrientation == wxHORIZONTAL )
x = rect.x + (rect.width - lineWidth - 1);
else
y = rect.y + lineWidth + 1;
break;
case wxALIGN_CENTRE:
if( textOrientation == wxHORIZONTAL )
x = rect.x + ((rect.width - lineWidth)/2);
else
y = rect.y + rect.height - ((rect.height - lineWidth)/2);
break;
case wxALIGN_LEFT:
default:
if( textOrientation == wxHORIZONTAL )
x = rect.x + 1;
else
y = rect.y + rect.height - 1;
break;
}
if( textOrientation == wxHORIZONTAL )
{
//changes applies here
wxString formattedText = FormatTextWithEllipses(dc,lines[l],rect.width);
dc.DrawText( formattedText, (int)x, (int)y );
y += lineHeight;
}
else
{
dc.DrawRotatedText( lines[l], (int)x, (int)y, 90.0 );
x += lineHeight;
}
}
}
dc.DestroyClippingRegion();
}
wxString CBOINCGridCtrl::FormatTextWithEllipses(wxDC& dc,const wxString &text,int width)
{
wxString retText;
wxString drawntext, ellipsis;
wxCoord w, h, base_w;
wxListItem item;
// determine if the string can fit inside the current width
dc.GetTextExtent(text, &w, &h);
if (w <= width)
{
retText = text;
}
else // otherwise, truncate and add an ellipsis if possible
{
// determine the base width
ellipsis = wxString(wxT("..."));
dc.GetTextExtent(ellipsis, &base_w, &h);
// continue until we have enough space or only one character left
wxCoord w_c, h_c;
size_t len = text.Length();
drawntext = text.Left(len);
while (len > 1)
{
dc.GetTextExtent(drawntext.Last(), &w_c, &h_c);
drawntext.RemoveLast();
len--;
w -= w_c;
if (w + base_w <= width)
break;
}
// if still not enough space, remove ellipsis characters
while (ellipsis.Length() > 0 && w + base_w > width)
{
ellipsis = ellipsis.Left(ellipsis.Length() - 1);
dc.GetTextExtent(ellipsis, &base_w, &h);
}
// now draw the text
retText = drawntext + ellipsis;
}
return retText;
}
/* not virtual in wxGrid, so code copied and modified her for painting sorting icons */
void CBOINCGridCtrl::DrawColLabel( wxDC& dc, int col )
{
if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 )
return;
int colLeft = GetColLeft(col);
wxRect rect;
int colRight = GetColRight(col) - 1;
dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW),1, wxSOLID) );
dc.DrawLine( colRight, 0,
colRight, m_colLabelHeight-1 );
dc.DrawLine( colLeft, 0, colRight, 0 );
dc.DrawLine( colLeft, m_colLabelHeight-1,
colRight+1, m_colLabelHeight-1 );
dc.SetPen( *wxWHITE_PEN );
dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight-1 );
dc.DrawLine( colLeft, 1, colRight, 1 );
dc.SetBackgroundMode( wxTRANSPARENT );
dc.SetTextForeground( GetLabelTextColour() );
dc.SetFont( GetLabelFont() );
int hAlign, vAlign, orient;
GetColLabelAlignment( &hAlign, &vAlign );
orient = GetColLabelTextOrientation();
rect.SetX( colLeft + 2 );
rect.SetY( 2 );
rect.SetWidth( GetColWidth(col) - 4 );
rect.SetHeight( m_colLabelHeight - 4 );
DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign, orient );
//paint sorting indicators, if needed
if(col == this->sortColumn) {
int x = rect.GetRight() - ascBitmap.GetWidth() - 2;
int y = rect.GetY();
dc.DrawBitmap(this->sortAscending ? ascBitmap : descBitmap,x,y,true);
}
}
/* handles left mouse click on column header */
void CBOINCGridCtrl::OnLabelLClick(wxGridEvent& ev) {
if(ev.GetCol()!= -1) {
//clicked on a column
//same column as last time, then change sort only direction
if(this->sortColumn == ev.GetCol()) {
this->sortAscending = ! this->sortAscending;
}
else {
this->sortColumn = ev.GetCol();
}
}
//stop further event processing to prevent loosing the current selection and grid cursor position
//ev.Skip();
}
void CBOINCGridCtrl::SortData() {
this->GetTable()->SortData(this->sortColumn,this->sortAscending);
}
void CBOINCGridCtrl::SetColumnSortType(int col,int sortType/*=CST_STRING*/) {
this->GetTable()->SetColumnSortType(col,sortType);
}
/* generic grid cell renderer */
CBOINCGridCellRenderer::CBOINCGridCellRenderer() : wxGridCellStringRenderer() {
}
void CBOINCGridCellRenderer::Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool isSelected) {
DoNormalTextDrawing(grid,attr,dc,rect,row,col,isSelected);
}
/* draws alternating background colours */
void CBOINCGridCellRenderer::DrawBackground(wxGrid& grid,
wxDC& dc,
const wxRect& rect,
int row,
bool isSelected)
{
dc.SetBackgroundMode( wxSOLID );
// grey out fields if the grid is disabled
if( grid.IsEnabled() )
{
if ( isSelected )
{
dc.SetBrush( wxBrush(grid.GetSelectionBackground(), wxSOLID) );
}
else
{
//alternate background colours
if(row % 2 == 0) {
dc.SetBrush(wxBrush(wxColour(240,240,240)));
}
else {
dc.SetBrush(*wxWHITE_BRUSH);
}
}
}
else
{
dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE), wxSOLID));
}
dc.SetPen( *wxTRANSPARENT_PEN );
dc.DrawRectangle(rect);
}
void CBOINCGridCellRenderer::DoNormalTextDrawing(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rectCell, int row, int col, bool isSelected) {
wxRect rect = rectCell;
rect.Inflate(-1);
// erase only this cells background, overflow cells should have been erased
this->DrawBackground(grid, dc, rectCell, row, isSelected);
int hAlign, vAlign;
attr.GetAlignment(&hAlign, &vAlign);
int overflowCols = 0;
if (attr.GetOverflow())
{
int cols = grid.GetNumberCols();
int best_width = GetBestSize(grid,attr,dc,row,col).GetWidth();
int cell_rows, cell_cols;
attr.GetSize( &cell_rows, &cell_cols ); // shouldn't get here if <=0
if ((best_width > rectCell.width) && (col < cols) && grid.GetTable())
{
int i, c_cols, c_rows;
for (i = col+cell_cols; i < cols; i++)
{
bool is_empty = true;
for (int j=row; j<row+cell_rows; j++)
{
// check w/ anchor cell for multicell block
grid.GetCellSize(j, i, &c_rows, &c_cols);
if (c_rows > 0) c_rows = 0;
if (!grid.GetTable()->IsEmptyCell(j+c_rows, i))
{
is_empty = false;
break;
}
}
if (is_empty)
rect.width += grid.GetColSize(i);
else
{
i--;
break;
}
if (rect.width >= best_width) break;
}
overflowCols = i - col - cell_cols + 1;
if (overflowCols >= cols) overflowCols = cols - 1;
}
if (overflowCols > 0) // redraw overflow cells w/ proper hilight
{
hAlign = wxALIGN_LEFT; // if oveflowed then it's left aligned
wxRect clip = rect;
clip.x += rectCell.width;
// draw each overflow cell individually
int col_end = col+cell_cols+overflowCols;
if (col_end >= grid.GetNumberCols())
col_end = grid.GetNumberCols() - 1;
for (int i = col+cell_cols; i <= col_end; i++)
{
clip.width = grid.GetColSize(i) - 1;
dc.DestroyClippingRegion();
dc.SetClippingRegion(clip);
SetTextColoursAndFont(grid, attr, dc,
grid.IsInSelection(row,i));
grid.DrawTextRectangle(dc, grid.GetCellValue(row, col),
rect, hAlign, vAlign);
clip.x += grid.GetColSize(i) - 1;
}
rect = rectCell;
rect.Inflate(-1);
rect.width++;
dc.DestroyClippingRegion();
}
}
// now we only have to draw the text
SetTextColoursAndFont(grid, attr, dc, isSelected);
//get a real grid class pointer
CBOINCGridCtrl* bgrid = wxDynamicCast(&grid, CBOINCGridCtrl);
//use the overloaded method here
bgrid->DrawTextRectangle(dc, grid.GetCellValue(row, col),
rect, hAlign, vAlign);
}
/* ############# message cell renderer */
CBOINCGridCellMessageRenderer::CBOINCGridCellMessageRenderer(int priocol){
column = priocol;
}
void CBOINCGridCellMessageRenderer::Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool isSelected) {
wxString szError("Error",wxConvUTF8);
if(grid.GetCellValue(row,column).Trim(false).IsSameAs(szError)) {
attr.SetTextColour(*wxRED);
}
CBOINCGridCellRenderer::Draw(grid,attr,dc,rect,row,col,isSelected);
}
/* ############# progress cell renderer */
CBOINCGridCellProgressRenderer::CBOINCGridCellProgressRenderer(int col,bool percentAppending/*=true*/) : CBOINCGridCellRenderer()
{
column = col;
m_bDoPercentAppending = percentAppending;
}
void CBOINCGridCellProgressRenderer::Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool isSelected) {
if(col==column) {
DoProgressDrawing(grid,attr,dc,rect,row,col,isSelected);
}
else {
DoNormalTextDrawing(grid,attr,dc,rect,row,col,isSelected);
}
}
void CBOINCGridCellProgressRenderer::DoProgressDrawing(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rectCell, int row, int col, bool isSelected) {
wxRect rect = rectCell;
rect.Inflate(-1);
// erase only this cells background, overflow cells should have been erased
this->DrawBackground(grid, dc, rectCell, row, isSelected);
// set text attributes
int hAlign, vAlign;
attr.GetAlignment(&hAlign, &vAlign);
SetTextColoursAndFont(grid, attr, dc, isSelected);
//calculate the two parts of the progress rect
wxString value = grid.GetCellValue(row,col);
wxString strValue = value;
if(m_bDoPercentAppending) {
strValue = strValue + wxString(" %",wxConvUTF8);
}
double dv;
value.ToDouble ( &dv ); // NOTE: we should do error-checking/reporting here!!
wxRect p1(rect);
wxRect p2(rect);
int r = (int)((rect.GetRight()-rect.GetLeft())*dv / 100.0);
p1.SetRight(rect.GetLeft()+r);
p2.SetLeft(rect.GetLeft()+r+1);
p2.SetRight(rect.GetRight()-1);
//start drawing
dc.SetClippingRegion(rect);
wxBrush old = dc.GetBrush();
wxColour progressColour = wxTheColourDatabase->Find(wxString("LIGHT BLUE",wxConvUTF8));
wxBrush* progressBrush = wxTheBrushList->FindOrCreateBrush(progressColour);
wxPen* progressPen = wxThePenList->FindOrCreatePen(progressColour,1,wxSOLID);
//draw the outline rectangle
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen(*progressPen);
dc.DrawRectangle(rect);
// Draw the left part
dc.SetBrush(*progressBrush);
dc.DrawRectangle(p1);
//draw the right part
dc.SetBrush(old);
dc.DrawRectangle(p2);
//
dc.DestroyClippingRegion();
// draw the text
grid.DrawTextRectangle(dc, strValue, rect, hAlign, vAlign);
}
/* enables or disbale the appendign of the percent sign to the progress text */
void CBOINCGridCellProgressRenderer::SetPercentAppending(bool enable/*=true*/) {
m_bDoPercentAppending = enable;
}
/* ############ GridTable class ############ */
CBOINCGridTable::CBOINCGridTable(int rows, int cols) : wxGridStringTable(rows,cols) {
//init column sort types with CST_STRING
for(int i=0; i< cols; i++) {
arrColumnSortTypes.Add(CST_STRING);
}
}
CBOINCGridTable::~CBOINCGridTable() {
}
void CBOINCGridTable::SetColumnSortType(int col,int sortType/*=CST_STRING*/) {
if(col>=0 && col < ((int)arrColumnSortTypes.GetCount())) {
arrColumnSortTypes[col] = sortType;
}
}
void CBOINCGridTable::SortData(int col,bool ascending) {
//valid column index ?
if(col<0) {
return;
}
wxArrayString arColValues;
//get the values of the sorting column
for(int i=0; i < this->GetNumberRows(); i++) {
arColValues.Add(this->GetValue(i,col));
}
//sort the sorting column values
reverseCompareOrder = !ascending;
switch(arrColumnSortTypes[col]) {
case CST_FLOAT:
arColValues.Sort(CompareFloatString);
break;
case CST_LONG:
arColValues.Sort(CompareLongString);
break;
case CST_TIME:
arColValues.Sort(CompareTimeString);
break;
case CST_DATETIME:
arColValues.Sort(CompareDateString);
break;
default:
arColValues.Sort(CompareStringStringNoCase);
break;
}
//rebuild the underlaying 2-dimensional string array
wxGridStringArray newArray;
for(unsigned int i=0; i< arColValues.GetCount();i++) {
//find the original row index
int indexold = FindRowIndexByColValue(col,arColValues[i]);
wxArrayString rowArray;
for(int j=0; j < this->GetNumberCols(); j++) {
rowArray.Add(this->GetValue(indexold,j));
//delete the value in the original string array
//to prevent finding the wrong index in case of identical values
//f.e. the project name is the same for multiple work units or transfers
this->SetValue(indexold,j,wxEmptyString);
}
newArray.Add(rowArray);
}
//fill the table data array with the sorted values
for(unsigned int i=0; i< newArray.GetCount(); i++) {
for(unsigned int j=0; j < newArray[i].GetCount();j++) {
this->SetValue(i,j,newArray[i][j]);
}
}
}
/* finds the first row index for the cell value value in column col
!! only use this (outside sorting method) with a column, that has unique values !!
*/
int CBOINCGridTable::FindRowIndexByColValue(int col,wxString& value) {
for(int i=0; i < this->GetNumberRows(); i++) {
wxString curr = GetValue(i,col);
if(this->GetValue(i,col).IsSameAs(value)) {
return i;
}
}
return -1;
}
/* for convinience purposes only */
CBOINCGridTable* CBOINCGridCtrl::GetTable() {
return wxDynamicCast(wxGrid::GetTable(), CBOINCGridTable);
}