diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:57:45 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 16:57:45 +0200 |
commit | 2a3ebac62eb6dd88122c0f447ea90ce368373d3a (patch) | |
tree | fae5c18deaecfb6f39d4d66dd3de8ce730b2025b /library/CustomGrid.cpp | |
parent | 1.17 (diff) | |
download | FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.gz FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.tar.bz2 FreeFileSync-2a3ebac62eb6dd88122c0f447ea90ce368373d3a.zip |
1.18
Diffstat (limited to 'library/CustomGrid.cpp')
-rw-r--r-- | library/CustomGrid.cpp | 1006 |
1 files changed, 511 insertions, 495 deletions
diff --git a/library/CustomGrid.cpp b/library/CustomGrid.cpp index b8737343..faa75b2d 100644 --- a/library/CustomGrid.cpp +++ b/library/CustomGrid.cpp @@ -5,39 +5,58 @@ #include "../algorithm.h" #include "resources.h" #include <typeinfo> +#include "../ui/gridView.h" #ifdef FFS_WIN #include <wx/icon.h> #include <wx/msw/wrapwin.h> //includes "windows.h" -#endif // FFS_WIN + +#elif defined FFS_LINUX +#include <gtk/gtk.h> +#endif + + +using namespace FreeFileSync; const unsigned int MIN_ROW_COUNT = 15; //class containing pure grid data: basically the same as wxGridStringTable, but adds cell formatting + +/* +class hierarchy: + CustomGridTable + /|\ + ________________|________________ + | | + CustomGridTableRim | + /|\ | + __________|__________ | + | | | +CustomGridTableLeft CustomGridTableRight CustomGridTableMiddle +*/ + class CustomGridTable : public wxGridTableBase { public: - CustomGridTable() : + CustomGridTable(int initialRows = 0, int initialCols = 0) : //note: initialRows/initialCols MUST match with GetNumberRows()/GetNumberCols() at initialization!!! wxGridTableBase(), - blue(80, 110, 255), - grey(212, 208, 200), - lightRed(235, 57, 61), - lightBlue(63, 206, 233), - lightGreen(54, 218, 2), - gridRefUI(NULL), - gridData(NULL), - lastNrRows(0), - lastNrCols(0) {} + COLOR_BLUE( 80, 110, 255), + COLOR_GREY( 212, 208, 200), + COLOR_LIGHT_RED( 235, 57, 61), + COLOR_LIGHT_BLUE( 63, 206, 233), + COLOR_LIGHT_GREEN(54, 218, 2), + gridDataView(NULL), + lastNrRows(initialRows), + lastNrCols(initialCols) {} virtual ~CustomGridTable() {} - void setGridDataTable(GridView* gridRefUI, FileCompareResult* gridData) + void setGridDataTable(GridView* gridDataView) { - this->gridRefUI = gridRefUI; - this->gridData = gridData; + this->gridDataView = gridDataView; } @@ -46,57 +65,27 @@ public: virtual int GetNumberRows() { - if (gridRefUI) - return std::max(gridRefUI->size(), MIN_ROW_COUNT); + if (gridDataView) + return std::max(gridDataView->elementsOnView(), MIN_ROW_COUNT); else return 0; //grid is initialized with zero number of rows } - virtual int GetNumberCols() //virtual used by middle grid! - { - return columnPositions.size(); - } - - virtual bool IsEmptyCell( int row, int col ) { - return (GetValue(row, col) == wxEmptyString); - } - - - virtual wxString GetValue(int row, int col) = 0; - - - 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} - } + return false; //avoid overlapping cells - 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; + //return (GetValue(row, col) == wxEmptyString); } - 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 void SetValue(int row, int col, const wxString& value) { - assert (false); // we don't want to use this, since the visible grid is directly connected to gridRefUI} - return true; + assert (false); //should not be used, since values are retrieved directly from gridDataView } - //update dimensions of grid: no need for InsertRows, AppendRows, DeleteRows anymore!!! + //update dimensions of grid: no need for InsertRows(), AppendRows(), DeleteRows() anymore!!! void updateGridSizes() { const int currentNrRows = GetNumberRows(); @@ -156,12 +145,6 @@ public: //########################################################################### - virtual wxString GetColLabelValue( int col ) - { - return CustomGrid::getTypeName(getTypeAtPos(col)); - } - - virtual wxGridCellAttr* GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) { const wxColour& color = getRowColor(row); @@ -190,6 +173,46 @@ public: } + const FileCompareLine* getRawData(const unsigned int row) const + { + if (gridDataView && row < gridDataView->elementsOnView()) + { + return &(*gridDataView)[row]; + } + return NULL; + } + +protected: + const wxColour COLOR_BLUE; + const wxColour COLOR_GREY; + const wxColour COLOR_LIGHT_RED; + const wxColour COLOR_LIGHT_BLUE; + const wxColour COLOR_LIGHT_GREEN; + + const GridView* gridDataView; //(very fast) access to underlying grid data :) + +private: + virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color + + int lastNrRows; + int lastNrCols; +}; + + +class CustomGridTableRim : public CustomGridTable +{ +public: + virtual int GetNumberCols() + { + return columnPositions.size(); + } + + virtual wxString GetColLabelValue( int col ) + { + return CustomGridRim::getTypeName(getTypeAtPos(col)); + } + + void setupColumns(const std::vector<xmlAccess::ColumnTypes>& positions) { columnPositions = positions; @@ -206,269 +229,225 @@ public: } - const FileCompareLine* getRawData(const unsigned int row) - { - if (gridRefUI && row < gridRefUI->size()) - { - const FileCompareLine& cmpLine = (*gridData)[(*gridRefUI)[row]]; - return &cmpLine; - } - return NULL; - } - - -protected: - virtual const wxColour& getRowColor(int row) = 0; //rows that are filtered out are shown in different color - +private: std::vector<xmlAccess::ColumnTypes> columnPositions; - - wxColour blue; - wxColour grey; - wxColour lightRed; - wxColour lightBlue; - wxColour lightGreen; - GridView* gridRefUI; //(very fast) access to underlying grid data :) - FileCompareResult* gridData; - int lastNrRows; - int lastNrCols; }; -class CustomGridTableLeft : public CustomGridTable +class CustomGridTableLeft : public CustomGridTableRim { public: - CustomGridTableLeft() : CustomGridTable() {} - ~CustomGridTableLeft() {} + virtual wxString GetValue(int row, int col) + { + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) + { + if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + { + switch (getTypeAtPos(col)) + { + case xmlAccess::FULL_NAME: + return gridLine->fileDescrLeft.fullName.c_str(); + case xmlAccess::FILENAME: + return wxEmptyString; + case xmlAccess::REL_PATH: + return gridLine->fileDescrLeft.relativeName.c_str(); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).leftDirectory.c_str(); + case xmlAccess::SIZE: //file size + return _("<Directory>"); + case xmlAccess::DATE: //date + return wxEmptyString; + } + } + else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + { + switch (getTypeAtPos(col)) + { + case xmlAccess::FULL_NAME: + return gridLine->fileDescrLeft.fullName.c_str(); + case xmlAccess::FILENAME: //filename + return wxString(gridLine->fileDescrLeft.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::REL_PATH: //relative path + return wxString(gridLine->fileDescrLeft.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).leftDirectory.c_str(); + case xmlAccess::SIZE: //file size + return globalFunctions::includeNumberSeparator(gridLine->fileDescrLeft.fileSize.ToString()); + case xmlAccess::DATE: //date + return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrLeft.lastWriteTimeRaw); + } + } + } + //if data is not found: + return wxEmptyString; + } +private: virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color { - if (gridRefUI && unsigned(row) < gridRefUI->size()) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; - //mark filtered rows - if (!cmpLine.selectedForSynchronization) - return blue; + if (!gridLine->selectedForSynchronization) + return COLOR_BLUE; //mark directories - else if (cmpLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - return grey; + else if (gridLine->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + return COLOR_GREY; else return *wxWHITE; } return *wxWHITE; } +}; - //virtual impl. - wxString GetValue(int row, int col) +class CustomGridTableRight : public CustomGridTableRim +{ +public: + virtual wxString GetValue(int row, int col) { - if (gridRefUI) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - if (unsigned(row) < gridRefUI->size()) + if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) { - const FileCompareLine& gridLine = (*gridData)[(*gridRefUI)[row]]; - - if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) + switch (getTypeAtPos(col)) { - switch (getTypeAtPos(col)) - { - case xmlAccess::FULL_NAME: - return gridLine.fileDescrLeft.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxEmptyString; - case xmlAccess::REL_PATH: //relative path - return gridLine.fileDescrLeft.relativeName.c_str(); - case xmlAccess::SIZE: //file size - return _("<Directory>"); - case xmlAccess::DATE: //date - return wxEmptyString; - } + case xmlAccess::FULL_NAME: + return gridLine->fileDescrRight.fullName.c_str(); + case xmlAccess::FILENAME: //filename + return wxEmptyString; + case xmlAccess::REL_PATH: //relative path + return gridLine->fileDescrRight.relativeName.c_str(); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).rightDirectory.c_str(); + case xmlAccess::SIZE: //file size + return _("<Directory>"); + case xmlAccess::DATE: //date + return wxEmptyString; } - else if (gridLine.fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + } + else if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_FILE) + { + switch (getTypeAtPos(col)) { - switch (getTypeAtPos(col)) - { - case xmlAccess::FULL_NAME: - return gridLine.fileDescrLeft.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxString(gridLine.fileDescrLeft.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::REL_PATH: //relative path - return wxString(gridLine.fileDescrLeft.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::SIZE: //file size - { - wxString fileSize = gridLine.fileDescrLeft.fileSize.ToString(); //tmp string - return globalFunctions::includeNumberSeparator(fileSize); - } - case xmlAccess::DATE: //date - return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrLeft.lastWriteTimeRaw); - } + case xmlAccess::FULL_NAME: + return gridLine->fileDescrRight.fullName.c_str(); + case xmlAccess::FILENAME: //filename + return wxString(gridLine->fileDescrRight.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::REL_PATH: //relative path + return wxString(gridLine->fileDescrRight.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); + case xmlAccess::DIRECTORY: + return gridDataView->getFolderPair(row).rightDirectory.c_str(); + case xmlAccess::SIZE: //file size + return globalFunctions::includeNumberSeparator(gridLine->fileDescrRight.fileSize.ToString()); + case xmlAccess::DATE: //date + return FreeFileSync::utcTimeToLocalString(gridLine->fileDescrRight.lastWriteTimeRaw); } } } //if data is not found: return wxEmptyString; } + +private: + virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + { + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) + { + //mark filtered rows + if (!gridLine->selectedForSynchronization) + return COLOR_BLUE; + //mark directories + else if (gridLine->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) + return COLOR_GREY; + else + return *wxWHITE; + } + return *wxWHITE; + } }; class CustomGridTableMiddle : public CustomGridTable { public: - CustomGridTableMiddle() : CustomGridTable() - { - lastNrCols = 1; //ensure CustomGridTable::updateGridSizes() is working correctly - } +//middle grid is created (first wxWidgets internal call to GetNumberCols()) with one column + CustomGridTableMiddle() : CustomGridTable(0, GetNumberCols()) {} //attention: static binding to virtual GetNumberCols() in a Constructor! - ~CustomGridTableMiddle() {} - - //virtual impl. - int GetNumberCols() + virtual int GetNumberCols() { return 1; } - - virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color + virtual wxString GetColLabelValue( int col ) { - if (gridRefUI && unsigned(row) < gridRefUI->size()) - { - const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; - - //mark filtered rows - if (!cmpLine.selectedForSynchronization) - return blue; - else - switch (cmpLine.cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - case FILE_RIGHT_SIDE_ONLY: - return lightGreen; - case FILE_LEFT_NEWER: - case FILE_RIGHT_NEWER: - return lightBlue; - case FILE_DIFFERENT: - return lightRed; - default: - return *wxWHITE; - } - } - return *wxWHITE; + return wxEmptyString; } virtual wxString GetValue(int row, int col) { - if (gridRefUI) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - if (unsigned(row) < gridRefUI->size()) + switch (gridLine->cmpResult) { - 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; - } + 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() {} +private: virtual const wxColour& getRowColor(int row) //rows that are filtered out are shown in different color { - if (gridRefUI && unsigned(row) < gridRefUI->size()) + const FileCompareLine* gridLine = getRawData(row); + if (gridLine) { - const FileCompareLine cmpLine = (*gridData)[(*gridRefUI)[row]]; - //mark filtered rows - if (!cmpLine.selectedForSynchronization) - return blue; - //mark directories - else if (cmpLine.fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - return grey; + if (!gridLine->selectedForSynchronization) + return COLOR_BLUE; 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 xmlAccess::FULL_NAME: - return gridLine.fileDescrRight.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxEmptyString; - case xmlAccess::REL_PATH: //relative path - return gridLine.fileDescrRight.relativeName.c_str(); - case xmlAccess::SIZE: //file size - return _("<Directory>"); - case xmlAccess::DATE: //date - return wxEmptyString; - } - } - else if (gridLine.fileDescrRight.objType == FileDescrLine::TYPE_FILE) + switch (gridLine->cmpResult) { - switch (getTypeAtPos(col)) - { - case xmlAccess::FULL_NAME: - return gridLine.fileDescrRight.fullName.c_str(); - case xmlAccess::FILENAME: //filename - return wxString(gridLine.fileDescrRight.relativeName.c_str()).AfterLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::REL_PATH: //relative path - return wxString(gridLine.fileDescrRight.relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); - case xmlAccess::SIZE: //file size - { - wxString fileSize = gridLine.fileDescrRight.fileSize.ToString(); //tmp string - return globalFunctions::includeNumberSeparator(fileSize); - } - case xmlAccess::DATE: //date - return FreeFileSync::utcTimeToLocalString(gridLine.fileDescrRight.lastWriteTimeRaw); - } + case FILE_LEFT_SIDE_ONLY: + case FILE_RIGHT_SIDE_ONLY: + return COLOR_LIGHT_GREEN; + case FILE_LEFT_NEWER: + case FILE_RIGHT_NEWER: + return COLOR_LIGHT_BLUE; + case FILE_DIFFERENT: + return COLOR_LIGHT_RED; + default: + return *wxWHITE; } - } } - //if data is not found: - return wxEmptyString; + return *wxWHITE; } }; - - //######################################################################################################## + CustomGrid::CustomGrid(wxWindow *parent, wxWindowID id, const wxPoint& pos, @@ -476,10 +455,10 @@ CustomGrid::CustomGrid(wxWindow *parent, long style, const wxString& name) : wxGrid(parent, id, pos, size, style, name), - leadGrid(NULL), - scrollbarsEnabled(true), - m_gridLeft(NULL), m_gridMiddle(NULL), m_gridRight(NULL), - gridDataTable(NULL), + m_gridLeft(NULL), + m_gridMiddle(NULL), + m_gridRight(NULL), + isLeading(false), currentSortColumn(-1), sortMarker(NULL) { @@ -500,32 +479,14 @@ CustomGrid::CustomGrid(wxWindow *parent, Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxEventHandler(CustomGrid::onGridAccess), NULL, this); Connect(wxEVT_GRID_LABEL_LEFT_CLICK, wxEventHandler(CustomGrid::onGridAccess), NULL, this); GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxEventHandler(CustomGrid::onGridAccess), NULL, this); + + GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this); } -void CustomGrid::initSettings(const bool enableScrollbars, - const bool showFileIcons, - CustomGrid* gridLeft, - CustomGrid* gridRight, - CustomGrid* gridMiddle, - GridView* gridRefUI, - FileCompareResult* gridData) +bool CustomGrid::isLeadGrid() const { - 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); - - this->initGridRenderer(showFileIcons); - - GetGridWindow()->Connect(wxEVT_ENTER_WINDOW, wxEventHandler(CustomGrid::adjustGridHeights), NULL, this); + return isLeading; } @@ -705,14 +666,17 @@ void additionalGridCommands(wxEvent& event, wxGrid* grid) void CustomGrid::onGridAccess(wxEvent& event) { - if (leadGrid != this) + if (!isLeading) { - leadGrid = this; + isLeading = true; - //notify grids of new user focus - m_gridLeft->leadGrid = this; - m_gridMiddle->leadGrid = this; - m_gridRight->leadGrid = this; + //notify other grids of new user focus + if (m_gridLeft != this) + m_gridLeft->isLeading = false; + if (m_gridMiddle != this) + m_gridMiddle->isLeading = false; + if (m_gridRight != this) + m_gridRight->isLeading = false; wxGrid::SetFocus(); } @@ -728,119 +692,194 @@ void CustomGrid::onGridAccess(wxEvent& event) } -const wxGrid* CustomGrid::getLeadGrid() +//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar +void CustomGrid::adjustGridHeights(wxEvent& event) { - return leadGrid; + if (m_gridLeft && m_gridRight && m_gridMiddle) + { + int y1 = 0; + int y2 = 0; + int y3 = 0; + int dummy = 0; + + m_gridLeft->GetViewStart(&dummy, &y1); + m_gridRight->GetViewStart(&dummy, &y2); + m_gridMiddle->GetViewStart(&dummy, &y3); + + if (y1 != y2 || y2 != y3) + { + int yMax = std::max(y1, std::max(y2, y3)); + + if (m_gridLeft->isLeadGrid()) //do not handle case (y1 == yMax) here!!! Avoid back coupling! + m_gridLeft->SetMargins(0, 0); + else if (y1 < yMax) + m_gridLeft->SetMargins(0, 30); + + if (m_gridRight->isLeadGrid()) + m_gridRight->SetMargins(0, 0); + else if (y2 < yMax) + m_gridRight->SetMargins(0, 30); + + if (m_gridMiddle->isLeadGrid()) + m_gridMiddle->SetMargins(0, 0); + else if (y3 < yMax) + m_gridMiddle->SetMargins(0, 30); + + m_gridLeft->ForceRefresh(); + m_gridRight->ForceRefresh(); + m_gridMiddle->ForceRefresh(); + } + } } -bool CustomGrid::isLeadGrid() +void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap) { - return leadGrid == static_cast<const wxGrid*>(this); + currentSortColumn = sortColumn; + sortMarker = bitmap; } -//overwrite virtual method to finally get rid of the scrollbars -void CustomGrid::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh) +void CustomGrid::DrawColLabel(wxDC& dc, int col) { - if (scrollbarsEnabled) - wxWindow::SetScrollbar(orientation, position, thumbSize, range, refresh); - else - wxWindow::SetScrollbar(orientation, 0, 0, 0, refresh); + wxGrid::DrawColLabel(dc, col); + + if (col == currentSortColumn) + dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border } +//############################################################################################ +//CustomGrid specializations -//workaround: ensure that all grids are properly aligned: add some extra window space to grids that have no horizontal scrollbar -void CustomGrid::adjustGridHeights(wxEvent& event) //m_gridLeft, m_gridRight, m_gridMiddle are not NULL in this context +template <bool leftSide, bool showFileIcons> +class GridCellRenderer : public wxGridCellStringRenderer { - int y1 = 0; - int y2 = 0; - int y3 = 0; - int dummy = 0; +public: + GridCellRenderer(CustomGridTableRim* gridDataTable) : m_gridDataTable(gridDataTable) {}; - m_gridLeft->GetViewStart(&dummy, &y1); - m_gridRight->GetViewStart(&dummy, &y2); - m_gridMiddle->GetViewStart(&dummy, &y3); - if (y1 != y2 || y2 != y3) + virtual void Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rect, + int row, int col, + bool isSelected) { - 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); - else if (y1 < yMax) - m_gridLeft->SetMargins(0, 30); - - if (leadGrid == m_gridRight) - m_gridRight->SetMargins(0, 0); - else if (y2 < yMax) - m_gridRight->SetMargins(0, 30); - - if (leadGrid == m_gridMiddle) - m_gridMiddle->SetMargins(0, 0); - else if (y3 < yMax) - m_gridMiddle->SetMargins(0, 30); - - m_gridLeft->ForceRefresh(); - m_gridRight->ForceRefresh(); - m_gridMiddle->ForceRefresh(); - } -} +#ifdef FFS_WIN + //############## show windows explorer file icons ###################### + if (showFileIcons) //evaluate at compile time + { + const int ICON_SIZE = 16; //size in pixel -void CustomGrid::updateGridSizes() -{ - assert(gridDataTable); - gridDataTable->updateGridSizes(); -} + if ( m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME && + rect.GetWidth() >= ICON_SIZE) + { + //retrieve grid data + const FileCompareLine* rowData = m_gridDataTable->getRawData(row); + if (rowData) //valid row + { + const DefaultChar* filename; + if (leftSide) //evaluate at compile time + filename = rowData->fileDescrLeft.fullName.c_str(); + else + filename = rowData->fileDescrRight.fullName.c_str(); + if (*filename != DefaultChar(0)) //test if filename is empty + { + // Get the file icon. + SHFILEINFO fileInfo; + fileInfo.hIcon = 0; //initialize hIcon -void CustomGrid::setSortMarker(const int sortColumn, const wxBitmap* bitmap) -{ - currentSortColumn = sortColumn; - sortMarker = bitmap; -} + if (SHGetFileInfo(filename, //NOTE: CoInitializeEx()/CoUninitialize() implicitly called by wxWidgets on program startup! + 0, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON)) + { + //clear area where icon will be placed + wxRect rectShrinked(rect); + rectShrinked.SetWidth(ICON_SIZE + 2); //add 2 pixel border + wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + //draw icon + if (fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { //bug report: https://sourceforge.net/tracker/?func=detail&aid=2768004&group_id=234430&atid=1093080 + wxIcon icon; + icon.SetHICON(static_cast<WXHICON>(fileInfo.hIcon)); + icon.SetSize(ICON_SIZE, ICON_SIZE); -void CustomGrid::DrawColLabel(wxDC& dc, int col) -{ - wxGrid::DrawColLabel(dc, col); + dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY()); - if (col == currentSortColumn) - dc.DrawBitmap(*sortMarker, GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border + if (!DestroyIcon(fileInfo.hIcon)) + throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted()); + } + + //draw rest + rectShrinked.SetWidth(rect.GetWidth() - ICON_SIZE - 2); + rectShrinked.SetX(rect.GetX() + ICON_SIZE + 2); + wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + return; + } + } + } + } + } + //default + wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); + +#elif defined FFS_LINUX + wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); +#endif + } + +private: + const CustomGridTableRim* const m_gridDataTable; +}; + + + +void CustomGridRim::updateGridSizes() +{ + assert(gridDataTable); + gridDataTable->updateGridSizes(); } -xmlAccess::ColumnAttributes CustomGrid::getDefaultColumnAttributes() +xmlAccess::ColumnAttributes CustomGridRim::getDefaultColumnAttributes() { xmlAccess::ColumnAttributes defaultColumnSettings; xmlAccess::ColumnAttrib newEntry; - newEntry.type = xmlAccess::FULL_NAME; newEntry.visible = false; newEntry.position = 0; newEntry.width = 150; defaultColumnSettings.push_back(newEntry); - newEntry.type = xmlAccess::FILENAME; - newEntry.visible = true; + newEntry.type = xmlAccess::DIRECTORY; newEntry.position = 1; - newEntry.width = 138; + newEntry.width = 140; defaultColumnSettings.push_back(newEntry); newEntry.type = xmlAccess::REL_PATH; + newEntry.visible = true; newEntry.position = 2; newEntry.width = 118; defaultColumnSettings.push_back(newEntry); - newEntry.type = xmlAccess::SIZE; + newEntry.type = xmlAccess::FILENAME; newEntry.position = 3; - newEntry.width = 67; + newEntry.width = 138; defaultColumnSettings.push_back(newEntry); - newEntry.type = xmlAccess::DATE; + newEntry.type = xmlAccess::SIZE; newEntry.position = 4; + newEntry.width = 70; + defaultColumnSettings.push_back(newEntry); + + newEntry.type = xmlAccess::DATE; + newEntry.position = 5; newEntry.width = 113; defaultColumnSettings.push_back(newEntry); @@ -848,7 +887,7 @@ xmlAccess::ColumnAttributes CustomGrid::getDefaultColumnAttributes() } -xmlAccess::ColumnAttributes CustomGrid::getColumnAttributes() +xmlAccess::ColumnAttributes CustomGridRim::getColumnAttributes() { std::sort(columnSettings.begin(), columnSettings.end(), xmlAccess::sortByPositionAndVisibility); @@ -866,7 +905,7 @@ xmlAccess::ColumnAttributes CustomGrid::getColumnAttributes() } -void CustomGrid::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) +void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) { //remove special alignment for column "size" for (int i = 0; i < GetNumberCols(); ++i) @@ -940,14 +979,14 @@ void CustomGrid::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) } -xmlAccess::ColumnTypes CustomGrid::getTypeAtPos(unsigned pos) const +xmlAccess::ColumnTypes CustomGridRim::getTypeAtPos(unsigned pos) const { assert(gridDataTable); return gridDataTable->getTypeAtPos(pos); } -wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType) +wxString CustomGridRim::getTypeName(xmlAccess::ColumnTypes colType) { switch (colType) { @@ -957,6 +996,8 @@ wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType) return _("Filename"); case xmlAccess::REL_PATH: return _("Relative path"); + case xmlAccess::DIRECTORY: + return _("Directory"); case xmlAccess::SIZE: return _("Size"); case xmlAccess::DATE: @@ -966,139 +1007,121 @@ wxString CustomGrid::getTypeName(xmlAccess::ColumnTypes colType) } } -//############################################################################################ -//CustomGrid specializations +//---------------------------------------------------------------------------------------- CustomGridLeft::CustomGridLeft(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name) : - CustomGrid(parent, id, pos, size, style, name) {} + CustomGridRim(parent, id, pos, size, style, name) {} -template <bool leftSide, bool showFileIcons> -class GridCellRenderer : public wxGridCellStringRenderer +bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) { -public: - GridCellRenderer(CustomGridTable* gridDataTable) : m_gridDataTable(gridDataTable) {}; - - - virtual void Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected) - { -#ifdef FFS_WIN - //############## show windows explorer file icons ###################### - - if (showFileIcons) //evaluate at compile time - { - const int ICON_SIZE = 16; //size in pixel - - if ( m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME && - rect.GetWidth() >= ICON_SIZE) - { - //retrieve grid data - const FileCompareLine* rowData = m_gridDataTable->getRawData(row); - if (rowData) //valid row - { - const DefaultChar* filename; - if (leftSide) //evaluate at compile time - filename = rowData->fileDescrLeft.fullName.c_str(); - else - filename = rowData->fileDescrRight.fullName.c_str(); + //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 - if (*filename != 0) //test if filename is empty - { - // Get the file icon. - SHFILEINFO fileInfo; - if (SHGetFileInfo(filename, - 0, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON)) - { - wxIcon icon; - icon.SetHICON((WXHICON)fileInfo.hIcon); - icon.SetSize(ICON_SIZE, ICON_SIZE); + return true; +} - //clear area where icon will be placed - wxRect rectShrinked(rect); - rectShrinked.SetWidth(ICON_SIZE + 2); //add 2 pixel border - dc.SetPen(*wxWHITE_PEN); - dc.SetBrush(*wxWHITE_BRUSH); - dc.DrawRectangle(rectShrinked); +void CustomGridLeft::initSettings(const bool showFileIcons, + CustomGrid* gridLeft, + CustomGrid* gridRight, + CustomGrid* gridMiddle, + GridView* gridDataView) +{ + //these grids will scroll together + m_gridLeft = gridLeft; + m_gridRight = gridRight; + m_gridMiddle = gridMiddle; - //draw icon - dc.DrawIcon(icon, rectShrinked.GetX() + 2, rectShrinked.GetY()); + //set underlying grid data + assert(gridDataTable); + gridDataTable->setGridDataTable(gridDataView); - rectShrinked.SetWidth(rect.GetWidth() - ICON_SIZE - 2); - rectShrinked.SetX(rect.GetX() + ICON_SIZE + 2); - wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + if (showFileIcons) + SetDefaultRenderer(new GridCellRenderer<true, true>(gridDataTable)); //SetDefaultRenderer takes ownership! + else + SetDefaultRenderer(new GridCellRenderer<true, false>(gridDataTable)); +} - if (!DestroyIcon(fileInfo.hIcon)) - throw RuntimeException(wxString(wxT("Error deallocating Icon handle!\n\n")) + FreeFileSync::getLastErrorFormatted()); - return; - } - } - } - } - } - //default - wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); +//this method is called when grid view changes: useful for parallel updating of multiple grids +void CustomGridLeft::DoPrepareDC(wxDC& dc) +{ + wxScrollHelper::DoPrepareDC(dc); -#elif defined FFS_LINUX - wxGridCellStringRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); -#endif + int x, y = 0; + if (isLeadGrid()) //avoid back coupling + { + GetViewStart(&x, &y); + m_gridMiddle->Scroll(-1, y); //scroll in y-direction only + m_gridRight->Scroll(x, y); } +} -private: - CustomGridTable* m_gridDataTable; -}; +//---------------------------------------------------------------------------------------- +CustomGridRight::CustomGridRight(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) : + CustomGridRim(parent, id, pos, size, style, name) {} -bool CustomGridLeft::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) +bool CustomGridRight::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) { - //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(); + gridDataTable = new CustomGridTableRight; SetTable(gridDataTable, true, wxGrid::wxGridSelectRows); //give ownership to wxGrid: gridDataTable is deleted automatically in wxGrid destructor return true; } +void CustomGridRight::initSettings(const bool showFileIcons, + CustomGrid* gridLeft, + CustomGrid* gridRight, + CustomGrid* gridMiddle, + GridView* gridDataView) +{ + //these grids will scroll together + m_gridLeft = gridLeft; + m_gridRight = gridRight; + m_gridMiddle = gridMiddle; + + //set underlying grid data + assert(gridDataTable); + gridDataTable->setGridDataTable(gridDataView); + + if (showFileIcons) + SetDefaultRenderer(new GridCellRenderer<false, true>(gridDataTable)); //SetDefaultRenderer takes ownership! + else + SetDefaultRenderer(new GridCellRenderer<false, false>(gridDataTable)); +} + + //this method is called when grid view changes: useful for parallel updating of multiple grids -void CustomGridLeft::DoPrepareDC(wxDC& dc) +void CustomGridRight::DoPrepareDC(wxDC& dc) { wxScrollHelper::DoPrepareDC(dc); int x, y = 0; - if (this == leadGrid) //avoid back coupling + if (isLeadGrid()) //avoid back coupling { GetViewStart(&x, &y); - m_gridMiddle->Scroll(-1, y); //scroll in y-direction only - m_gridRight->Scroll(x, y); + m_gridLeft->Scroll(x, y); + m_gridMiddle->Scroll(-1, y); } } -void CustomGridLeft::initGridRenderer(const bool showFileIcons) -{ - if (showFileIcons) - SetDefaultRenderer(new GridCellRenderer<true, true>(gridDataTable)); //SetDefaultRenderer takes ownership! - else - SetDefaultRenderer(new GridCellRenderer<true, false>(gridDataTable)); -} - - //---------------------------------------------------------------------------------------- CustomGridMiddle::CustomGridMiddle(wxWindow *parent, wxWindowID id, @@ -1106,7 +1129,8 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent, const wxSize& size, long style, const wxString& name) : - CustomGrid(parent, id, pos, size, style, name) + CustomGrid(parent, id, pos, size, style, name), + gridDataTable(NULL) { const wxString header = _("Legend"); wxString toolTip = header + wxT("\n") + @@ -1121,6 +1145,42 @@ CustomGridMiddle::CustomGridMiddle(wxWindow *parent, } +void CustomGridMiddle::initSettings(CustomGrid* gridLeft, + CustomGrid* gridRight, + CustomGrid* gridMiddle, + FreeFileSync::GridView* gridDataView) +{ + //these grids will scroll together + m_gridLeft = gridLeft; + m_gridRight = gridRight; + m_gridMiddle = gridMiddle; + + //set underlying grid data + assert(gridDataTable); + gridDataTable->setGridDataTable(gridDataView); + +#ifdef FFS_LINUX //get rid of scrollbars; Linux: change policy for GtkScrolledWindow + GtkWidget* gridWidget = wxWindow::m_widget; + GtkScrolledWindow* scrolledWindow = GTK_SCROLLED_WINDOW(gridWidget); + gtk_scrolled_window_set_policy(scrolledWindow, GTK_POLICY_NEVER, GTK_POLICY_NEVER); +#endif +} + + +#ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method +void CustomGridMiddle::SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh) +{ + wxWindow::SetScrollbar(orientation, 0, 0, 0, refresh); +} +#endif + +void CustomGridMiddle::updateGridSizes() +{ + assert(gridDataTable); + gridDataTable->updateGridSizes(); +} + + class GridCellRendererMiddle : public wxGridCellStringRenderer { public: @@ -1148,12 +1208,10 @@ public: //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); + wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); //print image into first block - rectShrinked.SetX(1); + rectShrinked.SetX(rect.GetX() + 1); if (rowData->selectedForSynchronization) dc.DrawLabel(wxEmptyString, *globalResource.bitmapCheckBoxTrue, rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); else @@ -1161,18 +1219,18 @@ public: //print second block (default): display compare result rectShrinked.SetWidth(rect.GetWidth() - shift); - rectShrinked.SetX(shift); + rectShrinked.SetX(rect.GetX() + shift); wxGridCellStringRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); } private: - CustomGridTable* m_gridDataTable; + const CustomGridTable* const m_gridDataTable; }; bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) { - gridDataTable = new CustomGridTableMiddle(); + gridDataTable = new CustomGridTableMiddle; 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 @@ -1188,7 +1246,7 @@ void CustomGridMiddle::DoPrepareDC(wxDC& dc) wxScrollHelper::DoPrepareDC(dc); int x, y = 0; - if (this == leadGrid) //avoid back coupling + if (isLeadGrid()) //avoid back coupling { GetViewStart(&x, &y); m_gridLeft->Scroll(-1, y); @@ -1196,45 +1254,3 @@ void CustomGridMiddle::DoPrepareDC(wxDC& dc) } } - -//---------------------------------------------------------------------------------------- -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); - } -} - - -void CustomGridRight::initGridRenderer(const bool showFileIcons) -{ - if (showFileIcons) - SetDefaultRenderer(new GridCellRenderer<false, true>(gridDataTable)); //SetDefaultRenderer takes ownership! - else - SetDefaultRenderer(new GridCellRenderer<false, false>(gridDataTable)); -} |