mirror of https://github.com/BOINC/boinc.git
parent
0e3f983e1c
commit
b054f46b76
|
@ -1,925 +0,0 @@
|
|||
// This file is part of BOINC.
|
||||
// http://boinc.berkeley.edu
|
||||
// Copyright (C) 2008 University of California
|
||||
//
|
||||
// BOINC 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 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// BOINC 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.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#if defined(__GNUG__) && !defined(__APPLE__)
|
||||
#pragma implementation "BOINCGridCtrl.h"
|
||||
#endif
|
||||
|
||||
#include "stdwx.h"
|
||||
#include "BOINCGridCtrl.h"
|
||||
#include "BOINCBaseView.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 ()
|
||||
|
||||
/* Constructor, don't call any grid methods here, because they could raise events,
|
||||
which couldn't be handled correctly while the grid isn't constructed completly.
|
||||
Instead call Setup() and place all further initialization there
|
||||
*/
|
||||
CBOINCGridCtrl::CBOINCGridCtrl(wxWindow* parent, wxWindowID iGridWindowID) : wxGrid(parent, iGridWindowID) {
|
||||
//init members
|
||||
sortColumn=-1;
|
||||
sortAscending=true;
|
||||
sortNeededByLabelClick=false;
|
||||
m_pkColumnIndex1=-1;
|
||||
m_pkColumnIndex2=-1;
|
||||
m_cursorcol=-1;
|
||||
m_cursorrow=-1;
|
||||
m_arrSelectedKeys1.Clear();
|
||||
m_arrSelectedKeys2.Clear();
|
||||
|
||||
//load sorting bitmaps
|
||||
ascBitmap = wxBitmap(sortascending_xpm);
|
||||
descBitmap = wxBitmap(sortdescending_xpm);
|
||||
}
|
||||
|
||||
/* make settings for the grid here instead in the constructor */
|
||||
void CBOINCGridCtrl::Setup() {
|
||||
//make grid cursor invisible
|
||||
SetCellHighlightPenWidth(0);
|
||||
SetCellHighlightROPenWidth(0);
|
||||
//change default selection colours
|
||||
SetSelectionBackground(*wxLIGHT_GREY);
|
||||
SetSelectionForeground(*wxBLACK);
|
||||
//
|
||||
SetRowLabelSize(1);//hide row labels
|
||||
SetColLabelSize(20); //make header row 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);//make the whole grid read-only
|
||||
SetDefaultCellBackgroundColour(*wxWHITE);
|
||||
#ifdef __WXMAC__
|
||||
SetLabelFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
SetDefaultCellFont(wxFont(12, wxSWISS, wxNORMAL, wxNORMAL, FALSE));
|
||||
#else
|
||||
SetLabelFont(*wxNORMAL_FONT);
|
||||
#endif
|
||||
this->SetScrollLineX(5);
|
||||
this->SetScrollLineY(5);
|
||||
}
|
||||
|
||||
CBOINCGridCtrl::~CBOINCGridCtrl() {
|
||||
}
|
||||
|
||||
/* use this method instead of wxGrid::GetSelectedRows()
|
||||
because a bug in wxGrid::GetSelectedRows() doesn't return anything although some rows are 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;
|
||||
}
|
||||
|
||||
/* sets the column with unique values */
|
||||
void CBOINCGridCtrl::SetPrimaryKeyColumns(int col1,int col2) {
|
||||
m_pkColumnIndex1 = col1;
|
||||
m_pkColumnIndex2 = col2;
|
||||
}
|
||||
|
||||
/* If the user clicked inside the grid, the former selected cells must be deselected.
|
||||
Because the code in OnListRender() of the GridViews raises SelectionEvents too,
|
||||
call to GetBatchCount() here is necessary to decide what to do
|
||||
*/
|
||||
void CBOINCGridCtrl::ClearSavedSelection() {
|
||||
if(this->GetBatchCount()<=0) {
|
||||
m_arrSelectedKeys1.Empty();
|
||||
m_arrSelectedKeys2.Empty();
|
||||
}
|
||||
}
|
||||
|
||||
/* save the key values of the currently selected rows for later restore */
|
||||
void CBOINCGridCtrl::SaveSelection() {
|
||||
if(m_pkColumnIndex1>=0) {
|
||||
wxArrayInt arrSelRows = GetSelectedRows2();
|
||||
m_arrSelectedKeys1.Empty();
|
||||
m_arrSelectedKeys2.Empty();
|
||||
for(unsigned int i=0; i< arrSelRows.GetCount();i++) {
|
||||
m_arrSelectedKeys1.Add(GetCellValue(arrSelRows[i],m_pkColumnIndex1));
|
||||
if (m_pkColumnIndex2 >= 0) {
|
||||
m_arrSelectedKeys2.Add(GetCellValue(arrSelRows[i],m_pkColumnIndex2));
|
||||
} else {
|
||||
m_arrSelectedKeys2.Add(wxEmptyString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* select all rows, that were formerly selected
|
||||
this raises selection events without user interaction */
|
||||
void CBOINCGridCtrl::RestoreSelection() {
|
||||
BeginBatch(); // To prevent flicker
|
||||
ClearSelection();
|
||||
for(unsigned int i=0;i < m_arrSelectedKeys1.size();i++) {
|
||||
int index = GetTable()->FindRowIndexByColValue(
|
||||
m_pkColumnIndex1,m_arrSelectedKeys1[i],m_pkColumnIndex2,m_arrSelectedKeys2[i]
|
||||
);
|
||||
if(index >=0) {
|
||||
SelectRow(index,true);
|
||||
}
|
||||
}
|
||||
EndBatch();
|
||||
}
|
||||
|
||||
void CBOINCGridCtrl::SaveGridCursorPosition() {
|
||||
m_cursorcol = GetGridCursorCol();
|
||||
m_cursorrow = GetGridCursorRow();
|
||||
if(m_cursorrow>=0 && m_cursorcol >=0) {
|
||||
m_szCursorKey1 = GetCellValue(m_cursorrow,m_pkColumnIndex1);
|
||||
if (m_pkColumnIndex2 >= 0) {
|
||||
m_szCursorKey2 = GetCellValue(m_cursorrow,m_pkColumnIndex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBOINCGridCtrl::RestoreGridCursorPosition() {
|
||||
int index = GetTable()->FindRowIndexByColValue(m_pkColumnIndex1,m_szCursorKey1,m_pkColumnIndex2,m_szCursorKey2);
|
||||
if(index >=0) {
|
||||
SetGridCursor(index,m_cursorcol);
|
||||
}
|
||||
}
|
||||
|
||||
int CBOINCGridCtrl::GetFirstSelectedRow() {
|
||||
int ret= -1;
|
||||
//Row mode ?
|
||||
wxArrayInt selRows = this->GetSelectedRows2();
|
||||
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);
|
||||
// Don't save width for hidden / invisible columns
|
||||
if (label.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
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 = 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 = GetColLabelValue(iIndex);
|
||||
// Don't restore width for hidden / invisible columns
|
||||
if (label.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
pConfig->SetPath(strBaseConfigLocation + label);
|
||||
|
||||
pConfig->Read(wxT("Width"), &iTempValue, -1);
|
||||
if (-1 != iTempValue) {
|
||||
SetColSize(iIndex, iTempValue);
|
||||
}
|
||||
}
|
||||
//read sorting
|
||||
pConfig->SetPath(strBaseConfigLocation);
|
||||
pConfig->Read(wxT("SortColumn"),&iTempValue,-1);
|
||||
if(-1 != iTempValue) {
|
||||
sortColumn = iTempValue;
|
||||
}
|
||||
pConfig->Read(wxT("SortAscending"),&iTempValue,-1);
|
||||
if(-1 != iTempValue) {
|
||||
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 == sortColumn) {
|
||||
int x = rect.GetRight() - ascBitmap.GetWidth() - 2;
|
||||
int y = rect.GetY();
|
||||
dc.DrawBitmap(this->sortAscending ? descBitmap : ascBitmap,x,y,true);
|
||||
}
|
||||
}
|
||||
|
||||
/* handles left mouse click on column header */
|
||||
void CBOINCGridCtrl::OnLabelLClick(wxGridEvent& ev) {
|
||||
if(ev.GetCol() != -1) {
|
||||
//same column as last time, then change only sort direction
|
||||
if(sortColumn == ev.GetCol()) {
|
||||
sortAscending = ! sortAscending;
|
||||
} else {
|
||||
int tmpOldColumn = sortColumn;
|
||||
|
||||
sortColumn = ev.GetCol();
|
||||
sortAscending = true;
|
||||
|
||||
// Force a repaint of the label
|
||||
if ( -1 != tmpOldColumn ) {
|
||||
SetColLabelValue(tmpOldColumn, GetColLabelValue(tmpOldColumn));
|
||||
}
|
||||
}
|
||||
|
||||
// Force a repaint of the label
|
||||
SetColLabelValue(ev.GetCol(), GetColLabelValue(ev.GetCol()));
|
||||
//
|
||||
sortNeededByLabelClick=true;
|
||||
// Update and sort data
|
||||
wxTimerEvent tEvent;
|
||||
wxDynamicCast(GetParent(),CBOINCBaseView)->FireOnListRender(tEvent);
|
||||
}
|
||||
// The base class calls ClearSelection(), so do NOT call ev.Skip();
|
||||
}
|
||||
|
||||
void CBOINCGridCtrl::SortData() {
|
||||
GetTable()->SortData(sortColumn,sortAscending);
|
||||
sortNeededByLabelClick=false;
|
||||
}
|
||||
|
||||
void CBOINCGridCtrl::SetColumnSortType(int col,int sortType/*=CST_STRING*/) {
|
||||
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 ######################## */
|
||||
|
||||
/* Constructor
|
||||
prio argument - the column index, that holds prio values
|
||||
*/
|
||||
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(wxT("Error"));
|
||||
if(grid.GetCellValue(row,column).Trim(false).IsSameAs(szError)) {
|
||||
attr.SetTextColour(*wxRED);
|
||||
}
|
||||
else {
|
||||
attr.SetTextColour(*wxBLACK);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* paints the progress bar */
|
||||
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
|
||||
//
|
||||
double dv = 0.0;
|
||||
wxString strValue = grid.GetCellValue(row,col);
|
||||
if(m_bDoPercentAppending) {
|
||||
strValue = strValue + wxT(" %");
|
||||
}
|
||||
|
||||
// Project view uses the format: %0.0f (%0.2f%%)
|
||||
// Everyone else uses: %.3f%%
|
||||
if (strValue.Find(wxT("(")) != wxNOT_FOUND) {
|
||||
strValue.SubString(strValue.Find(wxT("(")) + 1, strValue.Find(wxT(")")) - 1).ToDouble( &dv );
|
||||
} else {
|
||||
strValue.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(wxT("LIGHT BLUE"));
|
||||
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;
|
||||
//decide, which sort function to call
|
||||
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],-1,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 with value value1 in column col1 and value2 in col2
|
||||
ignores col2 and value2 if col2 < 0
|
||||
!! only use this (outside sorting method) with a pair of columns, that have unique value pairs !!
|
||||
*/
|
||||
int CBOINCGridTable::FindRowIndexByColValue(int col1,wxString& value1,int col2,wxString& value2) {
|
||||
|
||||
for(int i=0; i < this->GetNumberRows(); i++) {
|
||||
if(! this->GetValue(i,col1).IsSameAs(value1)) continue;
|
||||
if (col2 < 0) return i;
|
||||
if(this->GetValue(i,col2).IsSameAs(value2)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* for convinience purposes only */
|
||||
CBOINCGridTable* CBOINCGridCtrl::GetTable() {
|
||||
return wxDynamicCast(wxGrid::GetTable(), CBOINCGridTable);
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
// This file is part of BOINC.
|
||||
// http://boinc.berkeley.edu
|
||||
// Copyright (C) 2008 University of California
|
||||
//
|
||||
// BOINC 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 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// BOINC 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.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef _BOINCGRIDCTRL_H_
|
||||
#define _BOINCGRIDCTRL_H_
|
||||
|
||||
#if defined(__GNUG__) && !defined(__APPLE__)
|
||||
#pragma interface "BOINCGridCtrl.cpp"
|
||||
#endif
|
||||
|
||||
#define CST_STRING 0
|
||||
#define CST_TIME 1
|
||||
#define CST_LONG 2
|
||||
#define CST_FLOAT 3
|
||||
#define CST_DATETIME 4
|
||||
|
||||
/* generic boinc grid cell renderer */
|
||||
class CBOINCGridCellRenderer : public wxGridCellStringRenderer {
|
||||
public:
|
||||
CBOINCGridCellRenderer();
|
||||
void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool isSelected);
|
||||
protected:
|
||||
void DoNormalTextDrawing(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rectCell, int row, int col, bool isSelected);
|
||||
void DrawBackground(wxGrid& grid,wxDC& dc,const wxRect& rect,int row,bool isSelected);
|
||||
};
|
||||
|
||||
/* progress renderer */
|
||||
class CBOINCGridCellProgressRenderer : public CBOINCGridCellRenderer {
|
||||
int column;
|
||||
bool m_bDoPercentAppending;
|
||||
public:
|
||||
CBOINCGridCellProgressRenderer(int col,bool percentAppending=true);
|
||||
void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool isSelected);
|
||||
void SetPercentAppending(bool enable=true);
|
||||
protected:
|
||||
void DoProgressDrawing(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rectCell, int row, int col, bool isSelected);
|
||||
};
|
||||
|
||||
|
||||
/* message renderer */
|
||||
class CBOINCGridCellMessageRenderer : public CBOINCGridCellRenderer {
|
||||
int column;
|
||||
public:
|
||||
CBOINCGridCellMessageRenderer(int priocol);
|
||||
void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool isSelected);
|
||||
};
|
||||
|
||||
/* grid table */
|
||||
class CBOINCGridTable : public wxGridStringTable {
|
||||
public:
|
||||
CBOINCGridTable(int rows,int cols);
|
||||
virtual ~CBOINCGridTable();
|
||||
void SortData(int col,bool ascending);
|
||||
int FindRowIndexByColValue(int col1,wxString& value1,int col2,wxString& value2);
|
||||
void SetColumnSortType(int col,int sortType=CST_STRING);
|
||||
private:
|
||||
wxArrayInt arrColumnSortTypes;
|
||||
};
|
||||
|
||||
/* grid ctrl */
|
||||
class CBOINCGridCtrl : public wxGrid
|
||||
{
|
||||
public:
|
||||
CBOINCGridCtrl(wxWindow* parent, wxWindowID iGridWindowID);
|
||||
~CBOINCGridCtrl();
|
||||
int GetFirstSelectedRow();
|
||||
bool OnSaveState(wxConfigBase* pConfig);
|
||||
bool OnRestoreState(wxConfigBase* pConfig);
|
||||
void SetColAlignment(int col,int hAlign,int vAlign);
|
||||
void DrawTextRectangle( wxDC& dc, const wxArrayString& lines, const wxRect&,
|
||||
int horizontalAlignment = wxALIGN_LEFT,
|
||||
int verticalAlignment = wxALIGN_TOP,
|
||||
int textOrientation = wxHORIZONTAL );
|
||||
void DrawTextRectangle( wxDC& dc, const wxString&, const wxRect&,
|
||||
int horizontalAlignment = wxALIGN_LEFT,
|
||||
int verticalAlignment = wxALIGN_TOP,
|
||||
int textOrientation = wxHORIZONTAL );
|
||||
virtual void DrawColLabel( wxDC& dc, int col );
|
||||
void OnLabelLClick(wxGridEvent& ev);
|
||||
void SortData();
|
||||
void SetColumnSortType(int col,int sortType=CST_STRING);
|
||||
wxArrayInt GetSelectedRows2();
|
||||
CBOINCGridTable* GetTable();
|
||||
//methods to handle selection and grid cursor positions correct with sorting
|
||||
void SetPrimaryKeyColumns(int col1,int col2);
|
||||
void SaveSelection();
|
||||
void RestoreSelection();
|
||||
void SaveGridCursorPosition();
|
||||
void RestoreGridCursorPosition();
|
||||
void ClearSavedSelection();
|
||||
void Setup();
|
||||
int sortColumn;
|
||||
bool sortAscending;
|
||||
bool sortNeededByLabelClick;
|
||||
protected:
|
||||
DECLARE_EVENT_TABLE()
|
||||
private:
|
||||
wxString FormatTextWithEllipses(wxDC& dc,const wxString &text,int width);
|
||||
|
||||
|
||||
int ccollast,crowlast;
|
||||
wxBitmap ascBitmap;
|
||||
wxBitmap descBitmap;
|
||||
int m_pkColumnIndex1; //col index to use as a primary key
|
||||
int m_pkColumnIndex2; //col index to use as secondary key
|
||||
wxArrayString m_arrSelectedKeys1;//array for remembering the current selected rows by primary key column value
|
||||
wxArrayString m_arrSelectedKeys2;//array for remembering the current selected rows by secondary key column value
|
||||
int m_cursorcol; //saved grid cursor column index
|
||||
int m_cursorrow; //saved grid cursor row index
|
||||
wxString m_szCursorKey1;//primary key value for grid cursor cell
|
||||
wxString m_szCursorKey2;//secondary key value for grid cursor cell
|
||||
};
|
||||
|
||||
#endif //_BOINCGRIDCTRL_H_
|
Loading…
Reference in New Issue