Hello,
I have programmed a class derived from wxGrid. It does the following:
- basic sorting (1 column only for now, only one algorithm)
- basic filtering (based on Regular Expression) - only text filtering is supported for now (no "<" or ">" operators for numeric types or dates supported yet).
- it can be used by simply replacing all occurences of wxGrid by wxEGrid, and adding the wxEGrid.h and wxEGrid.cpp in the project (no lib compilation necessary). I have successfully compiled griddemo.cpp (wxWidgets sample) with this method.
I will make it available on wxCode later - the procedure is a bit complex for now - and I have just 10 minutes before my wife screams on me for being too much on the computer...
My questions are:
- are there plans to support sorting and/or filtering in wxWidgets ?
- I have seen some advanced Table widgets (closed source) in .NET application with very performant filters (RegEx also, and fast...) and with a hierarchical organisation feature (possible to groups data based on values in a tree control style - this hierarchy can be multileveled). Are there some plans similar to that in wxWidgets ? (I suspect it is a huge piece of work).
- I have found a small bug in wxGridWindow class. The mouse capture is sometimes not released (try selecting some cells while scrolling to the top or the bottom). Is it a known bug ? It could be solved in a few lines I guess (processing a mouse button left up event). I will post a solution later on the bug tracking list.
I post the code in the next posts.
Sebastien
wxGrid class extended
wxGrid class extended
Last edited by seb_seb0 on Mon Mar 30, 2009 8:27 pm, edited 2 times in total.
Here is the code. There are 2 files (wxEGrid.h and wxEGrid.cpp)
wxEGrid.h:
wxEGrid.h:
Code: Select all
#ifndef EGRID_H
#define EGRID_H
#include <wx/grid.h>
#include <wx/dc.h>
#include <wx/dcclient.h>
#include <wx/settings.h>
#include <wx/gdicmn.h>
#include <wx/renderer.h>
#include <wx/combobox.h>
#include <wx/dynarray.h>
#include <wx/regex.h>
#include <wx/checklst.h>
#include <wx/textctrl.h>
#include <wx/dialog.h>
#include <wx/button.h>
#include <wx/msgdlg.h>
#define WX_EGRID_MERGE_SORT 1
#define WX_EGRID_QUICK_SORT 2
#define WX_EGRID_SHELL_SORT 3
#define WX_EGRID_INSERTION_SORT 4
#define WX_EGRID_ASCENDING 0
#define WX_EGRID_DESCENDING 1
#define WX_STRING_ARRAY_SIZE 100
#define WX_ROW_ARRAY_SIZE 100
#define WX_ROWCOL_ARRAY_SIZE 100
#define WX_VALUENOTFOUND -1
/**
the wxGridTableBase must implement the following methods:
- GetValue()
- InsertCols()
- GetNumberCols()
for working
**/
class wxEGrid; //derived from wxGrid. Implement built-in filters & sorting
class wxPopupFilter; //for the combobox filter
class wxPopupFilter : public wxDialog
{
public:
wxPopupFilter(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size); //constructor
~wxPopupFilter(void); //destructor
void Clear(void); //clear the list box
int Append(wxString sValue); //append the value to the listbox
void InsertItems(const wxArrayString &items, unsigned int pos); //insert the value to the listbox
bool SetBackgroundColour(const wxColour & colour); //set the background colour
void Popup(void);
void SetGrid(wxEGrid *g); //set the grid member
wxEGrid* GetGrid(void); //get the grid member
void SetTextCtrl(wxTextCtrl *p); //set the pointer to the text control
void SetBackgroundColour(wxColour& colour); //override
void GetVirtualSize(int *width, int *height) const; //override
protected:
wxCheckListBox* ListBox;
wxScrolledWindow* Panel;
wxEGrid *grid;
wxTextCtrl *pTextCtrl;
void OnResize(wxSizeEvent& event); //process a size event
void OnKillFocus(wxFocusEvent &event);
void OnActivateWin(wxActivateEvent& event);
void OnItemSelected(wxCommandEvent& event); //the selection in the CheckListBox changed
};
class wxEGrid : public wxGrid
{
public:
/** constructors and Destructors **/
wxEGrid(wxWindow* parent,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxSize( 500,300 ),
long style = wxTAB_TRAVERSAL,
const wxString& name = wxPanelNameStr);
~wxEGrid();
/** OVERRIDEN FUNCTIONS FROM wxGrid **/
virtual void DrawColLabels(wxDC& dc, const wxArrayInt& cols); //Draw a range of column labels, including filters
virtual void DrawColLabel(wxDC& dc, int col); //draw one column label + filter
virtual void DrawRowLabels(wxDC& dc, const wxArrayInt& rows); //draw a range of row labels
virtual void DrawRowLabel(wxDC& dc, int row);//draw 1 row label
virtual void DrawCornerLabel(wxDC& dc); //draw the corner window
void UseNativeColHeader(bool native = true); //indicate if wxRendererNative should be used for drawing col labels
void SetUseNativeColLabels(bool native); //indicate if wxRendererNative should be used for drawing col labels
bool AppendCols(int numCols = 1, bool updateLabels = true); //add numCols column at the end of the table
bool DeleteCols(int pos = 0,int numCols = 1, bool updateLabels = true); //Delete numCols in the table at the position "pos"
bool InsertCols(int pos = 0,int numCols = 1, bool updateLabels = true); //Add numCols in the table at the position "pos"
bool AppendRows(int numRows=1, bool updateLabels=true); //add numRows rows at the end of the table
bool DeleteRows(int pos=0, int numRows=1, bool updateLabels=true); //delete numRows rows in the table at the position "pos"
bool InsertRows(int pos=0, int numRows=1, bool updateLabels=true); //insert numRows rows in the table at the position "pos"
bool CreateGrid(int numRows, int numCols, wxGridSelectionModes selmode=wxGridSelectCells); //Create the grid table
bool SetTable(wxGridTableBase *table, bool takeOwnership=false, wxGridSelectionModes selmode=wxGridSelectCells); //add a data table to the grid
void HideRow(int row); //Hide a row
void ShowRow(int row); //show a hidden row
bool IsRowShown(int row) const; //returns true if a row is shown
/** FILTER API **/
bool IsFilter(void); //return the current filter status
void SetFilter(bool enable=true); //set the filter (true = filters are activated and displayed)
void SetFactor(double factor); //Set the multiplicating factor for the displaying the filters
double GetFactor(void); //Get the multiplicating factor for the displaying the filters
wxString GetFilterValue(int col); //returns the current value of the filter
int GetFilterState(int col); //returns wxCONTROL_CURRENT or wxCONTROL_PRESSED
void ResetFilterState(void); //reset the filter arrow state
void SetCurrentFilterValue(wxString value); //Set the value of the current filter
void ClearFilter(int col); //reset the value of a specific filter
void ClearAllFilter(void); //reset the value of all filters
void ApplyFilter(int col, bool bRecompute = false,
bool bListFilteredValue = false); //apply the filter for a specific column
void ApplyAllFilter(bool bRecompute = false,
bool bListFilteredValue = false); //apply all filters to each column
wxWindow* GetFilterWindow(void); //return a pointer to the filter band
void SetRegExFlag(int flag); //Set the RegEx type
int GetRegExFlag(void); //return the RegEx type
bool AreFilteredApplied(void); //indicate if one or more filters are applied
/** SORTING API **/
bool IsSortingBy(int col = 0) const; //return true if the column sorts
int GetSortingColumn(void) const; //return the 1st column used for sorting
bool IsSortOrderAscending(int col = 0) const; //get the sort order of the column
void SetSortingColumn(int col, bool ascending = true, int key = 1); //set the sorting order of the column
void UnsetSortingColumn(void); //resets the sorting columns
void Sort(int algo = WX_EGRID_INSERTION_SORT); //sort the table
void SwapRows(int row1, int row2); //invert the content from row1 and row2
void CopyRow(int row1, int row2); //copy row2 in row1
/** VISUAL ATTRIBUTE API **/
void GetFilterArrowDim(int *width, int *height); //get the filter arrow Dimension
void SetFilterArrowDim(int width, int height); //set the filter arrow Dimension
wxColour GetFilterArrowColourOn(void); //get the filter arrow color ON
void SetFilterArrowColourOn(wxColour colour); //set the filter arrow color ON
wxColour GetFilterArrowColourOff(void); //get the filter arrow color OFF
void SetFilterArrowColourOff(wxColour colour); //set the filter arrow color OFF
wxColour GetFilterBkColourOn(void); //get the filter background color ON
void SetFilterBkColourOn(wxColour colour); //set the filter background color ON
wxColour GetFilterBkColourOff(void); //get the filter background color OFF
void SetFilterBkColourOff(wxColour colour); //set the filter background color OFF
wxColour GetSortingArrowColour(void); //get the sorting arrow color
void SetSortingArrowColour(wxColour colour); //set the sorting arrow color OFF
protected:
wxEGrid* Grid;
void OnLabelClick(wxGridEvent& event); //process click on a filter
void OnFilterTextLoseFocus(wxFocusEvent& event); //Text Field of filter lose focus
void OnFilterTextCharEvent(wxKeyEvent& event); //Text Field process a WXK_RETURN char event
void OnTextChangedEvent(wxCommandEvent& event); //process a text change in the filter text control
void SetColSizeEvent(wxGridSizeEvent& event); //resize the filters after column resize when needed
void OnClearFilter(wxCommandEvent& event); //the button "CLEAR" was clicked - clear all filters
void OnLeftUp(wxMouseEvent& event); //Release mouse capture
/**Protected filter API **/
wxArrayString ListValues(int col); //returns the content of the filter for a specific column
void Add_Filter(int pos); //insert a new filter (wxString) at pos
void Remove_Filter(int pos); //remove an existing filter (wxString) at pos
void Empty_Filters(void); //delete the wxString created
void Add_FRows(int pos); //add one row to the filtered row array
void Remove_FRows(int pos); //remove one row from the filtered row array
void Empty_FRows(void); //delete the filtered row arrays
bool IsAppliedFilter(int row,int col); //indicate if a row is filtered according to a column different from col
void ResetFilter(int col); //remove the filter from a specific column
void SetFilteredRow(int row); //set the IsRowFiltered flag for a row
/** PROTECTED SORTING API **/
void InsertionSort(); //sort the table
private:
//private members
int LabelHeight; //Label Heigth
double f; //multiplicating factor, for drawing the filters
bool bFilterOn; //true if filters are ON (displayed), false otherwise
int ArrowHeight; //height of the Filter Arrow
int ArrowWidth; //width of the Filter Arrow
wxColour FilterArrowColourOn; //Colour of the Filter arrow when ON
wxColour FilterArrowColourOff; //Colour of the Filter arrow when OFF
wxColour FilterBkColourOn; //Colour of the Filter text when ON
wxColour FilterBkColourOff;//Colour of the Filter text when OFF
wxColour SortArrowColor; //Colour of the sorting arrow
wxTextCtrl *FilterTextCtrl; //the text field that receives the RegEx for filtering
wxPopupFilter *FilterListCtrl; //The CheckListBox which shows the differents value available
wxButton *ClearFilterButton; //Button to disable filter
int iCurrentCol; //the column impacted by a filter focus (text control of listbox)
int FilterState; //>0: the corresponding column filter button is pressed. -1: no buttons pressed
int iMinimalHeight; //Minimal Acceptable Height for the rows
int iRegExType; //wxRE_ADVANCED, wxRE_EXTENDED, wxRE_BASIC
//filters data
wxString **Values; //List of Current filter values
int iFilterNumber; //amount of filter really allocated (nb of wxString pointers of "Values")
int iFilterMaxNumber; //amount of filters that can be allocated (size of "Values")
bool *IsRowFiltered; //indicate if a row is hidden due to a filter (size = iRealSize)
int **AppliedFilter; //indicate which filters (col index) applies on the row (size = iRealSize * iSize[i])
int *iSize; //size of the arrays in AppliedFilter
int iUsedSize; //used size of the dynamic arrays
int iRealSize; //real (allocated) size of the dynamic arrays
bool bFilterApplied; //indicate if filtering is applied
int *iSorting; //indicate if a column is a sorting column, and in which direction
int iSortSize; //size of the array iSortSize
};
#endif // EGRID_H
wxEGrid.cpp:
Code: Select all
#include "wxegrid.h"
/***********************************************************/
/*************** wxPopupFilter CLASS ***********************/
/****************** POPUP CLASS **************************/
/***********************************************************/
wxPopupFilter::wxPopupFilter(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size)
{
//constructor
wxWindowID idList;
//wxWS_EX_TRANSIENT style can maybe be used
Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER, _T("id"));
ListBox = NULL;
Panel = NULL;
grid = NULL;
Panel = new wxScrolledWindow(this, wxID_ANY);
ListBox = new wxCheckListBox(Panel, wxID_ANY, wxPoint(0,0), wxDefaultSize, 0, 0,
wxLB_SINGLE | wxBORDER_NONE |wxLB_HSCROLL,
//wxLB_MULTIPLE | wxBORDER_NONE |wxLB_HSCROLL,
wxDefaultValidator, _T("ID_CHECKLISTBOX1"));
idList = ListBox->GetId();
pTextCtrl = NULL;
Connect(wxEVT_SIZE, (wxObjectEventFunction)&wxPopupFilter::OnResize);
Connect(wxEVT_ACTIVATE, (wxObjectEventFunction)&wxPopupFilter::OnActivateWin);
Connect(wxEVT_KILL_FOCUS, (wxObjectEventFunction)&wxPopupFilter::OnKillFocus);
Connect(idList,wxEVT_COMMAND_CHECKLISTBOX_TOGGLED,(wxObjectEventFunction) &wxPopupFilter::OnItemSelected);
}
wxPopupFilter::~wxPopupFilter(void)
{
//destructor
if (HasCapture()) ReleaseMouse();
}
void wxPopupFilter::SetBackgroundColour(wxColour& colour)
{
if (ListBox != NULL) ListBox->SetBackgroundColour(colour);
wxWindow::SetBackgroundColour(colour);
}
void wxPopupFilter::OnActivateWin(wxActivateEvent& event)
{
//activate or diactivate window
if (!event.GetActive())
{
Show(false);
if (grid != NULL)
{
grid->ApplyAllFilter();
grid->ResetFilterState();
grid->ForceRefresh();
}
}
event.Skip();
}
void wxPopupFilter::OnItemSelected(wxCommandEvent& event)
{
//the selection in the CheckListBox changed
int i, iMax;
wxString s, sFilter;
//Get the list of all checked element
if (pTextCtrl == NULL) return;
i = event.GetInt();
s = event.GetString();
if (ListBox->IsChecked(i))
{
if (s == "<ALL>")
{
//pTextCtrl->SetValue(".*");
pTextCtrl->SetValue("");
return;
}
if (s == "<BLANK>")
{
pTextCtrl->SetValue("^$");
return;
}
if (s == "<NON BLANK>")
{
pTextCtrl->SetValue(".+");
return;
}
}
//scan all texts
iMax = ListBox->GetCount();
sFilter = wxEmptyString;
for(i=0;i<iMax;i++)
{
if (ListBox->IsChecked(i))
{
if (sFilter != wxEmptyString) sFilter += "|";
sFilter += "^";
sFilter += ListBox->GetString(i);
sFilter += "$";
}
}
pTextCtrl->SetValue(sFilter);
}
void wxPopupFilter::SetGrid(wxEGrid *g)
{
//set the grid member
grid = g;
}
wxEGrid* wxPopupFilter::GetGrid(void)
{
//get the grid member
return(grid);
}
bool wxPopupFilter::SetBackgroundColour(const wxColour & colour)
{
//set the background colour
bool bResult;
bResult = wxWindow::SetBackgroundColour(colour);
if (ListBox != NULL) ListBox->SetBackgroundColour(colour);
return(bResult);
}
void wxPopupFilter::Popup(void)
{
Show(true);
}
void wxPopupFilter::OnResize(wxSizeEvent& event)
{
//process a size event
wxSize cs;
cs = GetClientSize();
if (Panel != NULL)
{
//cs.SetHeight(50);
Panel->SetSize(cs);
}
if (ListBox != NULL)
{
//cs.SetHeight(50);
ListBox->SetSize(cs);
}
}
void wxPopupFilter::Clear(void)
{
//clear the list box
if (ListBox != NULL) ListBox->Clear();
}
int wxPopupFilter::Append(wxString sValue)
{
//append the value to the listbox
if (ListBox != NULL)
{
return(ListBox->Append(sValue));
}
return(-1);
}
void wxPopupFilter::InsertItems(const wxArrayString &items, unsigned int pos)
{
//insert the value to the listbox
if (ListBox != NULL)
{
ListBox->Append(items);
}
return;
}
void wxPopupFilter::OnKillFocus(wxFocusEvent &event)
{
wxWindow *LabelWin;
if (HasCapture())
{
ReleaseMouse();
Show(false);
if (grid != NULL)
{
LabelWin = (wxWindow *) grid->GetFilterWindow();
if (LabelWin != NULL)
{
LabelWin->Refresh(true,NULL);
LabelWin->Update();
}
grid->ResetFilterState();
grid->ForceRefresh();
grid->Update();
}
}
event.Skip();
}
void wxPopupFilter::SetTextCtrl(wxTextCtrl *p)
{
//set the pointer to the text control
pTextCtrl = p;
}
void wxPopupFilter::GetVirtualSize(int *width, int *height) const
{
//override
if (ListBox != NULL)
{
ListBox->GetVirtualSize(width,height);
}
else
{
wxWindow::GetVirtualSize(width,height);
}
}
/***********************************************************/
/*********************** EGRID CLASS ***********************/
/****************** THE MAIN CLASS ***********************/
/***********************************************************/
wxEGrid::wxEGrid(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name)
: wxGrid(parent, id, pos, size, style)
{
//CONSTRUTOR
wxColourDatabase wxColourDB;
wxWindow *gWindow, *GridWindow;
wxWindowID TextCtrlID, ButttonID;
int iHeight1, iHeight2;
iHeight1 = GetRowMinimalAcceptableHeight();
iHeight2 = GetDefaultRowSize();
iMinimalHeight = wxMax(iHeight1, iHeight2);
SetRowMinimalAcceptableHeight(0);
ArrowWidth = 8;
ArrowHeight = 4;
//Constructor
Grid = this;
bFilterOn = true;
LabelHeight = GetColLabelSize();
gWindow = GetGridColLabelWindow();
//gWindow = this;
//increase the LabelHeight by f for the place for the Filters
f = 1.7;
LabelHeight = LabelHeight * f;
SetColLabelSize(LabelHeight);
//Filters colour
FilterArrowColourOn = *wxRED;
FilterArrowColourOff = *wxBLACK;
FilterBkColourOn = wxColourDB.Find("LIGHT BLUE"); //STEEL BLUE
FilterBkColourOff = *wxWHITE;
iRegExType = wxRE_ADVANCED;
//filters data
iCurrentCol = -1;
FilterState = -1;
FilterTextCtrl = NULL;
FilterListCtrl = NULL;
Values = NULL;
IsRowFiltered = NULL;
AppliedFilter = NULL;
iSize = NULL;
iFilterNumber = 0;
iFilterMaxNumber = 0;
iUsedSize = 0;
iRealSize = 0;
bFilterApplied = false;
//sorting
iSorting = NULL;
iSortSize = 0;
//Bug Correction
GridWindow = GetGridWindow();
if (GridWindow != NULL)
{
//GridWindow->Connect(wxEVT_LEFT_UP,(wxObjectEventFunction)&wxEGrid::OnLeftUp);
}
//clear filter button
ClearFilterButton = new wxButton(this, wxID_ANY, "CLEAR", wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON"));
if (ClearFilterButton != NULL)
{
ClearFilterButton->Show(true);
ButttonID = ClearFilterButton->GetId();
Connect(ButttonID, wxEVT_COMMAND_BUTTON_CLICKED ,(wxObjectEventFunction)&wxEGrid::OnClearFilter);
}
//filter widgets
FilterTextCtrl = new wxTextCtrl(gWindow, wxID_ANY, _("Text"), wxPoint(96,288), wxDefaultSize, wxTE_CENTRE | wxTE_PROCESS_ENTER, wxDefaultValidator, _T("ID_TEXTCTRL1"));
if (FilterTextCtrl != NULL)
{
FilterTextCtrl->Show(false);
}
FilterListCtrl = new wxPopupFilter(gWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize);
if (FilterListCtrl != NULL)
{
FilterListCtrl->Show(false);
FilterListCtrl->SetGrid(this);
FilterListCtrl->SetBackgroundColour(FilterBkColourOff);
FilterListCtrl->SetTextCtrl(FilterTextCtrl);
}
//sort arrow color
SortArrowColor = *wxBLACK; //Colour of the sorting arrow
//events for filter click
Connect(wxEVT_GRID_LABEL_LEFT_CLICK,(wxObjectEventFunction)&wxEGrid::OnLabelClick);
Connect(wxEVT_GRID_COL_SIZE,(wxObjectEventFunction)&wxEGrid::SetColSizeEvent);
//connects events for the filters
if (FilterTextCtrl != NULL)
{
FilterTextCtrl->Connect(wxEVT_KILL_FOCUS, (wxObjectEventFunction)&wxEGrid::OnFilterTextLoseFocus, NULL, this);
FilterTextCtrl->Connect(wxEVT_CHAR, (wxObjectEventFunction)&wxEGrid::OnFilterTextCharEvent, NULL, this);
TextCtrlID = FilterTextCtrl->GetId();
Connect(TextCtrlID, wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&wxEGrid::OnTextChangedEvent);
}
}
wxEGrid::~wxEGrid()
{
//DESTRUCTOR
int i, iMax;
//delete objects
if (FilterTextCtrl != NULL) FilterTextCtrl->Destroy();
if (FilterListCtrl != NULL) FilterListCtrl->Destroy();
if (Values != NULL)
{
for(i=0; i<iFilterNumber; i++) delete Values[i];
free(Values);
}
if (iSorting != NULL) free(iSorting);
if (IsRowFiltered != NULL) free(IsRowFiltered);
if (AppliedFilter != NULL)
{
iMax = iUsedSize;
for(i=0; i<iMax; i++)
{
if (AppliedFilter[i] != NULL) free(AppliedFilter[i]);
}
free(AppliedFilter);
}
if (iSize != NULL) free(iSize);
// Disconnect Events
Disconnect(wxEVT_GRID_LABEL_LEFT_CLICK,(wxObjectEventFunction)&wxEGrid::OnLabelClick);
Disconnect(wxEVT_GRID_COL_SIZE,(wxObjectEventFunction)&wxEGrid::SetColSizeEvent);
}
/** EVENTS HANDLERS **/
void wxEGrid::OnLeftUp(wxMouseEvent& event)
{
//release mouse capture
wxWindow *GridWindow;
if (HasCapture())
{
ReleaseMouse();
}
else
{
GridWindow = GetGridWindow();
if (GridWindow != NULL)
{
if (GridWindow->HasCapture())
{
GridWindow->ReleaseMouse();
}
}
}
}
void wxEGrid::OnClearFilter(wxCommandEvent& event)
{
//the button "CLEAR" was clicked - clear all filters
BeginBatch();
ClearAllFilter();
EndBatch();
}
void wxEGrid::OnFilterTextLoseFocus(wxFocusEvent& event)
{
//Text Field of filter lose focus
if (FilterTextCtrl != NULL)
{
SetCurrentFilterValue(FilterTextCtrl->GetValue());
ApplyFilter(iCurrentCol);
FilterTextCtrl->Show(false);
iCurrentCol = -1;
}
event.Skip();
}
void wxEGrid::OnTextChangedEvent(wxCommandEvent& event)
{
//process a text change in the filter text control
wxWindow *LabelWindow;
wxRect InvalideArea;
int iRight, iLeft, x, y;
wxString sValue;
sValue = FilterTextCtrl->GetValue();
SetCurrentFilterValue(sValue);
//change the Background color of the text control
if (sValue == wxEmptyString)
{
FilterTextCtrl->SetBackgroundColour(FilterBkColourOff);
FilterListCtrl->SetBackgroundColour(FilterBkColourOff);
}
else
{
FilterTextCtrl->SetBackgroundColour(FilterBkColourOn);
FilterListCtrl->SetBackgroundColour(FilterBkColourOn);
}
//repaint the filter arrow to get the correct color
LabelWindow = GetGridColLabelWindow();
iLeft = GetColLeft(iCurrentCol);
iRight = GetColRight(iCurrentCol);
CalcUnscrolledPosition(0,0,&x,&y);
InvalideArea = wxRect(iLeft-x,0,iRight - iLeft,LabelHeight);
if (LabelWindow != NULL)
{
LabelWindow->Refresh(true,&InvalideArea);
LabelWindow->Update();
}
}
void wxEGrid::OnFilterTextCharEvent(wxKeyEvent& event)
{
//Text Field process a WXK_RETURN char event
if (FilterTextCtrl != NULL)
{
if (event.GetKeyCode() == WXK_RETURN)
{
SetCurrentFilterValue(FilterTextCtrl->GetValue());
ApplyFilter(iCurrentCol);
FilterTextCtrl->Show(false);
iCurrentCol = -1;
}
else
{
SetCurrentFilterValue(FilterTextCtrl->GetValue());
}
}
event.Skip();
}
/** OVERRIDEN FUNCTIONS FROM wxGrid **/
void wxEGrid::DrawColLabels(wxDC& dc, const wxArrayInt& cols)
{
//Draw a range of column labels, including filters
int i, numLabels;
wxCoord width,height;
int iLeft, iWidth,x,y;
wxRect r;
numLabels = GetNumberCols();
dc.GetSize(&width, &height);
CalcScrolledPosition(0,0,&x,&y);
for(i = 0; i < numLabels; i++ )
{
r = CellToRect(0,i);
iLeft = r.GetX() + x;
iWidth = r.GetWidth();
if ((iLeft+iWidth > 0) && (iLeft<width)) DrawColLabel(dc,i);
}
}
void wxEGrid::DrawColLabel(wxDC& dc, int col)
{
//draw one column label + filter
//wxGrid::DrawColLabel(dc,col);
int colLeft,colRight;
int hAlign, vAlign;
int orient;
int lh, lh2, lh3, iColWidth;
wxRect rect,rect3,rect4;
wxFont ft;
wxString FilterValue;
wxBrush FilterBkBrush, ArrowBkBrush;
wxPoint Pt[3];
int iActivated;
if ( GetColWidth(col) <= 0 || LabelHeight <= 0 ) return;
colLeft = GetColLeft(col);
if (bFilterOn) lh = LabelHeight / f; else lh = LabelHeight;
lh2 = lh + (LabelHeight - lh) / 2;
lh3 = ArrowHeight / 2;
iColWidth = GetColWidth(col);
rect = wxRect(colLeft, 0, iColWidth, lh);
if ( m_nativeColumnLabels )
{
wxRendererNative::Get().DrawHeaderButton((wxWindow*)GetColLabelWindow(),
dc,
rect,
0,
IsSortingBy(col)
? IsSortOrderAscending(col)
? wxHDR_SORT_ICON_UP
: wxHDR_SORT_ICON_DOWN
: wxHDR_SORT_ICON_NONE
);
//draw vertical lines for filters
colRight = GetColRight(col) - 1;
dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
dc.DrawLine(colRight, lh,
colRight, LabelHeight - 1);
dc.DrawLine(colLeft, lh - 1,
colRight , lh - 1);
dc.SetPen(*wxWHITE_PEN);
dc.DrawLine(colLeft, lh, colLeft,LabelHeight - 1);
dc.DrawLine(colLeft, lh ,
colRight , lh );
}
else
{
colRight = GetColRight(col) - 1;
dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
dc.DrawLine(colRight, 0,
colRight, LabelHeight - 1);
dc.DrawLine(colLeft, 0,
colRight, 0);
dc.DrawLine(colLeft, LabelHeight - 1,
colRight + 1, LabelHeight - 1);
//Filter
dc.DrawLine(colLeft, lh - 1,
colRight , lh - 1);
dc.SetPen(*wxWHITE_PEN);
dc.DrawLine(colLeft, 1, colLeft,LabelHeight - 1);
dc.DrawLine(colLeft, 1, colRight, 1);
//Filter
dc.DrawLine(colLeft, lh ,
colRight , lh );
//sorting arrow
if (IsSortingBy(col))
{
dc.SetBrush(wxBrush(SortArrowColor));
dc.SetPen(*wxTRANSPARENT_PEN);
if (IsSortOrderAscending(col))
{
Pt[0] = wxPoint(colLeft + iColWidth - 4, lh - 5);
Pt[1] = wxPoint(colLeft + iColWidth - 15, lh - 5);
Pt[2] = wxPoint(colLeft + iColWidth - 10, lh - 11);
}
else
{
Pt[0] = wxPoint(colLeft + iColWidth - 4, lh - 11);
Pt[1] = wxPoint(colLeft + iColWidth - 15, lh - 11);
Pt[2] = wxPoint(colLeft + iColWidth - 10, lh - 5);
}
dc.DrawPolygon(3,Pt);
dc.SetBrush(wxNullBrush);
};
}
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
dc.SetTextForeground(GetLabelTextColour());
dc.SetFont(GetLabelFont());
GetColLabelAlignment(&hAlign, &vAlign);
orient = GetColLabelTextOrientation();
rect.Deflate(2);
DrawTextRectangle(dc, GetColLabelValue(col), rect, hAlign, vAlign, orient);
if (bFilterOn)
{
//draw the filter
ft = GetLabelFont();
ft.SetWeight(wxFONTWEIGHT_NORMAL);
dc.SetFont(ft);
rect3 = wxRect(colLeft+3, lh + 2,
iColWidth - ArrowWidth - 8, LabelHeight - lh - 4);//rectangle for text
rect4 = wxRect(colLeft +iColWidth - ArrowWidth - 6, lh + 3,
ArrowWidth + 5, LabelHeight - lh - 6);//outline rectangle for the button
FilterValue = GetFilterValue(col);
Pt[0] = wxPoint(colLeft + iColWidth - ArrowWidth/2 - 4, lh2 + lh3);
Pt[1] = wxPoint(colLeft + iColWidth - 4, lh2 - lh3);
Pt[2] = wxPoint(colLeft + iColWidth - 4 - ArrowWidth, lh2 - lh3);
if (FilterValue == wxEmptyString)
{
//Filter not activated
ArrowBkBrush = wxBrush(FilterArrowColourOff);
FilterBkBrush = wxBrush(FilterBkColourOff);
}
else
{
//Filter activated
ArrowBkBrush = wxBrush(FilterArrowColourOn);
FilterBkBrush = wxBrush(FilterBkColourOn);
}
//draw the Push button
iActivated = GetFilterState(col); //wxCONTROL_CURRENT or wxCONTROL_PRESSED
wxRendererNative::Get().DrawPushButton(this,dc,rect4, iActivated);
//draw the dropdown arrow
dc.SetBrush(ArrowBkBrush);
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawPolygon(3,Pt);
//Draw the text
dc.SetBrush(FilterBkBrush);
dc.SetBackgroundMode(wxTRANSPARENT);
dc.DrawRectangle(colLeft+2, lh + 2, iColWidth - ArrowWidth - 8,LabelHeight - lh - 4);
dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
dc.DrawLine(colLeft+2,lh + 2,colLeft + iColWidth - ArrowWidth - 6,lh + 2);
dc.DrawLine(colLeft+2,lh + 2,colLeft + 2, LabelHeight - 3);
dc.SetPen(*wxWHITE_PEN);
dc.DrawLine(colLeft+2,LabelHeight - 3,colLeft + iColWidth - ArrowWidth - 6,LabelHeight - 3);
dc.DrawLine(colLeft + iColWidth - ArrowWidth - 6,lh + 2,colLeft + iColWidth - ArrowWidth - 6, LabelHeight - 3);
DrawTextRectangle(dc, FilterValue, rect3, wxALIGN_CENTRE, wxALIGN_CENTRE, wxHORIZONTAL);
dc.SetBrush(wxNullBrush);
dc.SetPen(wxNullPen);
}
}
void wxEGrid::DrawCornerLabel(wxDC& dc)
{
//draws the corner window
int lh, w, h;
wxWindow *pCornerWindow;
if ( m_nativeColumnLabels )
{
wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight));
rect.Deflate(1);
wxRendererNative::Get().DrawHeaderButton((wxWindow*) GetGridCornerLabelWindow(), dc, rect, 0);
}
else
{
dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1, m_rowLabelWidth - 1, 0 );
dc.DrawLine( m_rowLabelWidth - 1, m_colLabelHeight - 1, 0, m_colLabelHeight - 1 );
dc.DrawLine( 0, 0, m_rowLabelWidth, 0 );
dc.DrawLine( 0, 0, 0, m_colLabelHeight );
dc.SetPen( *wxWHITE_PEN );
dc.DrawLine( 1, 1, m_rowLabelWidth - 1, 1 );
dc.DrawLine( 1, 1, 1, m_colLabelHeight - 1 );
}
if (bFilterOn)
{
if (ClearFilterButton != NULL)
{
lh = LabelHeight / f;
pCornerWindow = GetGridCornerLabelWindow();
if (pCornerWindow != NULL)
{
pCornerWindow->GetSize(&w,&h);
pCornerWindow->SetSize(0,0,w,lh);
ClearFilterButton->SetSize(1,lh,w-2,LabelHeight-lh);
}
}
}
}
void wxEGrid::DrawRowLabels(wxDC& dc, const wxArrayInt& rows)
{
//draw a range of row labels
const size_t numLabels = rows.GetCount();
for(size_t i = 0; i < numLabels; i++)
{
DrawRowLabel(dc, rows[i]);
}
}
void wxEGrid::DrawRowLabel(wxDC& dc, int row)
{
//draw 1 row label
if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 )
return;
wxRect rect;
int rowTop = GetRowTop(row),
rowBottom = GetRowBottom(row) - 1;
dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
dc.DrawLine( m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom );
dc.DrawLine( 0, rowTop, 0, rowBottom );
dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom );
dc.SetPen( *wxWHITE_PEN );
dc.DrawLine( 1, rowTop, 1, rowBottom );
dc.DrawLine( 1, rowTop, m_rowLabelWidth - 1, rowTop );
dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT );
if (bFilterApplied)
{
dc.SetTextForeground(FilterArrowColourOn);
}
else
{
dc.SetTextForeground(GetLabelTextColour());
}
dc.SetFont( GetLabelFont() );
int hAlign, vAlign;
GetRowLabelAlignment( &hAlign, &vAlign );
rect.SetX( 2 );
rect.SetY( GetRowTop(row) + 2 );
rect.SetWidth( m_rowLabelWidth - 4 );
rect.SetHeight( GetRowHeight(row) - 4 );
DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
}
void wxEGrid::UseNativeColHeader(bool native)
{
//indicate if wxRendererNative should be used for drawing col labels
return(SetUseNativeColLabels(native));
}
void wxEGrid::SetUseNativeColLabels(bool native)
{
//indicate if wxRendererNative should be used for drawing col labels
int iHeight;
BeginBatch();
wxGrid::SetUseNativeColLabels(native);
//label size is modified if native == true
//therefore we must modify it back to display filter correctly
if (native)
{
iHeight = GetColLabelSize(); //get the new label size
SetColLabelSize(iHeight * f);
LabelHeight = iHeight * f;
}
EndBatch();
Refresh();
}
bool wxEGrid::AppendCols(int numCols, bool updateLabels)
{
//add numCols column at the end of the table
bool bResult;
int i;
//Append the columns
bResult = wxGrid::AppendCols(numCols, updateLabels);
//Create the filters
if (bResult)
{
for(i = 0; i<numCols;i++) Add_Filter(iFilterNumber + i);
}
return(bResult);
}
bool wxEGrid::DeleteCols(int pos, int numCols, bool updateLabels)
{
//Delete numCols in the table at the position "pos"
bool bResult;
int i;
//Delete the columns
bResult = wxGrid::DeleteCols(pos, numCols, updateLabels);
//Delete the filters
if (bResult)
{
for(i = 0; i<numCols;i++) Remove_Filter(pos);
}
return(bResult);
}
bool wxEGrid::InsertCols(int pos, int numCols, bool updateLabels)
{
//Add numCols in the table at the position "pos"
bool bResult;
int i;
//add the columns
bResult = wxGrid::InsertCols(pos, numCols, updateLabels);
//add the filters
if (bResult)
{
for(i = 0; i<numCols;i++) Add_Filter(pos);
}
return(bResult);
}
bool wxEGrid::AppendRows(int numRows, bool updateLabels)
{
//add numRows rows at the end of the table
bool bResult;
int i;
bResult = wxGrid::AppendRows(numRows, updateLabels);
//Filtered rows table update
if (bResult)
{
for(i = 0; i<numRows;i++) Add_FRows(iFilterNumber + i);
}
return(bResult);
}
bool wxEGrid::DeleteRows(int pos, int numRows, bool updateLabels)
{
//delete numRows rows in the table at the position "pos"
bool bResult;
int i;
bResult = wxGrid::DeleteRows(pos, numRows, updateLabels);
//Filtered rows table update
if (bResult)
{
for(i = 0; i<numRows;i++) Remove_FRows(pos);
}
return(bResult);
}
bool wxEGrid::InsertRows(int pos, int numRows, bool updateLabels)
{
//insert numRows rows in the table at the position "pos"
bool bResult;
int i;
bResult = wxGrid::InsertRows(pos, numRows, updateLabels);
//Filtered rows table update
if (bResult)
{
for(i = 0; i<numRows;i++) Add_FRows(pos);
}
return(bResult);
}
void wxEGrid::Add_Filter(int pos)
{
//insert a new filter (wxString) at pos
int i, iPos, j, jMin;
iPos = pos;
if (pos <0 ) iPos = 0;
if (pos > iFilterNumber) iPos = iFilterNumber;
//allocate new memory if necessary
if (iFilterMaxNumber <= iFilterNumber)
{
iFilterMaxNumber = iFilterMaxNumber + WX_STRING_ARRAY_SIZE;
iSortSize = iSortSize + WX_STRING_ARRAY_SIZE;
Values = (wxString **) realloc(Values,iFilterMaxNumber * sizeof(wxString*));
iSorting = (int *) realloc(iSorting, iSortSize * sizeof(int));
//initialisation
jMin = iSortSize - WX_STRING_ARRAY_SIZE;
for(j=jMin;j<iSortSize;j++) iSorting[j] = 0;
}
if (Values == NULL) return;
if (iSorting == NULL) return;
//shift the existing wxString to the right
for(i=iFilterNumber-1; i>=iPos; i--)
{
Values[i+1] = Values[i];
iSorting[i+1] = iSorting[i];
}
//allocate the new wxString
Values[iPos] = new wxString();
iSorting[iPos] = 0;
iFilterNumber++;
}
void wxEGrid::Remove_Filter(int pos)
{
//remove an existing filter (wxString) at pos
int i, iPos;
iPos = pos;
if (pos <0 ) iPos = 0;
if (pos > iFilterNumber-1) iPos = iFilterNumber-1;
//delete the wxString
if (Values == NULL) return;
delete Values[iPos];
//shift the values to the left
for(i=iPos; i<iFilterNumber-1; i++)
{
Values[i] = Values[i+1];
iSorting[i] = iSorting[i+1];
}
iFilterNumber--;
}
void wxEGrid::Empty_Filters(void)
{
//delete the wxString created
int i;
if (Values == NULL) return;
for(i=0; i<iFilterNumber;i++) delete Values[i];
iFilterNumber = 0;
}
void wxEGrid::Add_FRows(int pos)
{
//add one row to the filtered row array
/*
bool *IsRowFiltered; //indicate if a row is hidden due to a filter (size = iRealSize)
int **AppliedFilter; //indicate which filters (col index) applies on the row (size = iRealSize * iSize[i])
int *iSize; //size of the arrays in AppliedFilter
int iUsedSize; //used size of the dynamic arrays
int iRealSize; //real (allocated) size of the dynamic arrays
WX_ROWCOL_ARRAY_SIZE
*/
int iPos, i, j;
iPos = pos;
if (pos < 0) iPos = 0;
if (pos > iUsedSize) iPos = iUsedSize;
//allocate memory when necessary
if (iUsedSize >= iRealSize)
{
iRealSize = iRealSize + WX_ROW_ARRAY_SIZE;
IsRowFiltered = (bool *) realloc(IsRowFiltered, iRealSize * sizeof(bool));
iSize = (int *) realloc(iSize, iRealSize * sizeof(int));
AppliedFilter = (int **) realloc(AppliedFilter, iRealSize * sizeof(int*));
}
if (AppliedFilter == NULL) return;
if (IsRowFiltered == NULL) return;
if (iSize == NULL) return;
//shift the values to the right
for(i=iUsedSize-1; i>=iPos; i--)
{
AppliedFilter[i+1] = AppliedFilter[i];
iSize[i+1] = iSize[i];
IsRowFiltered[i+1] = IsRowFiltered[i];
}
//give standard values
AppliedFilter[iPos] = (int*) malloc(WX_ROWCOL_ARRAY_SIZE * sizeof(int));
if (AppliedFilter[iPos] == NULL)
{
iSize[iPos] = 0;
return;
}
iSize[iPos] = WX_ROWCOL_ARRAY_SIZE;
for(j=0; j<WX_ROWCOL_ARRAY_SIZE; j++) AppliedFilter[iPos][j] = -1;
IsRowFiltered[iPos] = false;
iUsedSize++;
}
void wxEGrid::Remove_FRows(int pos)
{
//remove one row from the filtered row array
int iPos, i;
iPos = pos;
if (pos < 0) iPos = 0;
if (pos > iUsedSize) iPos = iUsedSize;
if (AppliedFilter == NULL) return;
if (IsRowFiltered == NULL) return;
if (iSize == NULL) return;
//free some memory
if (iSize[iPos] > 0) free(AppliedFilter[iPos]);
//shift the values to the left
for(i=iPos; i<iUsedSize-1; i++)
{
AppliedFilter[i] = AppliedFilter[i+1];
iSize[i] = iSize[i+1];
IsRowFiltered[i] = IsRowFiltered[i+1];
}
iUsedSize--;
}
void wxEGrid::Empty_FRows(void)
{
//delete the filtered row arrays
int i;
if (AppliedFilter == NULL) return;
if (IsRowFiltered == NULL) return;
if (iSize == NULL) return;
for(i=0;i<iUsedSize;i++)
{
IsRowFiltered[i] = false;
if (AppliedFilter[i] != NULL)
{
free(AppliedFilter[i]);
AppliedFilter[i] = NULL;
iSize[i] = 0;
}
}
}
bool wxEGrid::CreateGrid(int numRows, int numCols, wxGridSelectionModes selmode)
{
//Create the grid table
bool bResult;
int i;
//create the grid
bResult = wxGrid::CreateGrid(numRows, numCols, selmode);
//create the filters
if (bResult)
{
Empty_Filters();
for(i = 0; i<numCols;i++) Add_Filter(i);
Empty_FRows();
for(i = 0; i<numRows;i++) Add_FRows(i);
}
return(bResult);
}
bool wxEGrid::SetTable(wxGridTableBase *table, bool takeOwnership, wxGridSelectionModes selmode)
{
//add a data table to the grid
bool bResult;
int i, numCols, numRows;
bResult = wxGrid::SetTable(table, takeOwnership, selmode);
if ((bResult) && (table != NULL))
{
Empty_Filters();
numCols = table->GetNumberCols();
for(i = 0; i<numCols;i++) Add_Filter(i);
Empty_FRows();
numRows = table->GetNumberRows();
for(i = 0; i<numRows;i++) Add_FRows(i);
}
return(bResult);
}
void wxEGrid::HideRow(int row)
{
//Hide a row
int iSize;
if (row < 0) return;
if (row >= GetNumberRows()) return;
iSize = GetRowSize(row);
if (iSize <= 0) return; //already hidden
SetRowSize(row, 0); //hide the row by setting its size to a negative value
}
void wxEGrid::ShowRow(int row)
{
//show a hidden row
int iSize;
if (row < 0) return;
if (row >= GetNumberRows()) return;
if (IsRowFiltered != NULL)
{
//do not show because it is filtered
//it should stay hidden
if (IsRowFiltered[row]) return;
}
iSize = GetRowSize(row);
if (iSize > 0) return; //already shown
//show the row by setting its size to a strictly positive value
if (iSize == 0) SetRowSize(row, iMinimalHeight); else SetRowSize(row, -iSize);
}
bool wxEGrid::IsRowShown(int row) const
{
//returns true if a row is shown3
int iSize;
if (row < 0) return(false);
if (row >= GetNumberRows()) return(false);
iSize = GetRowSize(row);
if (iSize > 0) return(true);
return(false);
}
void wxEGrid::SetColSizeEvent(wxGridSizeEvent& event)
{
//hide filters window after col resizing
int col, iLeft, iRight, iButton,x, y;
col = event.GetRowOrCol();
if (col == iCurrentCol)
{
if (FilterTextCtrl != NULL)
{
iLeft = GetColLeft(col);
iRight = GetColRight(col);
iButton = iRight - ArrowWidth - 5; // X coordinate of the Push button
CalcScrolledPosition(0,0,&x,&y);
FilterTextCtrl->SetSize(iLeft+3 + x, LabelHeight / f + 2,
iButton - iLeft-3, LabelHeight -LabelHeight / f - 4);
}
}
if (col + 1 == iCurrentCol)
{
if (FilterTextCtrl != NULL)
{
iLeft = GetColLeft(iCurrentCol);
iRight = GetColRight(iCurrentCol);
iButton = iRight - ArrowWidth - 5; // X coordinate of the Push button
CalcScrolledPosition(0,0,&x,&y);
FilterTextCtrl->SetSize(iLeft+3 + x, LabelHeight / f + 2,
iButton - iLeft-3, LabelHeight -LabelHeight / f - 4);
}
}
}
/** FILTERS API **/
bool wxEGrid::IsFilter(void)
{
//return the current filter status
return(bFilterOn);
}
void wxEGrid::SetFilter(bool enable)
{
//set the filter (true = filters are activated and displayed)
if (bFilterOn == enable) return;
bFilterOn = enable;
if (bFilterOn)
{
LabelHeight = LabelHeight * f;
SetColLabelSize(LabelHeight);
if (ClearFilterButton!=NULL) ClearFilterButton->Show(true);
}
else
{
LabelHeight = LabelHeight / f;
SetColLabelSize(LabelHeight);
if (ClearFilterButton!=NULL) ClearFilterButton->Show(false);
if (FilterListCtrl!=NULL) FilterListCtrl->Show(false);
if (FilterTextCtrl!=NULL) FilterTextCtrl->Show(false);
}
ForceRefresh();
}
void wxEGrid::SetFactor(double factor)
{
//Set the multiplicating factor for the displaying the filters
//ColLabelSize = LabelHeight = f * size of column header = size of label + size of filter
if (factor < 1) return;
LabelHeight = LabelHeight / f;
f = factor;
LabelHeight = LabelHeight * f;
SetColLabelSize(LabelHeight);
ForceRefresh();
}
double wxEGrid::GetFactor(void)
{
//Get the multiplicating factor for the displaying the filters
return(f);
}
wxString wxEGrid::GetFilterValue(int col)
{
//returns the current value of the filter
//Get the value of the current filter
if ((col >= 0) && (col < GetNumberCols()))
{
return(*Values[col]);
}
return(wxEmptyString);
}
wxArrayString wxEGrid::ListValues(int col)
{
//returns the content of the filter for a specific column
wxSortedArrayString sArray;
wxString s;
int i, iMax;
bool bBlank, bNonBlank;
if (col < 0) return(wxArrayString());
if (col >= GetNumberCols()) return(wxArrayString());
iMax = GetNumberRows();
bBlank = false;
bNonBlank = false;
for(i=0; i<iMax ; i++)
{
if (IsRowShown(i))
{
s = GetCellValue(i,col);
if (s == wxEmptyString)
{
bBlank = true;
}
else
{
bNonBlank = true;
if (sArray.Index(s) == wxNOT_FOUND) sArray.Add(s);
}
}
else if ((!IsAppliedFilter(i,col)) && (IsRowFiltered[i]))
{
//the row is hidden due to a filter on another column
s = GetCellValue(i,col);
if (s == wxEmptyString)
{
bBlank = true;
}
else
{
bNonBlank = true;
if (sArray.Index(s) == wxNOT_FOUND) sArray.Add(s);
}
}
}
if (bBlank) sArray.Add("<BLANK>");
if (bNonBlank) sArray.Add("<NON BLANK>");
return(sArray);
}
bool wxEGrid::IsAppliedFilter(int row,int col)
{
//indicate if a row is filtered according to the column different from col
int i, iMax;
if (row < 0) return(false);
if (row > GetNumberRows()) return(false);
if (AppliedFilter == NULL) return(false);
if (iSize == NULL) return(false);
if (AppliedFilter[row] == NULL) return(false);
iMax = iSize[row];
for(i=0;i<iMax;i++)
{
if ((AppliedFilter[row][i] != col) && (AppliedFilter[row][i] != -1)) return(true);
}
return(false);
}
int wxEGrid::GetFilterState(int col)
{
//returns wxCONTROL_CURRENT or wxCONTROL_PRESSED
if (FilterState == col) return(wxCONTROL_PRESSED);
return(wxCONTROL_CURRENT);
}
void wxEGrid::ResetFilterState(void)
{
//reset the filter arrows state
FilterState = -1;
}
void wxEGrid::ClearFilter(int col)
{
//reset the value of a specific filter
wxWindow *LabelWin;
if (col < 0) return;
if (col >= GetNumberCols()) return;
*Values[col] = wxEmptyString;
LabelWin = (wxWindow *) GetColLabelWindow();
if (GetBatchCount() == 0 )
{
if (LabelWin != NULL)
{
LabelWin->Refresh(true,NULL);
LabelWin->Update();
}
}
ApplyFilter(col);
}
void wxEGrid::ClearAllFilter(void)
{
//reset the value of all filters
int i, iMax;
iMax = GetNumberCols();
for(i=0;i<iMax;i++) ClearFilter(i);
}
void wxEGrid::ApplyFilter(int col, bool bRecompute, bool bListFilteredValue)
{
//apply the filter for a specific column
wxString s;
int i, iMax, j, jMax, k;
wxRegEx *r;
bool bAllocate;
if (col < 0) return;
if (col >= GetNumberCols()) return;
if (Values == NULL) return;
s = *Values[col];
r = new wxRegEx(s,iRegExType);
if (r == NULL) return;
BeginBatch();
//remove the previous filter from this column
ResetFilter(col);
//apply the new filter
if (r->IsValid())
{
iMax = GetNumberRows();
for(i=0;i<iMax;i++)
{
//apply the filter only on specific rows
//case 1: the row is visible: apply the filter
//case 2: the row was hidden by a filter on another column: apply the filter
//case 3: the row was hidden by the user : do not apply the filter
//
//flags
//case 1: (IsRowShown(i) == true)
//case 2: ((IsRowFiltered[i] = true)&&(IsRowShown(i) == false))
//case 3: ((IsRowFiltered[i] = false)&&(IsRowShown(i) == false))
if ((IsRowShown(i) == true) || ((IsRowFiltered[i] = true)&&(IsRowShown(i) == false)))
{
//case 1 OR case 2
//test the filter
s = GetCellValue(i,col);
if (!r->Matches(s))
{
//there is a match - set the filter flags
jMax = iSize[i];
bAllocate = true;
for(j=0;j<jMax;j++)
{
if (AppliedFilter[i][j] == -1) AppliedFilter[i][j] = col;
bAllocate = false;
break;
}
if (bAllocate)
{
AppliedFilter[i] = (int *) realloc(AppliedFilter[i], (iSize[i]+WX_ROWCOL_ARRAY_SIZE) * sizeof(int));
if (AppliedFilter[i] == NULL) return;
iSize[i] = iSize[i] + WX_ROWCOL_ARRAY_SIZE;
//initialisation
for(k=iSize[i] - WX_ROWCOL_ARRAY_SIZE; k < iSize[i]; k++) AppliedFilter[i][k] = -1;
}
SetFilteredRow(i);
}
//check if the row needs to be hidden or not
if (IsRowFiltered[i] == true) HideRow(i); else ShowRow(i);
}
}
}
bFilterApplied = AreFilteredApplied();
EndBatch();
ForceRefresh();
delete r;
}
bool wxEGrid::AreFilteredApplied(void)
{
//indicate if one or more filters are applied
int i, iMax;
iMax = GetNumberCols();
if (Values == NULL) return(false);
for(i=0;i<iMax;i++)
{
if (*Values[i] != wxEmptyString) return(true);
}
return(false);
}
void wxEGrid::ResetFilter(int col)
{
//remove the filter from a specific column
int i, iMax, j, jMax;
if (col < 0) return;
if (col > GetNumberCols()) return;
if (IsRowFiltered == NULL) return;
if (AppliedFilter == NULL) return;
if (iSize == NULL) return;
iMax = GetNumberRows();
for(i=0;i<iMax;i++)
{
//check 1st if a filter is applied on the row
if (IsRowFiltered[i])
{
//if a filter is applied on it, look up if it is due to the filter from column "col"
jMax = iSize[i];
for(j=0;j<jMax;j++)
{
if (AppliedFilter[i][j] == col) AppliedFilter[i][j]=-1;
}
SetFilteredRow(i);
if (!IsRowFiltered[i]) ShowRow(i);
}
}
}
void wxEGrid::SetFilteredRow(int row)
{
//set the IsRowFiltered flag for a row
int j, jMax;
jMax = iSize[row];
IsRowFiltered[row] = false;
for(j=0;j<jMax;j++)
{
if (AppliedFilter[row][j] != -1)
{
IsRowFiltered[row] = true;
return;
}
}
}
void wxEGrid::ApplyAllFilter(bool bRecompute, bool bListFilteredValue)
{
//apply all filters to each column
int i, iMax;
iMax = GetNumberCols();
for(i=0;i<iMax;i++) ApplyFilter(i, bRecompute, bListFilteredValue);
}
void wxEGrid::OnLabelClick(wxGridEvent& event)
{
//process click on a filter
wxPoint Pos, wPos;
int col, iLeft, iRight, iButton, iRowLabelSize, x, y, w, h, k, kMax;
wxString FilterValue;
wxArrayString sArray;
int iPreviousCol;
Pos = event.GetPosition();
iRowLabelSize = GetRowLabelSize();
CalcScrolledPosition(0,0,&x,&y);
if ((Pos.y > LabelHeight / f)&&(Pos.x > iRowLabelSize))
{
//event occured in the Filter area - therefore we process it
col = event.GetCol();
iLeft = GetColLeft(col);
iRight = GetColRight(col);
iButton = iRight - ArrowWidth - 5; // X coordinate of the Push button
if ((Pos.x - iRowLabelSize - x > iButton) && (Pos.x - iRowLabelSize - x < iRight))
{
if (FilterListCtrl != NULL)
{
iPreviousCol = iCurrentCol;
iCurrentCol = col;
if (FilterTextCtrl != NULL)
{
if (FilterTextCtrl->IsShown())
{
*Values[iPreviousCol] = FilterTextCtrl->GetValue();
ApplyFilter(iPreviousCol);
FilterTextCtrl->Show(false);
}
}
//add the values
FilterListCtrl->Clear();
//fill the ListBox with the String values of the filter
sArray = ListValues(col);
FilterListCtrl->Append("<ALL>");
//FilterListCtrl->Append("<CUSTOM>");
FilterListCtrl->InsertItems(sArray, 2);
wPos = ClientToScreen(wxPoint(0,0));
FilterListCtrl->Refresh(true,NULL);
FilterListCtrl->Update();
FilterListCtrl->GetVirtualSize(&w,&h);
if (w > iRight - iLeft)
{
if (iCurrentCol > 0)
{
iLeft = GetColLeft(iCurrentCol - 1);
}
else
{
iLeft = 0;
}
w = iRight - iLeft;
}
else
{
w = iRight - iLeft;
};
FilterListCtrl->SetSize(iLeft + x + wPos.x + iRowLabelSize, LabelHeight - 4 + wPos.y,
w, 100);
if ((col >= 0) && (col < GetNumberCols())) FilterValue = *Values[col]; else FilterValue = wxEmptyString;
iPreviousCol = iCurrentCol;
iCurrentCol = col;
FilterState = col;
if (FilterValue == wxEmptyString)
{
FilterListCtrl->SetBackgroundColour(FilterBkColourOff);
}
else
{
FilterListCtrl->SetBackgroundColour(FilterBkColourOn);
}
FilterListCtrl->Popup();
ForceRefresh();
Update();
FilterListCtrl->SetFocus();
}
}
else if ((Pos.x - iRowLabelSize - x > iLeft) && (Pos.x - iRowLabelSize - x < iButton))
{
//display the text control
if (FilterTextCtrl != NULL)
{
//(colLeft+2, lh + 2, iColWidth - ArrowWidth - 8,LabelHeight - lh - 4);
//lh = LabelHeight / f
iPreviousCol = iCurrentCol;
iCurrentCol = col;
if (FilterTextCtrl->IsShown())
{
*Values[iPreviousCol] = FilterTextCtrl->GetValue();
ApplyFilter(iPreviousCol);
}
FilterTextCtrl->SetSize(iLeft+3 + x, LabelHeight / f + 2,
iButton - iLeft-3, LabelHeight -LabelHeight / f - 4);
FilterValue = wxEmptyString;
if ((col >= 0) && (col < GetNumberCols())) FilterValue = *Values[col]; else FilterValue = wxEmptyString;
FilterTextCtrl->SetValue(FilterValue);
if (FilterTextCtrl->GetValue() == wxEmptyString)
{
FilterTextCtrl->SetBackgroundColour(FilterBkColourOff);
}
else
{
FilterTextCtrl->SetBackgroundColour(FilterBkColourOn);
}
FilterTextCtrl->Show(true);
FilterTextCtrl->SetFocus();
}
}
else
{
//skip the event to allow parent window to process them when needed
event.Skip();
}
}
else if (Pos.x > iRowLabelSize)
{
//event occured in the label area. Update the sort indicator
col = event.GetCol();
if (iSorting != NULL)
{
if (iSorting[col] == 0)
{
kMax = GetNumberCols();
for(k=0; k<kMax; k++) iSorting[k] = 0;
iSorting[col] = 1;
Sort(WX_EGRID_INSERTION_SORT);
}
else if (iSorting[col] > 0)
{
iSorting[col] = -iSorting[col];
Sort(WX_EGRID_INSERTION_SORT);
}
else
{
iSorting[col] = 0;
}
}
ForceRefresh();
//skip the event to allow parent window to process them when needed
event.Skip();
}
else
{
//skip the event to allow parent window to process them when needed
event.Skip();
}
}
wxWindow* wxEGrid::GetFilterWindow(void)
{
//return a pointer to the filter band
return((wxWindow *) GetColLabelWindow());
}
void wxEGrid::SetRegExFlag(int flag)
{
//Set the RegEx type
iRegExType = flag;
}
int wxEGrid::GetRegExFlag(void)
{
//return the RegEx type
return(iRegExType);
}
void wxEGrid::SetCurrentFilterValue(wxString value)
{
//Set the value of the current filter
if ((iCurrentCol >= 0) && (iCurrentCol < GetNumberCols()))
{
*Values[iCurrentCol] = value;
}
}
/** VISUAL ATTRIBUTE API **/
void wxEGrid::GetFilterArrowDim(int *width, int *height)
{
//get the filter arrow Dimension
*width = ArrowWidth;
*height = ArrowHeight;
}
void wxEGrid::SetFilterArrowDim(int width, int height)
{
//set the filter arrow Dimension
if (width > 0) ArrowWidth = width;
if (height > 0) ArrowHeight = height;
Refresh(true,NULL);
Update();
}
wxColour wxEGrid::GetFilterArrowColourOn(void)
{
//get the filter arrow color ON
return(FilterArrowColourOn);
}
void wxEGrid::SetFilterArrowColourOn(wxColour colour)
{
//set the filter arrow color ON
FilterArrowColourOn = colour;
}
wxColour wxEGrid::GetFilterArrowColourOff(void)
{
//get the filter arrow color OFF
return(FilterArrowColourOff);
}
void wxEGrid::SetFilterArrowColourOff(wxColour colour)
{
//set the filter arrow color OFF
FilterArrowColourOff = colour;
}
wxColour wxEGrid::GetFilterBkColourOn(void)
{
//get the filter background color ON
return(FilterBkColourOn);
}
void wxEGrid::SetFilterBkColourOn(wxColour colour)
{
//set the filter arrow color ON
FilterBkColourOn = colour;
}
wxColour wxEGrid::GetFilterBkColourOff(void)
{
//get the filter background color ON
return(FilterBkColourOff);
}
void wxEGrid::SetFilterBkColourOff(wxColour colour)
{
//set the filter arrow color OFF
FilterBkColourOff = colour;
}
wxColour wxEGrid::GetSortingArrowColour(void)
{
//get the sorting arrow color
return(SortArrowColor);
}
void wxEGrid::SetSortingArrowColour(wxColour colour)
{
//set the sorting arrow color OFF
SortArrowColor = colour;
}
/** Sorting API ***/
bool wxEGrid::IsSortingBy(int col) const
{
//return true if the column sorts
if (iSorting == NULL) return(false);
if (col < 0) return(false);
if (col >= iSortSize) return(false);
if (iSorting[col] == 0) return(false);
return(true);
}
int wxEGrid::GetSortingColumn(void) const
{
//return the 1st column used for sorting
int i;
if (iSorting == NULL) return(-1);
for(i=0;i<iSortSize;i++)
{
if ((iSorting[i] == 1)||(iSorting[i] == -1)) return(i);
}
return(0);
}
bool wxEGrid::IsSortOrderAscending(int col) const
{
//get the sort order of the column
if (iSorting == NULL) return(false);
if (col < 0) return(false);
if (col >= iSortSize) return(false);
if (iSorting[col] > 0) return(true);
return(false);
}
void wxEGrid::SetSortingColumn(int col, bool ascending, int key)
{
//set the sorting order of the column
//col : the column number used for sorting
//ascending: true if ascending sorting is used, false otherwise
//key: 1 if used for primary sorting, 2 for secondary,...
int iKey;
if (iSorting == NULL) return;
if (col < 0) return;
if (col >= iSortSize) return;
if (key <0) iKey = -key; else iKey = key;
if (ascending) iSorting[col] = iKey; else iSorting[col] = -iKey;
}
void wxEGrid::UnsetSortingColumn(void)
{
//reset the sorting columns
int i;
if (iSorting == NULL) return;
for(i=0; i<iSortSize; i++) iSorting[i] = 0;
}
void wxEGrid::Sort(int algo)
{
//sort the table
InsertionSort();
}
void wxEGrid::SwapRows(int row1, int row2)
{
//invert the content from row1 and row2
int i, iMax;
wxString s;
iMax = GetNumberCols();
for(i=0;i<iMax;i++)
{
s = GetCellValue(row1,i);
SetCellValue(row1,i,GetCellValue(row2,i));
SetCellValue(row2,i,s);
}
}
void wxEGrid::CopyRow(int row1, int row2)
{
//copy row2 in row1
int i, iMax;
iMax = GetNumberCols();
for(i=0;i<iMax;i++)
{
SetCellValue(row1,i,GetCellValue(row2,i));
}
}
void wxEGrid::InsertionSort()
{
//sort the table
int col, row;
int i, j;
wxString s, s2;
bool bAscending;
BeginBatch();
col = GetSortingColumn();
bAscending = true;
if (iSorting == NULL) return;
if (iSorting[col] < 0) bAscending = false;
row = GetNumberRows();
AppendRows(1);
if (bAscending)
{
for (i=1; i < row; i++)
{
s = GetCellValue(i,col);
CopyRow(row,i);
j = i;
s2 = GetCellValue(j-1,col);
while ((j > 0) && (s2.Cmp(s)>0))
{
CopyRow(j,j-1);
j = j - 1;
}
CopyRow(j,row);
}
}
else
{
for (i=1; i < row; i++)
{
s = GetCellValue(i,col);
CopyRow(row,i);
j = i;
s2 = GetCellValue(j-1,col);
while ((j > 0) && (s.Cmp(s2)>0))
{
CopyRow(j,j-1);
j = j - 1;
}
CopyRow(j,row);
}
}
DeleteRows(row,1);
EndBatch();
ForceRefresh();
}
Re: wxGrid class extended
Hi,seb_seb0 wrote: My questions are:
- are there plans to support sorting and/or filtering in wxWidgets ?
...
- I have found a small bug in wxGridWindow class. The mouse capture is sometimes not released (try selecting some cells while scrolling to the top or the bottom). Is it a known bug ? It could be solved in a few lines I guess (processing a mouse button left up event). I will post a solution later on the bug tracking list.
since this is mostly a user forum, such questions about the development of wx may not be answered; for such matters the mailing list usually gets you better answers
BTW: thanks for the nice contrib
-
- I live to help wx-kind
- Posts: 178
- Joined: Mon Jul 23, 2007 9:01 am
Hello,
i have tested the wxEgrid class on Ubuntu Linux with wxGTK-2.8.9 (svn-head). It made some little adjustment, because g++ raised a lots of warnings. These are fixed now.
I have attached a linux makefile an the modified source.
Best regards
Orbitcowboy
i have tested the wxEgrid class on Ubuntu Linux with wxGTK-2.8.9 (svn-head). It made some little adjustment, because g++ raised a lots of warnings. These are fixed now.
I have attached a linux makefile an the modified source.
Best regards
Orbitcowboy
- Attachments
-
- wxEGrid.tar.gz
- tar -xpvzf wxEGrid.tar.gz
cd wxEGrid/make
make depend
make
../bin/GridDemo - (26.75 KiB) Downloaded 523 times
OS: Ubuntu 9.04 (32/64-Bit), Debian Lenny (32-Bit)
Compiler: gcc/g++-4.3.3 , gcc/g++-4.4.0
wxWidgets: 2.8.10,2.9.0
Compiler: gcc/g++-4.3.3 , gcc/g++-4.4.0
wxWidgets: 2.8.10,2.9.0
Re: wxGrid class extended
I have few query:-
1) How to Incorporate these 2 file (wxEgrid.cpp, wxEgrid.h) in the source code ( I am new to python and wxpython).
1) How to Incorporate these 2 file (wxEgrid.cpp, wxEgrid.h) in the source code ( I am new to python and wxpython).