diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:56:14 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:56:14 +0200 |
commit | 1046c195a9bbac24678c06310a4dd56b10347244 (patch) | |
tree | 89ad9f6fe3e538d65ef973b628ed9284b6c99e9f /library/CustomGrid.cpp | |
parent | 1.14 (diff) | |
download | FreeFileSync-1046c195a9bbac24678c06310a4dd56b10347244.tar.gz FreeFileSync-1046c195a9bbac24678c06310a4dd56b10347244.tar.bz2 FreeFileSync-1046c195a9bbac24678c06310a4dd56b10347244.zip |
1.15
Diffstat (limited to 'library/CustomGrid.cpp')
-rw-r--r-- | library/CustomGrid.cpp | 879 |
1 files changed, 616 insertions, 263 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp index 25ceb537..95300a2b 100644 --- a/library/CustomGrid.cpp +++ b/library/CustomGrid.cpp @@ -3,24 +3,28 @@ #include "resources.h" #include <wx/dc.h> #include "../algorithm.h" +#include "resources.h" + +using namespace xmlAccess; + -const unsigned int MinimumRows = 15; +const unsigned int MIN_ROW_COUNT = 15; //class containing pure grid data: basically the same as wxGridStringTable, but adds cell formatting -class CustomGridTableBase : public wxGridStringTable +class CustomGridTable : public wxGridTableBase { public: - CustomGridTableBase(int numRows, int numCols) : - wxGridStringTable(numRows, numCols), + CustomGridTable() : + wxGridTableBase(), lightBlue(80, 110, 255), lightGrey(212, 208, 200), - gridIdentifier(0), - gridRefUI(0), - gridData(0), - lastNrRows(MinimumRows) {} + gridRefUI(NULL), + gridData(NULL), + lastNrRows(0), + lastNrCols(0) {} - ~CustomGridTableBase() {} + virtual ~CustomGridTable() {} void setGridDataTable(GridView* gridRefUI, FileCompareResult* gridData) @@ -30,182 +34,56 @@ public: } - void SetGridIdentifier(int id) - { - gridIdentifier = id; - } - //########################################################################### //grid standard input output methods, redirected directly to gridData to improve performance virtual int GetNumberRows() { if (gridRefUI) - return max(gridRefUI->size(), MinimumRows); + return std::max(gridRefUI->size(), MIN_ROW_COUNT); else - return MinimumRows; //grid is initialized with this number of rows + return 0; //grid is initialized with zero number of rows } - virtual bool IsEmptyCell( int row, int col ) + virtual int GetNumberCols() //virtual used by middle grid! { - return (GetValue(row, col) == wxEmptyString); + return columnPositions.size(); } - wxString evaluateCmpResult(const CompareFilesResult result, const bool selectedForSynchronization) + virtual bool IsEmptyCell( int row, int col ) { - if (selectedForSynchronization) - switch (result) - { - case FILE_LEFT_SIDE_ONLY: - return wxT("<|"); - break; - case FILE_RIGHT_SIDE_ONLY: - return wxT("|>"); - break; - case FILE_RIGHT_NEWER: - return wxT(">>"); - break; - case FILE_LEFT_NEWER: - return wxT("<<"); - break; - case FILE_DIFFERENT: - return wxT("!="); - break; - case FILE_EQUAL: - return wxT("=="); - break; - default: - assert (false); - return wxEmptyString; - } - else return wxT("(-)"); + return (GetValue(row, col) == wxEmptyString); } - virtual wxString GetValue(int row, int col) - { - if (gridRefUI) - { - if (unsigned(row) < gridRefUI->size()) - { - const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; - wxString fileSize; //tmp string - - switch (gridIdentifier) - { - case 1: - if (col < 4) - { - if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - { - switch (col) - { - case 0: //filename - return wxEmptyString; - case 1: //relative path - return gridLine.fileDescrLeft.relativeName; - case 2: //file size - return _("<Directory>"); - case 3: //date - return wxEmptyString; - } - } - else if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_FILE) - { - switch (col) - { - case 0: //filename - return gridLine.fileDescrLeft.relativeName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR); - case 1: //relative path - return gridLine.fileDescrLeft.relativeName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); - case 2: //file size - return globalFunctions::includeNumberSeparator(fileSize = gridLine.fileDescrLeft.fileSize.ToString()); - case 3: //date - return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrLeft.lastWriteTimeRaw); - } - } - } - else - assert (false); - - break; - - case 2: - if (col < 4) - { - if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - { - switch (col) - { - case 0: //filename - return wxEmptyString; - case 1: //relative path - return gridLine.fileDescrRight.relativeName; - case 2: //file size - return _("<Directory>"); - case 3: //date - return wxEmptyString; - } - } - else if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE) - { - switch (col) - { - case 0: //filename - return gridLine.fileDescrRight.relativeName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR); - case 1: //relative path - return gridLine.fileDescrRight.relativeName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); - case 2: //file size - return globalFunctions::includeNumberSeparator(fileSize = gridLine.fileDescrRight.fileSize.ToString()); - case 3: //date - return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrRight.lastWriteTimeRaw); - } - } - } - else - assert (false); - - break; + virtual wxString GetValue(int row, int col) = 0; - case 3: - if (col < 1) - return evaluateCmpResult(gridLine.cmpResult, gridLine.selectedForSynchronization); - else - assert (false); - break; - default: - assert (false); - break; - } - } - } - //if data is not found: - return wxEmptyString; - } - - - virtual void SetValue( int row, int col, const wxString& value ) + virtual void SetValue(int row, int col, const wxString& value) { assert (false); //should not be used, since values are retrieved directly from gridRefUI } + virtual void Clear() { assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} } - virtual bool InsertRows( size_t pos = 0, size_t numRows = 1 ) + + virtual bool InsertRows(size_t pos = 0, size_t numRows = 1) { assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} return true; } - virtual bool AppendRows( size_t numRows = 1 ) + + virtual bool AppendRows(size_t numRows = 1) { assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} return true; } - virtual bool DeleteRows( size_t pos = 0, size_t numRows = 1 ) + + virtual bool DeleteRows(size_t pos = 0, size_t numRows = 1) { assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} return true; @@ -216,7 +94,7 @@ public: { if (gridRefUI) { - int currentNrRows = GetNumberRows(); + const int currentNrRows = GetNumberRows(); if (lastNrRows < currentNrRows) { @@ -243,79 +121,326 @@ public: } lastNrRows = currentNrRows; } - } -//########################################################################### - enum RowColor {BLUE, GREY, NONE}; + const int currentNrCols = GetNumberCols(); - inline //redundant command - RowColor getRowColor(int row) //rows that are filtered out are shown in different color - { - if (gridRefUI) + if (lastNrCols < currentNrCols) { - if (unsigned(row) < gridRefUI->size()) + if (GetView()) { - const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; - - //mark filtered rows - if (!cmpLine.selectedForSynchronization) - return BLUE; - //mark directories - else if (gridIdentifier == 1 && cmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - return GREY; - else if (gridIdentifier == 2 && cmpLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - return GREY; - else - return NONE; + wxGridTableMessage msg(this, + wxGRIDTABLE_NOTIFY_COLS_APPENDED, + currentNrCols - lastNrCols); + + GetView()->ProcessTableMessage( msg ); } } - return NONE; + else if (lastNrCols > currentNrCols) + { + if (GetView()) + { + wxGridTableMessage msg(this, + wxGRIDTABLE_NOTIFY_COLS_DELETED, + 0, + lastNrCols - currentNrCols); + + GetView()->ProcessTableMessage( msg ); + } + } + lastNrCols = currentNrCols; } +//########################################################################### - wxGridCellAttr* GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) + virtual wxString GetColLabelValue( int col ) { - wxGridCellAttr* result = wxGridTableBase::GetAttr(row, col, kind); + return CustomGrid::getTypeName(getTypeAtPos(col)); + } + + + virtual wxGridCellAttr* GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) + { + const wxColour& color = getRowColor(row); - // MARK FILTERED ROWS: + //add color to some rows + wxGridCellAttr* result = wxGridTableBase::GetAttr(row, col, kind); if (result) - { //if kind is not a cell or row attribute, we have to clone the cell attribute, since we don't want to change e.g. all column attribs - if (result->GetKind() != wxGridCellAttr::Cell && result->GetKind() != wxGridCellAttr::Row) + { + if (result->GetBackgroundColour() == color) { - wxGridCellAttr* attr = result->Clone(); + return result; + } + else //grid attribute might be referenced by other nodes, so clone it! + { + wxGridCellAttr* attr = result->Clone(); //attr has ref-count 1 result->DecRef(); result = attr; } - - RowColor color = getRowColor(row); - if (color == BLUE) - result->SetBackgroundColour(lightBlue); - else if (color == GREY) - result->SetBackgroundColour(lightGrey); - else - result->SetBackgroundColour(*wxWHITE); } else - { - result = new wxGridCellAttr; + result = new wxGridCellAttr; //created with ref-count 1 - RowColor color = getRowColor(row); - if (color == BLUE) - result->SetBackgroundColour(lightBlue); - else if (color == GREY) - result->SetBackgroundColour(lightGrey); - } + result->SetBackgroundColour(color); return result; } -private: + + void setupColumns(const std::vector<xmlAccess::XmlGlobalSettings::ColumnTypes>& positions) + { + columnPositions = positions; + updateGridSizes(); //add or remove columns + } + + + XmlGlobalSettings::ColumnTypes getTypeAtPos(unsigned pos) const + { + if (pos < columnPositions.size()) + return columnPositions[pos]; + else + return XmlGlobalSettings::ColumnTypes(1000); + } + + +protected: + virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color + + std::vector<xmlAccess::XmlGlobalSettings::ColumnTypes> columnPositions; + wxColour lightBlue; wxColour lightGrey; - int gridIdentifier; GridView* gridRefUI; //(very fast) access to underlying grid data :) FileCompareResult* gridData; int lastNrRows; + int lastNrCols; +}; + + +class CustomGridTableLeft : public CustomGridTable +{ +public: + CustomGridTableLeft() : CustomGridTable() {} + ~CustomGridTableLeft() {} + + virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + { + if (gridRefUI && unsigned(row) < gridRefUI->size()) + { + const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; + + //mark filtered rows + if (!cmpLine.selectedForSynchronization) + return lightBlue; + //mark directories + else if (cmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + return lightGrey; + else + return *wxWHITE; + } + return *wxWHITE; + } + + + //virtual impl. + wxString GetValue(int row, int col) + { + if (gridRefUI) + { + if (unsigned(row) < gridRefUI->size()) + { + const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; + + if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + { + switch (getTypeAtPos(col)) + { + case XmlGlobalSettings::FILENAME: //filename + return wxEmptyString; + case XmlGlobalSettings::REL_PATH: //relative path + return gridLine.fileDescrLeft.relativeName.c_str(); + case XmlGlobalSettings::SIZE: //file size + return _("<Directory>"); + case XmlGlobalSettings::DATE: //date + return wxEmptyString; + } + } + else if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + { + switch (getTypeAtPos(col)) + { + case XmlGlobalSettings::FILENAME: //filename + return gridLine.fileDescrLeft.relativeName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); + case XmlGlobalSettings::REL_PATH: //relative path + return gridLine.fileDescrLeft.relativeName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); + case XmlGlobalSettings::SIZE: //file size + { + wxString fileSize = gridLine.fileDescrLeft.fileSize.ToString(); //tmp string + return globalFunctions::includeNumberSeparator(fileSize); + } + case XmlGlobalSettings::DATE: //date + return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrLeft.lastWriteTimeRaw); + } + } + } + } + //if data is not found: + return wxEmptyString; + } +}; + + +class CustomGridTableMiddle : public CustomGridTable +{ +public: + CustomGridTableMiddle() : CustomGridTable() + { + lastNrCols = 1; //ensure CustomGridTable::updateGridSizes() is working correctly + } + ~CustomGridTableMiddle() {} + + //virtual impl. + int GetNumberCols() + { + return 1; + } + + + int selectedForSynchronization(const unsigned int row) // 0 == false, 1 == true, -1 == not defined + { + if (gridRefUI && row < gridRefUI->size()) + { + const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; + + if (cmpLine.selectedForSynchronization) + return 1; + else + return 0; + } + return -1; + } + + + //virtual impl. + const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + { + if (gridRefUI && unsigned(row) < gridRefUI->size()) + { + const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; + + //mark filtered rows + if (!cmpLine.selectedForSynchronization) + return lightBlue; + else + return *wxWHITE; + } + return *wxWHITE; + } + + //virtual impl. + wxString GetValue(int row, int col) + { + if (gridRefUI) + { + if (unsigned(row) < gridRefUI->size()) + { + const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; + + switch (gridLine.cmpResult) + { + case FILE_LEFT_SIDE_ONLY: + return wxT("<|"); + case FILE_RIGHT_SIDE_ONLY: + return wxT("|>"); + case FILE_RIGHT_NEWER: + return wxT(">>"); + case FILE_LEFT_NEWER: + return wxT("<<"); + case FILE_DIFFERENT: + return wxT("!="); + case FILE_EQUAL: + return wxT("=="); + default: + assert (false); + return wxEmptyString; + } + } + } + //if data is not found: + return wxEmptyString; + } +}; + + +class CustomGridTableRight : public CustomGridTable +{ +public: + CustomGridTableRight() : CustomGridTable() {} + ~CustomGridTableRight() {} + + //virtual impl. + const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + { + if (gridRefUI && unsigned(row) < gridRefUI->size()) + { + const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; + + //mark filtered rows + if (!cmpLine.selectedForSynchronization) + return lightBlue; + //mark directories + else if (cmpLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + return lightGrey; + else + return *wxWHITE; + } + return *wxWHITE; + } + + //virtual impl. + wxString GetValue(int row, int col) + { + if (gridRefUI) + { + if (unsigned(row) < gridRefUI->size()) + { + const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; + + if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + { + switch (getTypeAtPos(col)) + { + case XmlGlobalSettings::FILENAME: //filename + return wxEmptyString; + case XmlGlobalSettings::REL_PATH: //relative path + return gridLine.fileDescrRight.relativeName.c_str(); + case XmlGlobalSettings::SIZE: //file size + return _("<Directory>"); + case XmlGlobalSettings::DATE: //date + return wxEmptyString; + } + } + else if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE) + { + switch (getTypeAtPos(col)) + { + case XmlGlobalSettings::FILENAME: //filename + return gridLine.fileDescrRight.relativeName.AfterLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); + case XmlGlobalSettings::REL_PATH: //relative path + return gridLine.fileDescrRight.relativeName.BeforeLast(GlobalResources::FILE_NAME_SEPARATOR).c_str(); + case XmlGlobalSettings::SIZE: //file size + { + wxString fileSize = gridLine.fileDescrRight.fileSize.ToString(); //tmp string + return globalFunctions::includeNumberSeparator(fileSize); + } + case XmlGlobalSettings::DATE: //date + return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrRight.lastWriteTimeRaw); + } + } + } + } + //if data is not found: + return wxEmptyString; + } }; @@ -333,26 +458,33 @@ CustomGrid::CustomGrid(wxWindow *parent, m_gridLeft(NULL), m_gridRight(NULL), m_gridMiddle(NULL), gridDataTable(NULL), currentSortColumn(-1), - sortMarker(NULL) {} - - -CustomGrid::~CustomGrid() {} - - -bool CustomGrid::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) + sortMarker(NULL) { - //use custom wxGridTableBase class for management of large sets of formatted data. - //This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default. - - gridDataTable = new CustomGridTableBase(numRows, numCols); - SetTable(gridDataTable, true, selmode); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor - return true; + //set color of selections + wxColour darkBlue(40, 35, 140); + SetSelectionBackground(darkBlue); + SetSelectionForeground(*wxWHITE); } -void CustomGrid::deactivateScrollbars() +void CustomGrid::initSettings(bool enableScrollbars, + CustomGrid* gridLeft, + CustomGrid* gridRight, + CustomGrid* gridMiddle, + GridView* gridRefUI, + FileCompareResult* gridData) { - scrollbarsEnabled = false; + scrollbarsEnabled = enableScrollbars; + + //these grids will scroll together + assert(gridLeft && gridRight && gridMiddle); + m_gridLeft = gridLeft; + m_gridRight = gridRight; + m_gridMiddle = gridMiddle; + + //set underlying grid data + assert(gridDataTable); + gridDataTable->setGridDataTable(gridRefUI, gridData); } @@ -380,7 +512,7 @@ void CustomGrid::adjustGridHeights() //m_gridLeft, m_gridRight, m_gridMiddle are if (y1 != y2 || y2 != y3) { - int yMax = max(y1, max(y2, y3)); + int yMax = std::max(y1, std::max(y2, y3)); if (::leadGrid == m_gridLeft) //do not handle case (y1 == yMax) here!!! Avoid back coupling! m_gridLeft->SetMargins(0, 0); @@ -404,90 +536,311 @@ void CustomGrid::adjustGridHeights() //m_gridLeft, m_gridRight, m_gridMiddle are } -//this method is called when grid view changes: useful for parallel updating of multiple grids -void CustomGrid::DoPrepareDC(wxDC& dc) +void CustomGrid::updateGridSizes() { - wxScrollHelper::DoPrepareDC(dc); + assert(gridDataTable); + gridDataTable->updateGridSizes(); +} - int x, y = 0; - if (this == ::leadGrid) //avoid back coupling - { - if (this == m_gridLeft) + +void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap) +{ + currentSortColumn = sortColumn; + sortMarker = bitmap; +} + + +void CustomGrid::DrawColLabel(wxDC& dc, int col) +{ + wxGrid::DrawColLabel(dc, col); + + if (col == currentSortColumn) + dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border +} + + +void CustomGrid::setColumnAttributes(const xmlAccess::XmlGlobalSettings::ColumnAttributes& attr) +{ + //remove special alignment for column "size" + for (int i = 0; i < GetNumberCols(); ++i) + if (getTypeAtPos(i) == XmlGlobalSettings::SIZE) { - GetViewStart(&x, &y); - m_gridRight->Scroll(x, y); - m_gridMiddle->Scroll(-1, y); //scroll in y-direction only - adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL + wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); + cellAttributes->SetAlignment(wxALIGN_LEFT,wxALIGN_CENTRE); + SetColAttr(i, cellAttributes); + break; } - else if (this == m_gridRight) +//---------------------------------------------------------------------------------- + setSortMarker(-1); //clear sorting marker + + columnSettings.clear(); + if (attr.size() == 0) + { //default settings: + xmlAccess::XmlGlobalSettings::ColumnAttrib newEntry; + newEntry.type = xmlAccess::XmlGlobalSettings::FILENAME; + newEntry.visible = true; + newEntry.position = 0; + newEntry.width = 138; + columnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::XmlGlobalSettings::REL_PATH; + newEntry.position = 1; + newEntry.width = 118; + columnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::XmlGlobalSettings::SIZE; + newEntry.position = 2; + newEntry.width = 67; + columnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::XmlGlobalSettings::DATE; + newEntry.position = 3; + newEntry.width = 113; + columnSettings.push_back(newEntry); + } + else + { + for (unsigned int i = 0; i < COLUMN_TYPE_COUNT; ++i) { - GetViewStart(&x, &y); - m_gridLeft->Scroll(x, y); - m_gridMiddle->Scroll(-1, y); - adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL + XmlGlobalSettings::ColumnAttrib newEntry; + + if (i < attr.size()) + newEntry = attr[i]; + else + { + newEntry.type = xmlAccess::XmlGlobalSettings::FILENAME; + newEntry.visible = true; + newEntry.position = i; + newEntry.width = 100; + } + columnSettings.push_back(newEntry); } - else if (this == m_gridMiddle) + + sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByType); + for (unsigned int i = 0; i < COLUMN_TYPE_COUNT; ++i) //just be sure that each type exists only once + columnSettings[i].type = XmlGlobalSettings::ColumnTypes(i); + + sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionOnly); + for (unsigned int i = 0; i < COLUMN_TYPE_COUNT; ++i) //just be sure that positions are numbered correctly + columnSettings[i].position = i; + + sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); + } + + std::vector<XmlGlobalSettings::ColumnTypes> newPositions; + for (unsigned int i = 0; i < columnSettings.size() && columnSettings[i].visible; ++i) //hidden columns are sorted to the end of vector! + newPositions.push_back(columnSettings[i].type); + + //set column positions + assert(gridDataTable); + gridDataTable->setupColumns(newPositions); + + //set column width (set them after setupColumns!) + for (unsigned int i = 0; i < newPositions.size(); ++i) + SetColSize(i, columnSettings[i].width); + +//-------------------------------------------------------------------------------------------------------- + //set special alignment for column "size" + for (int i = 0; i < GetNumberCols(); ++i) + if (getTypeAtPos(i) == XmlGlobalSettings::SIZE) { - GetViewStart(&x, &y); - m_gridLeft->Scroll(-1, y); - m_gridRight->Scroll(-1, y); - adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL + wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); + cellAttributes->SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE); + SetColAttr(i, cellAttributes); //make filesize right justified on grids + break; } - } + + ClearSelection(); + ForceRefresh(); } -//these classes will scroll together, hence the name ;) -void CustomGrid::setScrollFriends(CustomGrid* gridLeft, CustomGrid* gridRight, CustomGrid* gridMiddle) +xmlAccess::XmlGlobalSettings::ColumnAttributes CustomGrid::getColumnAttributes() { - assert(gridLeft && gridRight && gridMiddle); + sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); - m_gridLeft = gridLeft; - m_gridRight = gridRight; - m_gridMiddle = gridMiddle; + xmlAccess::XmlGlobalSettings::ColumnAttributes output; + xmlAccess::XmlGlobalSettings::ColumnAttrib newEntry; + for (unsigned int i = 0; i < columnSettings.size(); ++i) + { + newEntry = columnSettings[i]; + if (newEntry.visible) //hidden columns are sorted to the end of vector! + newEntry.width = GetColSize(i); + output.push_back(newEntry); + } - assert(gridDataTable); - if (this == m_gridLeft) - gridDataTable->SetGridIdentifier(1); - else if (this == m_gridRight) - gridDataTable->SetGridIdentifier(2); - else if (this == m_gridMiddle) - gridDataTable->SetGridIdentifier(3); - else - assert (false); + return output; } -void CustomGrid::setGridDataTable(GridView* gridRefUI, FileCompareResult* gridData) -{ //set underlying grid data +XmlGlobalSettings::ColumnTypes CustomGrid::getTypeAtPos(unsigned pos) const +{ assert(gridDataTable); - gridDataTable->setGridDataTable(gridRefUI, gridData); + return gridDataTable->getTypeAtPos(pos); } -void CustomGrid::updateGridSizes() +wxString CustomGrid::getTypeName(XmlGlobalSettings::ColumnTypes colType) { - assert(gridDataTable); - gridDataTable->updateGridSizes(); + switch (colType) + { + case XmlGlobalSettings::FILENAME: + return _("Filename"); + case XmlGlobalSettings::REL_PATH: + return _("Relative path"); + case XmlGlobalSettings::SIZE: + return _("Size"); + case XmlGlobalSettings::DATE: + return _("Date"); + default: + return wxEmptyString; + } } +//############################################################################################ +//CustomGrid specializations -void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap) +CustomGridLeft::CustomGridLeft(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) : + CustomGrid(parent, id, pos, size, style, name) {} + + +bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) { - currentSortColumn = sortColumn; - sortMarker = bitmap; + //use custom wxGridTableBase class for management of large sets of formatted data. + //This is done in CreateGrid instead of SetTable method since source code is generated and wxFormbuilder invokes CreatedGrid by default. + gridDataTable = new CustomGridTableLeft(); + SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor + return true; } -void CustomGrid::DrawColLabel(wxDC& dc, int col) +//this method is called when grid view changes: useful for parallel updating of multiple grids +void CustomGridLeft::DoPrepareDC(wxDC& dc) { - assert(0 <= col && col < 4); + wxScrollHelper::DoPrepareDC(dc); - wxGrid::DrawColLabel(dc, col); + int x, y = 0; + if (this == ::leadGrid) //avoid back coupling + { + GetViewStart(&x, &y); + m_gridMiddle->Scroll(-1, y); //scroll in y-direction only + m_gridRight->Scroll(x, y); + adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL + } +} - if (col == currentSortColumn) + +//---------------------------------------------------------------------------------------- +CustomGridMiddle::CustomGridMiddle(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) : + CustomGrid(parent, id, pos, size, style, name) {} + + +bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) +{ + CustomGridTableMiddle* newTable = new CustomGridTableMiddle(); + gridDataTable = newTable; + SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor + + //display checkboxes (representing bool values) if row is enabled for synchronization + SetDefaultRenderer(new GridCellRendererAddCheckbox(newTable)); //SetDefaultRenderer takes ownership! + + return true; +} + + +//this method is called when grid view changes: useful for parallel updating of multiple grids +void CustomGridMiddle::DoPrepareDC(wxDC& dc) +{ + wxScrollHelper::DoPrepareDC(dc); + + int x, y = 0; + if (this == ::leadGrid) //avoid back coupling { - dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border + GetViewStart(&x, &y); + m_gridLeft->Scroll(-1, y); + m_gridRight->Scroll(-1, y); + adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL } } + +void CustomGridMiddle::GridCellRendererAddCheckbox::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rect, + int row, int col, + bool isSelected) +{ + const int selected = m_gridDataTable->selectedForSynchronization(row); + if (selected < 0) //no valid row + { + wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); + return; + } + + const int shift = std::min(11 + 3, rect.GetWidth()); //11 is width of checkbox image + + wxRect rectShrinked(rect); + + //clean first block of rect that will receive image of checkbox + rectShrinked.SetWidth(shift); + dc.SetPen(*wxWHITE_PEN); + dc.SetBrush(*wxWHITE_BRUSH); + dc.DrawRectangle(rectShrinked); + + //print image into first block + rectShrinked.SetX(1); + if (selected > 0) + dc.DrawLabel(wxEmptyString, *globalResource.bitmapCheckBoxTrue, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + else //if (selected == 0) -> redundant + dc.DrawLabel(wxEmptyString, *globalResource.bitmapCheckBoxFalse, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + + //print second block (default): display compare result + rectShrinked.SetWidth(rect.GetWidth() - shift); + rectShrinked.SetX(shift); + wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); +} + + +//---------------------------------------------------------------------------------------- +CustomGridRight::CustomGridRight(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) : + CustomGrid(parent, id, pos, size, style, name) {} + + +bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) +{ + gridDataTable = new CustomGridTableRight(); + SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor + return true; +} + + +//this method is called when grid view changes: useful for parallel updating of multiple grids +void CustomGridRight::DoPrepareDC(wxDC& dc) +{ + wxScrollHelper::DoPrepareDC(dc); + + int x, y = 0; + if (this == ::leadGrid) //avoid back coupling + { + GetViewStart(&x, &y); + m_gridLeft->Scroll(x, y); + m_gridMiddle->Scroll(-1, y); + adjustGridHeights(); //keep here to ensure m_gridLeft, m_gridRight, m_gridMiddle != NULL + } +} |