wxGrid class extended

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
seb_seb0
In need of some credit
In need of some credit
Posts: 8
Joined: Sun Mar 29, 2009 11:33 am

wxGrid class extended

Post by seb_seb0 »

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
Last edited by seb_seb0 on Mon Mar 30, 2009 8:27 pm, edited 2 times in total.
seb_seb0
In need of some credit
In need of some credit
Posts: 8
Joined: Sun Mar 29, 2009 11:33 am

Post by seb_seb0 »

Here is the code. There are 2 files (wxEGrid.h and wxEGrid.cpp)

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
seb_seb0
In need of some credit
In need of some credit
Posts: 8
Joined: Sun Mar 29, 2009 11:33 am

Post by seb_seb0 »

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();

}
Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am

Re: wxGrid class extended

Post by Auria »

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.
Hi,

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 :)
orbitcowboy
I live to help wx-kind
I live to help wx-kind
Posts: 178
Joined: Mon Jul 23, 2007 9:01 am

Post by orbitcowboy »

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
You do not have the required permissions to view the files attached to this post.
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
Ankit
In need of some credit
In need of some credit
Posts: 1
Joined: Thu Sep 19, 2013 7:19 am

Re: wxGrid class extended

Post by Ankit »

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).