diff options
Diffstat (limited to 'library')
-rw-r--r-- | library/binary.cpp | 15 | ||||
-rw-r--r-- | library/custom_grid.cpp | 499 | ||||
-rw-r--r-- | library/custom_grid.h | 41 | ||||
-rw-r--r-- | library/db_file.cpp | 111 | ||||
-rw-r--r-- | library/db_file.h | 4 | ||||
-rw-r--r-- | library/dir_exist_async.h | 4 | ||||
-rw-r--r-- | library/dir_lock.cpp | 126 | ||||
-rw-r--r-- | library/dir_lock.h | 2 | ||||
-rw-r--r-- | library/hard_filter.cpp | 16 | ||||
-rw-r--r-- | library/icon_buffer.cpp | 533 | ||||
-rw-r--r-- | library/icon_buffer.h | 32 | ||||
-rw-r--r-- | library/lock_holder.h | 6 | ||||
-rw-r--r-- | library/parallel_scan.cpp | 128 | ||||
-rw-r--r-- | library/parallel_scan.h | 2 | ||||
-rw-r--r-- | library/process_xml.cpp | 295 | ||||
-rw-r--r-- | library/process_xml.h | 62 | ||||
-rw-r--r-- | library/resources.cpp | 68 | ||||
-rw-r--r-- | library/resources.h | 15 | ||||
-rw-r--r-- | library/soft_filter.h | 43 | ||||
-rw-r--r-- | library/statistics.cpp | 2 | ||||
-rw-r--r-- | library/statistics.h | 2 | ||||
-rw-r--r-- | library/status_handler.cpp | 13 | ||||
-rw-r--r-- | library/status_handler.h | 18 |
23 files changed, 1098 insertions, 939 deletions
diff --git a/library/binary.cpp b/library/binary.cpp index 1fd8c55f..1e043d33 100644 --- a/library/binary.cpp +++ b/library/binary.cpp @@ -38,10 +38,7 @@ public: bufSize /= 2; } - operator size_t() const - { - return bufSize; - } + operator size_t() const { return bufSize; } private: static const size_t BUFFER_SIZE_MIN = 128 * 1024; @@ -69,13 +66,15 @@ private: bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename2, CompareCallback& callback) { - FileInput file1(filename1); //throw (FileError) - FileInput file2(filename2); //throw (FileError) + FileInput file1(filename1); //throw FileError + FileInput file2(filename2); //throw FileError static boost::thread_specific_ptr<std::vector<char>> cpyBuf1; static boost::thread_specific_ptr<std::vector<char>> cpyBuf2; - if (!cpyBuf1.get()) cpyBuf1.reset(new std::vector<char>()); - if (!cpyBuf2.get()) cpyBuf2.reset(new std::vector<char>()); + if (!cpyBuf1.get()) + cpyBuf1.reset(new std::vector<char>()); + if (!cpyBuf2.get()) + cpyBuf2.reset(new std::vector<char>()); std::vector<char>& memory1 = *cpyBuf1; std::vector<char>& memory2 = *cpyBuf2; diff --git a/library/custom_grid.cpp b/library/custom_grid.cpp index ccabbea9..b4d7af41 100644 --- a/library/custom_grid.cpp +++ b/library/custom_grid.cpp @@ -16,7 +16,6 @@ #include "../shared/custom_tooltip.h" #include "../shared/i18n.h" #include <wx/dcclient.h> -#include "icon_buffer.h" #include <wx/icon.h> #include <wx/tooltip.h> #include <wx/settings.h> @@ -272,100 +271,105 @@ protected: const FileSystemObject* fsObj = getRawData(row); if (fsObj) { - if (!fsObj->isEmpty<side>()) + struct GetValue : public FSObjectVisitor { - struct GetValue : public FSObjectVisitor + GetValue(xmlAccess::ColumnTypes colType, const FileSystemObject& fso) : colType_(colType), fsObj_(fso) {} + virtual void visit(const FileMapping& fileObj) { - GetValue(xmlAccess::ColumnTypes colType) : colType_(colType) {} - virtual void visit(const FileMapping& fileObj) + switch (colType_) { - switch (colType_) - { - case xmlAccess::FULL_PATH: - value = toWx(fileObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); - break; - case xmlAccess::FILENAME: //filename - value = toWx(fileObj.getShortName<side>()); - break; - case xmlAccess::REL_PATH: //relative path - value = toWx(fileObj.getParentRelativeName()); - break; - case xmlAccess::DIRECTORY: - value = toWx(fileObj.getBaseDirPf<side>()); - break; - case xmlAccess::SIZE: //file size + case xmlAccess::FULL_PATH: + value = toWx(fileObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); + break; + case xmlAccess::FILENAME: //filename + value = toWx(fileObj.getShortName<side>()); + break; + case xmlAccess::REL_PATH: //relative path + value = toWx(fileObj.getObjRelativeName().BeforeLast(FILE_NAME_SEPARATOR)); //returns empty string if ch not found + break; + case xmlAccess::DIRECTORY: + value = toWx(fileObj.getBaseDirPf<side>()); + break; + case xmlAccess::SIZE: //file size + if (!fsObj_.isEmpty<side>()) value = zen::toStringSep(fileObj.getFileSize<side>()); - break; - case xmlAccess::DATE: //date + break; + case xmlAccess::DATE: //date + if (!fsObj_.isEmpty<side>()) value = zen::utcTimeToLocalString(fileObj.getLastWriteTime<side>()); - break; - case xmlAccess::EXTENSION: //file extension - value = toWx(fileObj.getExtension<side>()); - break; - } + break; + case xmlAccess::EXTENSION: //file extension + value = toWx(fileObj.getExtension<side>()); + break; } + } - virtual void visit(const SymLinkMapping& linkObj) + virtual void visit(const SymLinkMapping& linkObj) + { + switch (colType_) { - switch (colType_) - { - case xmlAccess::FULL_PATH: - value = toWx(linkObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); - break; - case xmlAccess::FILENAME: //filename - value = toWx(linkObj.getShortName<side>()); - break; - case xmlAccess::REL_PATH: //relative path - value = toWx(linkObj.getParentRelativeName()); - break; - case xmlAccess::DIRECTORY: - value = toWx(linkObj.getBaseDirPf<side>()); - break; - case xmlAccess::SIZE: //file size + case xmlAccess::FULL_PATH: + value = toWx(linkObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); + break; + case xmlAccess::FILENAME: //filename + value = toWx(linkObj.getShortName<side>()); + break; + case xmlAccess::REL_PATH: //relative path + value = toWx(linkObj.getObjRelativeName().BeforeLast(FILE_NAME_SEPARATOR)); //returns empty string if ch not found + break; + case xmlAccess::DIRECTORY: + value = toWx(linkObj.getBaseDirPf<side>()); + break; + case xmlAccess::SIZE: //file size + if (!fsObj_.isEmpty<side>()) value = _("<Symlink>"); - break; - case xmlAccess::DATE: //date + break; + case xmlAccess::DATE: //date + if (!fsObj_.isEmpty<side>()) value = zen::utcTimeToLocalString(linkObj.getLastWriteTime<side>()); - break; - case xmlAccess::EXTENSION: //file extension - value = wxEmptyString; - break; - } + break; + case xmlAccess::EXTENSION: //file extension + value = wxEmptyString; + break; } + } - virtual void visit(const DirMapping& dirObj) + virtual void visit(const DirMapping& dirObj) + { + switch (colType_) { - switch (colType_) - { - case xmlAccess::FULL_PATH: - value = toWx(dirObj.getFullName<side>()); - break; - case xmlAccess::FILENAME: - value = toWx(dirObj.getShortName<side>()); - break; - case xmlAccess::REL_PATH: - value = toWx(dirObj.getParentRelativeName()); - break; - case xmlAccess::DIRECTORY: - value = toWx(dirObj.getBaseDirPf<side>()); - break; - case xmlAccess::SIZE: //file size + case xmlAccess::FULL_PATH: + value = toWx(dirObj.getFullName<side>()); + break; + case xmlAccess::FILENAME: + value = toWx(dirObj.getShortName<side>()); + break; + case xmlAccess::REL_PATH: + value = toWx(dirObj.getObjRelativeName().BeforeLast(FILE_NAME_SEPARATOR)); //returns empty string if ch not found + break; + case xmlAccess::DIRECTORY: + value = toWx(dirObj.getBaseDirPf<side>()); + break; + case xmlAccess::SIZE: //file size + if (!fsObj_.isEmpty<side>()) value = _("<Directory>"); - break; - case xmlAccess::DATE: //date - value = wxEmptyString; - break; - case xmlAccess::EXTENSION: //file extension + break; + case xmlAccess::DATE: //date + if (!fsObj_.isEmpty<side>()) value = wxEmptyString; - break; - } + break; + case xmlAccess::EXTENSION: //file extension + value = wxEmptyString; + break; } - xmlAccess::ColumnTypes colType_; - wxString value; - } getVal(getTypeAtPos(col)); - fsObj->accept(getVal); - return getVal.value; - } + } + xmlAccess::ColumnTypes colType_; + wxString value; + + const FileSystemObject& fsObj_; + } getVal(getTypeAtPos(col), *fsObj); + fsObj->accept(getVal); + return getVal.value; } //if data is not found: return wxEmptyString; @@ -381,12 +385,11 @@ protected: { virtual void visit(const FileMapping& fileObj) { - //Optimization: if filename exists on both sides, always use left side's file: - //Icon should be the same on both sides anyway... - if (!fileObj.isEmpty<LEFT_SIDE>() && !fileObj.isEmpty<RIGHT_SIDE>()) - iconName = fileObj.getFullName<LEFT_SIDE>(); - else - iconName = fileObj.getFullName<side>(); + //Optimization: if filename exists on both sides, always use left side's file + //if (!fileObj.isEmpty<LEFT_SIDE>() && !fileObj.isEmpty<RIGHT_SIDE>()) + // iconName = fileObj.getFullName<LEFT_SIDE>(); + //else -> now with thumbnails this isn't viable anymore + iconName = fileObj.getFullName<side>(); } virtual void visit(const SymLinkMapping& linkObj) { @@ -585,14 +588,15 @@ private: switch (fsObj->getCategory()) { case FILE_LEFT_SIDE_ONLY: - case FILE_RIGHT_SIDE_ONLY: + case FILE_LEFT_NEWER: result.first = *wxBLACK; - result.second = COLOR_CMP_GREEN; + result.second = COLOR_SYNC_BLUE; //COLOR_CMP_BLUE; break; - case FILE_LEFT_NEWER: + + case FILE_RIGHT_SIDE_ONLY: case FILE_RIGHT_NEWER: result.first = *wxBLACK; - result.second = COLOR_CMP_BLUE; + result.second = COLOR_SYNC_GREEN; //COLOR_CMP_GREEN; break; case FILE_DIFFERENT: result.first = *wxBLACK; @@ -688,10 +692,22 @@ bool CustomGrid::isLeadGrid() const } +void CustomGrid::setIconManager(const std::shared_ptr<IconBuffer>& iconBuffer) +{ + if (iconBuffer.get()) + SetDefaultRowSize(iconBuffer->getSize() + 1, true); //+ 1 for line between rows + else + SetDefaultRowSize(IconBuffer(IconBuffer::SIZE_SMALL).getSize() + 1, true); //currently iconBuffer is always bound, but we may use it as a "no icon" status at some time... + + enableFileIcons(iconBuffer); + Refresh(); +} + + void CustomGrid::RefreshCell(int row, int col) { wxRect rectScrolled(CellToRect(row, col)); - + //use: wxRect rect = CellToRect( row, col ); ? CalcScrolledPosition(rectScrolled.x, rectScrolled.y, &rectScrolled.x, &rectScrolled.y); GetGridWindow()->RefreshRect(rectScrolled); //note: CellToRect() and YToRow work on m_gridWindow NOT on the whole grid! @@ -943,8 +959,6 @@ bool gridsShouldBeCleared(const wxEvent& event) if (mouseEvent->ButtonDown(wxMOUSE_BTN_LEFT)) return true; - - return false; } else { @@ -980,8 +994,6 @@ bool gridsShouldBeCleared(const wxEvent& event) case WXK_ESCAPE: return true; } - - return false; } } @@ -1060,7 +1072,7 @@ void CustomGrid::adjustGridHeights(wxEvent& event) void CustomGrid::updateGridSizes() { - if(getGridDataTable()) + if (getGridDataTable()) getGridDataTable()->updateGridSizes(); } @@ -1100,9 +1112,9 @@ void CustomGrid::DrawColLabel(wxDC& dc, int col) if (col == m_marker.first) { if (m_marker.second == ASCENDING) - dc.DrawBitmap(GlobalResources::instance().getImage(wxT("smallUp")), GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border + dc.DrawBitmap(GlobalResources::getImage(wxT("smallUp")), GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border else - dc.DrawBitmap(GlobalResources::instance().getImage(wxT("smallDown")), GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border + dc.DrawBitmap(GlobalResources::getImage(wxT("smallDown")), GetColRight(col) - 16 - 2, 2, true); //respect 2-pixel border } } @@ -1178,13 +1190,15 @@ std::set<size_t> CustomGrid::getAllSelectedRows() const //############################################################################################ //CustomGrid specializations -template <bool showFileIcons> class GridCellRenderer : public wxGridCellStringRenderer { public: - GridCellRenderer(CustomGridRim::LoadSuccess& loadIconSuccess, const CustomGridTableRim* gridDataTable) : - m_loadIconSuccess(loadIconSuccess), - m_gridDataTable(gridDataTable) {} + GridCellRenderer(CustomGridRim::FailedIconLoad& failedLoads, + const CustomGridTableRim* gridDataTable, + const std::shared_ptr<zen::IconBuffer>& iconBuffer) : + failedLoads_(failedLoads), + m_gridDataTable(gridDataTable), + iconBuffer_(iconBuffer) {} virtual void Draw(wxGrid& grid, @@ -1196,79 +1210,68 @@ public: { //############## show windows explorer file icons ###################### - if (showFileIcons && //evaluate at compile time + if (iconBuffer_.get() && m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME) { - if (rect.GetWidth() >= IconBuffer::ICON_SIZE) + const int iconSize = iconBuffer_->getSize(); + if (rect.GetWidth() >= iconSize) { // Partitioning: - // _____________________ - // | 2 pix | icon | rest | - // --------------------- + // ____________________________ + // | 2 pix border | icon | rest | + // ---------------------------- + { + //clear area where icon will be placed (including border) + wxRect rectShrinked(rect); + rectShrinked.SetWidth(LEFT_BORDER + iconSize); //add 2 pixel border + wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + } - //clear area where icon will be placed - wxRect rectShrinked(rect); - rectShrinked.SetWidth(IconBuffer::ICON_SIZE + LEFT_BORDER); //add 2 pixel border - wxGridCellRenderer::Draw(grid, attr, dc, rectShrinked, row, col, isSelected); + { + //draw rest + wxRect rest(rect); //unscrolled + rest.x += LEFT_BORDER + iconSize; + rest.width -= LEFT_BORDER + iconSize; + wxGridCellStringRenderer::Draw(grid, attr, dc, rest, row, col, isSelected); + } - //draw rest - wxRect rest(rect); //unscrolled - rest.x += IconBuffer::ICON_SIZE + LEFT_BORDER; - rest.width -= IconBuffer::ICON_SIZE + LEFT_BORDER; - wxGridCellStringRenderer::Draw(grid, attr, dc, rest, row, col, isSelected); + wxRect rectIcon(rect); + rectIcon.SetWidth(iconSize); //set to icon area only + rectIcon.x += LEFT_BORDER; // //try to draw icon //retrieve grid data const Zstring fileName = m_gridDataTable->getIconFile(row); if (!fileName.empty()) { + wxIcon icon; + //first check if it is a directory icon: if (fileName == Zstr("folder")) - { - dc.DrawIcon(IconBuffer::getDirectoryIcon(), rectShrinked.GetX() + LEFT_BORDER, rectShrinked.GetY()); - m_loadIconSuccess[row] = true; //save status of last icon load -> used for async. icon loading - } + icon = iconBuffer_->genericDirIcon(); else //retrieve file icon { - wxIcon icon; - bool iconDrawnFully = false; + if (!iconBuffer_->requestFileIcon(fileName, &icon)) //returns false if icon is not in buffer { - const bool loadSuccess = IconBuffer::getInstance().requestFileIcon(fileName, &icon); //returns false if icon is not in buffer - if (loadSuccess) - { - //----------------------------------------------------------------------------------------------- - //only mark as successful if icon was drawn fully! - //(attention: when scrolling, rows get partially updated, which can result in the upper half being blank!) - - //rect where icon was placed - wxRect iconRect(rect); //unscrolled - iconRect.x += LEFT_BORDER; - iconRect.SetWidth(IconBuffer::ICON_SIZE); - - //convert to scrolled coordinates - grid.CalcScrolledPosition(iconRect.x, iconRect.y, &iconRect.x, &iconRect.y); - - wxRegionIterator regionsInv(grid.GetGridWindow()->GetUpdateRegion()); - while (regionsInv) - { - if (regionsInv.GetRect().Contains(iconRect)) - { - iconDrawnFully = true; - break; - } - ++regionsInv; - } - } - else - icon = IconBuffer::getFileIcon(); //better than nothing - } + icon = iconBuffer_->genericFileIcon(); //better than nothing - if (icon.IsOk()) - dc.DrawIcon(icon, rectShrinked.GetX() + LEFT_BORDER, rectShrinked.GetY()); + failedLoads_.insert(row); //save status of failed icon load -> used for async. icon loading + //falsify only! we want to avoid writing incorrect success values when only partially updating the DC, e.g. when scrolling, + //see repaint behavior of ::ScrollWindow() function! + } + } - //----------------------------------------------------------------------------------------------- - //save status of last icon load -> used for async. icon loading - m_loadIconSuccess[row] = iconDrawnFully; + if (icon.IsOk()) + { + int posX = rectIcon.GetX(); + int posY = rectIcon.GetY(); + //center icon if it is too small + if (rectIcon.GetWidth() > icon.GetWidth()) + posX += (rectIcon.GetWidth() - icon.GetWidth()) / 2; + if (rectIcon.GetHeight() > icon.GetHeight()) + posY += (rectIcon.GetHeight() - icon.GetHeight()) / 2; + + dc.DrawIcon(icon, posX, posY); } } return; @@ -1285,11 +1288,11 @@ public: wxDC& dc, int row, int col) { - if (showFileIcons && //evaluate at compile time + if (iconBuffer_.get() && //evaluate at compile time m_gridDataTable->getTypeAtPos(col) == xmlAccess::FILENAME) { wxSize rv = wxGridCellStringRenderer::GetBestSize(grid, attr, dc, row, col); - rv.SetWidth(rv.GetWidth() + LEFT_BORDER + IconBuffer::ICON_SIZE); + rv.SetWidth(rv.GetWidth() + LEFT_BORDER + iconBuffer_->getSize()); return rv; } @@ -1299,8 +1302,9 @@ public: private: - CustomGridRim::LoadSuccess& m_loadIconSuccess; + CustomGridRim::FailedIconLoad& failedLoads_; const CustomGridTableRim* const m_gridDataTable; + std::shared_ptr<zen::IconBuffer> iconBuffer_; static const int LEFT_BORDER = 2; }; @@ -1313,7 +1317,7 @@ CustomGridRim::CustomGridRim(wxWindow* parent, const wxSize& size, long style, const wxString& name) : - CustomGrid(parent, id, pos, size, style, name), fileIconsAreEnabled(false), otherGrid(NULL) + CustomGrid(parent, id, pos, size, style, name), otherGrid(NULL) { Connect(wxEVT_GRID_COL_SIZE, wxGridSizeEventHandler(CustomGridRim::OnResizeColumn), NULL, this); //row-based tooltip } @@ -1491,7 +1495,7 @@ void CustomGridRim::setColumnAttributes(const xmlAccess::ColumnAttributes& attr) if (getTypeAtPos(i) == xmlAccess::SIZE) { wxGridCellAttr* cellAttributes = GetOrCreateCellAttr(0, i); - cellAttributes->SetAlignment(wxALIGN_LEFT,wxALIGN_CENTRE); + cellAttributes->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE); SetColAttr(i, cellAttributes); break; } @@ -1647,20 +1651,14 @@ void CustomGridRim::autoSizeColumns() //performance optimized column resizer (a } -void CustomGridRim::enableFileIcons(const bool value) +void CustomGridRim::enableFileIcons(const std::shared_ptr<IconBuffer>& iconBuffer) { - fileIconsAreEnabled = value; - - if (value) - SetDefaultRenderer(new GridCellRenderer<true>(loadIconSuccess, getGridDataTableRim())); //SetDefaultRenderer takes ownership! - else - SetDefaultRenderer(new GridCellRenderer<false>(loadIconSuccess, getGridDataTableRim())); - - Refresh(); + iconBuffer_ = iconBuffer; + SetDefaultRenderer(new GridCellRenderer(failedLoads, getGridDataTableRim(), iconBuffer)); //SetDefaultRenderer takes ownership! } -CustomGridRim::VisibleRowRange CustomGridRim::getVisibleRows() +std::pair<CustomGridRim::RowBegin, CustomGridRim::RowEnd> CustomGridRim::getVisibleRows() { int dummy = -1; int height = -1; @@ -1668,17 +1666,17 @@ CustomGridRim::VisibleRowRange CustomGridRim::getVisibleRows() if (height >= 0) { - const int topRow = mousePosToCell(wxPoint(0, 0)).first; - if (topRow >= 0) - { - const int rowCount = static_cast<int>(ceil(height / static_cast<double>(GetDefaultRowSize()))); // = height / rowHeight rounded up - const int bottomRow = topRow + rowCount - 1; + const int rowTop = mousePosToCell(wxPoint(0, 0)).first; + int rowEnd = mousePosToCell(wxPoint(0, height)).first; + if (rowEnd == -1) //when scrolling to the very end, there are a few border pixels that do not belong to any row + rowEnd = GetNumberRows(); + else + ++rowEnd; - return VisibleRowRange(topRow, bottomRow); //"top" means here top of the screen: => smaller value - } + if (0 <= rowTop && rowTop <= rowEnd) + return std::make_pair(rowTop, rowEnd); //"top" means here top of the screen => smaller value } - - return VisibleRowRange(0, 0); + return std::make_pair(0, 0); } @@ -1691,45 +1689,53 @@ CustomGridTableRim* CustomGridRim::getGridDataTableRim() const void CustomGridRim::getIconsToBeLoaded(std::vector<Zstring>& newLoad) //loads all (not yet) drawn icons { + //don't check too often! give worker thread some time to fetch data + newLoad.clear(); - if (fileIconsAreEnabled) //don't check too often! give worker thread some time to fetch data + if (iconBuffer_.get()) { const CustomGridTableRim* gridDataTable = getGridDataTableRim(); if (!gridDataTable) return; - const VisibleRowRange rowsOnScreen = getVisibleRows(); const int totalCols = const_cast<CustomGridTableRim*>(gridDataTable)->GetNumberCols(); const int totalRows = const_cast<CustomGridTableRim*>(gridDataTable)->GetNumberRows(); + //determine column + const int colFilename = [&]() -> int + { + for (int k = 0; k < totalCols; ++k) + if (gridDataTable->getTypeAtPos(k) == xmlAccess::FILENAME) + return k; + return -1; + }(); + if (colFilename < 0) + return; + + const auto rowsOnScreen = getVisibleRows(); + //loop over all visible rows const int firstRow = static_cast<int>(rowsOnScreen.first); - const int lastRow = std::min(int(rowsOnScreen.second), totalRows - 1); - const int rowNo = lastRow - firstRow + 1; + const int rowNo = std::min(static_cast<int>(rowsOnScreen.second), totalRows) - firstRow; - for (int i = 0; i < rowNo; ++i) + for (int i = 0; i < rowNo; ++i) { - //alternate when adding rows: first, last, first + 1, last - 1 ... - const int currentRow = i % 2 == 0 ? - firstRow + i / 2 : - lastRow - (i - 1) / 2; + //alternate when adding rows: first, last, first + 1, last - 1 ... -> Icon buffer will then load reversely, i.e. from inside out + const int currentRow = firstRow + (i % 2 == 0 ? + i / 2 : + rowNo - 1 - (i - 1) / 2); - LoadSuccess::const_iterator j = loadIconSuccess.find(currentRow); - if (j != loadIconSuccess.end() && j->second == false) //find failed attempts to load icon + if (failedLoads.find(currentRow) != failedLoads.end()) //find failed attempts to load icon { const Zstring fileName = gridDataTable->getIconFile(currentRow); if (!fileName.empty()) { //test if they are already loaded in buffer: - if (zen::IconBuffer::getInstance().requestFileIcon(fileName)) + if (iconBuffer_->requestFileIcon(fileName)) { //exists in buffer: refresh Row - for (int k = 0; k < totalCols; ++k) - if (gridDataTable->getTypeAtPos(k) == xmlAccess::FILENAME) - { - RefreshCell(currentRow, k); - break; - } + RefreshCell(currentRow, colFilename); //do a *full* refresh for *every* failed load to update partial DC updates while scrolling + failedLoads.erase(currentRow); // } else //not yet in buffer: mark for async. loading { @@ -1755,7 +1761,7 @@ IconUpdater::IconUpdater(CustomGridLeft* leftGrid, CustomGridRight* rightGrid) : } -IconUpdater::~IconUpdater() {} //non-inline destructor for std::auto_ptr to work with forward declaration +IconUpdater::~IconUpdater() {} void IconUpdater::loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons @@ -1769,7 +1775,8 @@ void IconUpdater::loadIconsAsynchronously(wxEvent& event) //loads all (not yet) //merge vectors newLoad.insert(newLoad.end(), iconsLeft.begin(), iconsLeft.end()); - zen::IconBuffer::getInstance().setWorkload(newLoad); + if (m_leftGrid->iconBuffer_.get()) + m_leftGrid->iconBuffer_->setWorkload(newLoad); //event.Skip(); } @@ -1930,7 +1937,7 @@ CustomGridMiddle::CustomGridMiddle(wxWindow* parent, } -CustomGridMiddle::~CustomGridMiddle() {} //non-inline destructor for std::auto_ptr to work with forward declaration +CustomGridMiddle::~CustomGridMiddle() {} bool CustomGridMiddle::CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode) @@ -2039,33 +2046,33 @@ void CustomGridMiddle::showToolTip(int rowNumber, wxPoint pos) switch (syncOp) { case SO_CREATE_NEW_LEFT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("syncCreateLeftAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("createLeft"))); break; case SO_CREATE_NEW_RIGHT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("syncCreateRightAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("createRight"))); break; case SO_DELETE_LEFT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("syncDeleteLeftAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("deleteLeft"))); break; case SO_DELETE_RIGHT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("syncDeleteRightAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("deleteRight"))); break; case SO_OVERWRITE_LEFT: case SO_COPY_METADATA_TO_LEFT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("syncDirLeftAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("updateLeft"))); break; case SO_OVERWRITE_RIGHT: case SO_COPY_METADATA_TO_RIGHT: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("syncDirRightAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("updateRight"))); break; case SO_DO_NOTHING: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("syncDirNoneAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("none"))); break; case SO_EQUAL: - toolTip->show(getDescription(syncOp), pos, &GlobalResources::instance().getImage(wxT("equalAct"))); + toolTip->show(getDescription(syncOp), pos, &GlobalResources::getImage(wxT("equal"))); break; case SO_UNRESOLVED_CONFLICT: - toolTip->show(fsObj->getSyncOpConflict(), pos, &GlobalResources::instance().getImage(wxT("conflictAct"))); + toolTip->show(fsObj->getSyncOpConflict(), pos, &GlobalResources::getImage(wxT("conflict"))); break; }; } @@ -2075,28 +2082,28 @@ void CustomGridMiddle::showToolTip(int rowNumber, wxPoint pos) switch (cmpRes) { case FILE_LEFT_SIDE_ONLY: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::instance().getImage(wxT("leftOnlyAct"))); + toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("leftOnly"))); break; case FILE_RIGHT_SIDE_ONLY: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::instance().getImage(wxT("rightOnlyAct"))); + toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("rightOnly"))); break; case FILE_LEFT_NEWER: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::instance().getImage(wxT("leftNewerAct"))); + toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("leftNewer"))); break; case FILE_RIGHT_NEWER: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::instance().getImage(wxT("rightNewerAct"))); + toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("rightNewer"))); break; case FILE_DIFFERENT: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::instance().getImage(wxT("differentAct"))); + toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("different"))); break; case FILE_EQUAL: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::instance().getImage(wxT("equalAct"))); + toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("equal"))); break; case FILE_DIFFERENT_METADATA: - toolTip->show(getDescription(cmpRes), pos, &GlobalResources::instance().getImage(wxT("conflictAct"))); + toolTip->show(getDescription(cmpRes), pos, &GlobalResources::getImage(wxT("conflict"))); break; case FILE_CONFLICT: - toolTip->show(fsObj->getCatConflict(), pos, &GlobalResources::instance().getImage(wxT("conflictAct"))); + toolTip->show(fsObj->getCatConflict(), pos, &GlobalResources::getImage(wxT("conflict"))); break; } } @@ -2259,16 +2266,14 @@ void GridCellRendererMiddle::Draw(wxGrid& grid, if (rowIsHighlighted && m_gridMiddle.highlightedPos == CustomGridMiddle::BLOCKPOS_CHECK_BOX) { - if (fsObj->isActive()) - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("checkboxTrueFocus")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - else - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("checkboxFalseFocus")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(fsObj->isActive() ? + wxT("checkboxTrueFocus") : + wxT("checkboxFalseFocus")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); } - //default - else if (fsObj->isActive()) - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("checkboxTrue")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - else - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("checkboxFalse")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + else //default + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(fsObj->isActive() ? + wxT("checkboxTrue") : + wxT("checkboxFalse")), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); //clean remaining block of rect that will receive image of checkbox/directions rectShrinked.SetWidth(rect.GetWidth() - CHECK_BOX_WIDTH); @@ -2288,13 +2293,13 @@ void GridCellRendererMiddle::Draw(wxGrid& grid, case CustomGridMiddle::BLOCKPOS_CHECK_BOX: break; case CustomGridMiddle::BLOCKPOS_LEFT: - dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(true, SYNC_DIR_LEFT)), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_LEFT)), rectShrinked, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); break; case CustomGridMiddle::BLOCKPOS_MIDDLE: - dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(true, SYNC_DIR_NONE)), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_NONE)), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; case CustomGridMiddle::BLOCKPOS_RIGHT: - dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(true, SYNC_DIR_RIGHT)), rectShrinked, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, getSyncOpImage(fsObj->testSyncOperation(SYNC_DIR_RIGHT)), rectShrinked, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); break; } else //default @@ -2308,26 +2313,26 @@ void GridCellRendererMiddle::Draw(wxGrid& grid, switch (fsObj->getCategory()) { case FILE_LEFT_SIDE_ONLY: - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("leftOnlySmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("leftOnlySmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; case FILE_RIGHT_SIDE_ONLY: - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("rightOnlySmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("rightOnlySmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; case FILE_LEFT_NEWER: - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("leftNewerSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("leftNewerSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; case FILE_RIGHT_NEWER: - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("rightNewerSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("rightNewerSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; case FILE_DIFFERENT: - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("differentSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("differentSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; case FILE_EQUAL: - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("equalSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("equalSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; case FILE_CONFLICT: case FILE_DIFFERENT_METADATA: - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("conflictSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("conflictSmall")), rectShrinked, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); break; } } @@ -2362,9 +2367,9 @@ void CustomGridMiddle::DrawColLabel(wxDC& dc, int col) const wxRect rect(GetColLeft(col), 0, GetColWidth(col), GetColLabelSize()); if (getGridDataTableMiddle()->syncPreviewIsActive()) - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("syncViewSmall")), rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("syncViewSmall")), rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); else - dc.DrawLabel(wxEmptyString, GlobalResources::instance().getImage(wxT("cmpViewSmall")), rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); + dc.DrawLabel(wxEmptyString, GlobalResources::getImage(wxT("cmpViewSmall")), rect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); } @@ -2373,25 +2378,25 @@ const wxBitmap& zen::getSyncOpImage(SyncOperation syncOp) switch (syncOp) //evaluate comparison result and sync direction { case SO_CREATE_NEW_LEFT: - return GlobalResources::instance().getImage(wxT("createLeftSmall")); + return GlobalResources::getImage(wxT("createLeftSmall")); case SO_CREATE_NEW_RIGHT: - return GlobalResources::instance().getImage(wxT("createRightSmall")); + return GlobalResources::getImage(wxT("createRightSmall")); case SO_DELETE_LEFT: - return GlobalResources::instance().getImage(wxT("deleteLeftSmall")); + return GlobalResources::getImage(wxT("deleteLeftSmall")); case SO_DELETE_RIGHT: - return GlobalResources::instance().getImage(wxT("deleteRightSmall")); + return GlobalResources::getImage(wxT("deleteRightSmall")); case SO_OVERWRITE_RIGHT: case SO_COPY_METADATA_TO_RIGHT: - return GlobalResources::instance().getImage(wxT("syncDirRightSmall")); + return GlobalResources::getImage(wxT("updateRightSmall")); case SO_OVERWRITE_LEFT: case SO_COPY_METADATA_TO_LEFT: - return GlobalResources::instance().getImage(wxT("syncDirLeftSmall")); + return GlobalResources::getImage(wxT("updateLeftSmall")); case SO_DO_NOTHING: - return GlobalResources::instance().getImage(wxT("syncDirNoneSmall")); + return GlobalResources::getImage(wxT("noneSmall")); case SO_EQUAL: - return GlobalResources::instance().getImage(wxT("equalSmall")); + return GlobalResources::getImage(wxT("equalSmall")); case SO_UNRESOLVED_CONFLICT: - return GlobalResources::instance().getImage(wxT("conflictSmall")); + return GlobalResources::getImage(wxT("conflictSmall")); } return wxNullBitmap; //dummy diff --git a/library/custom_grid.h b/library/custom_grid.h index 6b577011..a1bce692 100644 --- a/library/custom_grid.h +++ b/library/custom_grid.h @@ -10,10 +10,10 @@ #include <vector> #include <wx/grid.h> #include "process_xml.h" -#include <map> #include <memory> #include <set> #include "../file_hierarchy.h" +#include "icon_buffer.h" class CustomGridTable; @@ -75,20 +75,21 @@ public: //set sort direction indicator on UI typedef int SortColumn; + //notify wxGrid that underlying table size has changed + virtual void updateGridSizes(); + enum SortDirection { ASCENDING, DESCENDING }; - - //notify wxGrid that underlying table size has changed - virtual void updateGridSizes(); - typedef std::pair<SortColumn, SortDirection> SortMarker; void setSortMarker(SortMarker marker); bool isLeadGrid() const; + void setIconManager(const std::shared_ptr<zen::IconBuffer>& iconBuffer); + protected: void RefreshCell(int row, int col); virtual void DrawColLabel(wxDC& dc, int col); @@ -105,6 +106,7 @@ private: virtual void alignOtherGrids(CustomGrid* gridLeft, CustomGrid* gridMiddle, CustomGrid* gridRight) = 0; void adjustGridHeights(wxEvent& event); + virtual void enableFileIcons(const std::shared_ptr<zen::IconBuffer>& iconBuffer) = 0; CustomGrid* m_gridLeft; CustomGrid* m_gridMiddle; @@ -116,7 +118,6 @@ private: }; -template <bool showFileIcons> class GridCellRenderer; @@ -125,7 +126,7 @@ class IconUpdater : private wxEvtHandler //update file icons periodically: use S { public: IconUpdater(CustomGridLeft* leftGrid, CustomGridRight* rightGrid); - ~IconUpdater(); //non-inline destructor for std::auto_ptr to work with forward declaration + ~IconUpdater(); private: void loadIconsAsynchronously(wxEvent& event); //loads all (not yet) drawn icons @@ -133,7 +134,7 @@ private: CustomGridRim* m_leftGrid; CustomGridRim* m_rightGrid; - std::auto_ptr<wxTimer> m_timer; //user timer event to periodically update icons: better than idle event because also active when scrolling! :) + std::unique_ptr<wxTimer> m_timer; //user timer event to periodically update icons: better than idle event because also active when scrolling! :) }; @@ -141,7 +142,6 @@ private: class CustomGridRim : public CustomGrid { friend class IconUpdater; - template <bool showFileIcons> friend class GridCellRenderer; public: @@ -162,8 +162,6 @@ public: void autoSizeColumns(); //performance optimized column resizer - void enableFileIcons(const bool value); - virtual void updateGridSizes(); protected: @@ -174,6 +172,7 @@ protected: private: CustomGridTableRim* getGridDataTableRim() const; + virtual void enableFileIcons(const std::shared_ptr<zen::IconBuffer>& iconBuffer); void OnResizeColumn(wxGridSizeEvent& event); @@ -183,18 +182,15 @@ private: //asynchronous icon loading void getIconsToBeLoaded(std::vector<Zstring>& newLoad); //loads all (not yet) drawn icons - typedef size_t FromRow; - typedef size_t ToRow; - typedef std::pair<FromRow, ToRow> VisibleRowRange; - VisibleRowRange getVisibleRows(); - + typedef size_t RowBegin; + typedef size_t RowEnd; + std::pair<RowBegin, RowEnd> getVisibleRows(); //return [first, last) number pair typedef size_t RowNumber; - typedef bool IconLoaded; - typedef std::map<RowNumber, IconLoaded> LoadSuccess; - LoadSuccess loadIconSuccess; //save status of last icon load when drawing on GUI + typedef std::set<RowNumber> FailedIconLoad; + FailedIconLoad failedLoads; //save status of last icon load when drawing on GUI - bool fileIconsAreEnabled; + std::shared_ptr<zen::IconBuffer> iconBuffer_; xmlAccess::ColumnAttributes columnSettings; //set visibility, position and width of columns CustomGridRim* otherGrid; //sibling grid on other side @@ -259,7 +255,7 @@ public: long style = wxWANTS_CHARS, const wxString& name = wxGridNameStr); - ~CustomGridMiddle(); //non-inline destructor for std::auto_ptr to work with forward declaration + ~CustomGridMiddle(); virtual bool CreateGrid(int numRows, int numCols, wxGrid::wxGridSelectionModes selmode = wxGrid::wxGridSelectCells); @@ -274,6 +270,7 @@ private: virtual CustomGridTable* getGridDataTable() const; CustomGridTableMiddle* getGridDataTableMiddle() const; + virtual void enableFileIcons(const std::shared_ptr<zen::IconBuffer>& iconBuffer) {}; #ifdef FFS_WIN //get rid of scrollbars; Windows: overwrite virtual method virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh = true); #endif @@ -308,7 +305,7 @@ private: int highlightedRow; BlockPosition highlightedPos; - std::auto_ptr<CustomTooltip> toolTip; + std::unique_ptr<CustomTooltip> toolTip; }; //custom events for middle grid: diff --git a/library/db_file.cpp b/library/db_file.cpp index 994e8579..268e411e 100644 --- a/library/db_file.cpp +++ b/library/db_file.cpp @@ -48,7 +48,7 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) Zstring dbname = Zstring(Zstr(".sync")) + (tempfile ? Zstr(".tmp") : Zstr("")) + SYNC_DB_FILE_ENDING; #endif - return baseMap.getBaseDir<side>() + dbname; + return baseMap.getBaseDirPf<side>() + dbname; } @@ -56,12 +56,12 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false) class FileInputStreamDB : public FileInputStream { public: - FileInputStreamDB(const Zstring& filename) : //throw (FileError) + FileInputStreamDB(const Zstring& filename) : //throw FileError FileInputStream(filename) { //read FreeFileSync file identifier char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {}; - Read(formatDescr, sizeof(formatDescr)); //throw (FileError) + Read(formatDescr, sizeof(formatDescr)); //throw FileError if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr)) throw FileError(_("Incompatible synchronization database format:") + " \n" + "\"" + filename + "\""); @@ -74,11 +74,11 @@ private: class FileOutputStreamDB : public FileOutputStream { public: - FileOutputStreamDB(const Zstring& filename) : //throw (FileError) + FileOutputStreamDB(const Zstring& filename) : //throw FileError FileOutputStream(filename) { //write FreeFileSync file identifier - Write(FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); //throw (FileError) + Write(FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); //throw FileError } private: @@ -164,7 +164,7 @@ typedef std::map<UniqueId, MemoryStreamPtr> StreamMapping; //list of streams class ReadFileStream : public zen::ReadInputStream { public: - ReadFileStream(wxInputStream& stream, const wxString& filename, StreamMapping& streamList, bool leftSide) : ReadInputStream(stream, filename) + ReadFileStream(wxInputStream& stream, const wxString& filename, StreamMapping& streamList) : ReadInputStream(stream, filename) { //|------------------------------------------------------------------------------------- //| ensure 32/64 bit portability: used fixed size data types only e.g. boost::uint32_t | @@ -172,69 +172,28 @@ public: boost::int32_t version = readNumberC<boost::int32_t>(); -#ifndef _MSC_VER -#warning remove this check after migration! -#endif - if (version != 6) //migrate! - - if (version != FILE_FORMAT_VER) //read file format version - throw FileError(_("Incompatible synchronization database format:") + " \n" + "\"" + filename.c_str() + "\""); + if (version != FILE_FORMAT_VER) //read file format version + throw FileError(_("Incompatible synchronization database format:") + " \n" + "\"" + filename.c_str() + "\""); + streamList.clear(); -#ifndef _MSC_VER -#warning remove this case after migration! -#endif - - if (version == 6) + boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair + while (dbCount-- != 0) { - streamList.clear(); + //DB id of partner databases + const CharArray tmp2 = readArrayC(); + const std::string sessionID(tmp2->begin(), tmp2->end()); - //read DB id - const CharArray tmp = readArrayC(); - std::string mainId(tmp->begin(), tmp->end()); + CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) - boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair - while (dbCount-- != 0) - { - //DB id of partner databases - const CharArray tmp2 = readArrayC(); - const std::string partnerID(tmp2->begin(), tmp2->end()); - - CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) - - if (leftSide) - streamList.insert(std::make_pair(partnerID + mainId, buffer)); - else - streamList.insert(std::make_pair(mainId + partnerID, buffer)); - } - } - else - { - streamList.clear(); - - boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair - while (dbCount-- != 0) - { - //DB id of partner databases - const CharArray tmp2 = readArrayC(); - const std::string sessionID(tmp2->begin(), tmp2->end()); - - CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation) - - streamList.insert(std::make_pair(sessionID, buffer)); - } + streamList.insert(std::make_pair(sessionID, buffer)); } } }; namespace { -StreamMapping loadStreams(const Zstring& filename, - -#ifndef _MSC_VER -#warning remove this parameter after migration! -#endif - bool leftSide) //throw (FileError) +StreamMapping loadStreams(const Zstring& filename) //throw FileError { if (!zen::fileExists(filename)) throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + " \n\n" + @@ -244,12 +203,12 @@ StreamMapping loadStreams(const Zstring& filename, try { //read format description (uncompressed) - FileInputStreamDB uncompressed(filename); //throw (FileError) + FileInputStreamDB uncompressed(filename); //throw FileError wxZlibInputStream input(uncompressed, wxZLIB_ZLIB); StreamMapping streamList; - ReadFileStream(input, toWx(filename), streamList, leftSide); + ReadFileStream(input, toWx(filename), streamList); return streamList; } catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file @@ -277,14 +236,14 @@ DirInfoPtr parseStream(const std::vector<char>& stream, const Zstring& fileName) } -std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw (FileError) +std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw FileError { const Zstring fileNameLeft = getDBFilename<LEFT_SIDE>(baseMapping); const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseMapping); //read file data: list of session ID + DirInfo-stream - const StreamMapping streamListLeft = ::loadStreams(fileNameLeft, true); //throw (FileError) - const StreamMapping streamListRight = ::loadStreams(fileNameRight, false); //throw (FileError) + const StreamMapping streamListLeft = ::loadStreams(fileNameLeft); //throw FileError + const StreamMapping streamListRight = ::loadStreams(fileNameRight); //throw FileError //find associated session: there can be at most one session within intersection of left and right ids StreamMapping::const_iterator streamLeft = streamListLeft .end(); @@ -484,11 +443,11 @@ public: //save/load DirContainer -void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw (FileError) +void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw FileError { { //write format description (uncompressed) - FileOutputStreamDB uncompressed(filename); //throw (FileError) + FileOutputStreamDB uncompressed(filename); //throw FileError wxZlibOutputStream output(uncompressed, 4, wxZLIB_ZLIB); /* 4 - best compromise between speed and compression: (scanning 200.000 objects) @@ -516,7 +475,7 @@ bool equalEntry(const MemoryStreamPtr& lhs, const MemoryStreamPtr& rhs) } -void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) +void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw FileError { //transactional behaviour! write to tmp files first const Zstring dbNameLeftTmp = getDBFilename<LEFT_SIDE >(baseMapping, true); @@ -527,7 +486,7 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) //delete old tmp file, if necessary -> throws if deletion fails! removeFile(dbNameLeftTmp); // - removeFile(dbNameRightTmp); //throw (FileError) + removeFile(dbNameRightTmp); //throw FileError //(try to) load old database files... StreamMapping streamListLeft; @@ -535,14 +494,14 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) try //read file data: list of session ID + DirInfo-stream { - streamListLeft = ::loadStreams(dbNameLeft, true); + streamListLeft = ::loadStreams(dbNameLeft); } - catch(FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! + catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! try { - streamListRight = ::loadStreams(dbNameRight, false); + streamListRight = ::loadStreams(dbNameRight); } - catch(FileError&) {} + catch (FileError&) {} //find associated session: there can be at most one session within intersection of left and right ids StreamMapping::iterator streamLeft = streamListLeft .end(); @@ -572,7 +531,7 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) oldDirInfoRight = parseStream(*streamRight->second, dbNameRight); //throw FileError } } - catch(FileError&) + catch (FileError&) { //if error occurs: just overwrite old file! User is already informed about issues right after comparing! oldDirInfoLeft .reset(); //read both or none! @@ -622,17 +581,17 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw (FileError) //write (temp-) files... Loki::ScopeGuard guardTempFileLeft = Loki::MakeGuard(&zen::removeFile, dbNameLeftTmp); - saveFile(streamListLeft, dbNameLeftTmp); //throw (FileError) + saveFile(streamListLeft, dbNameLeftTmp); //throw FileError Loki::ScopeGuard guardTempFileRight = Loki::MakeGuard(&zen::removeFile, dbNameRightTmp); - saveFile(streamListRight, dbNameRightTmp); //throw (FileError) + saveFile(streamListRight, dbNameRightTmp); //throw FileError //operation finished: rename temp files -> this should work transactionally: //if there were no write access, creation of temp files would have failed removeFile(dbNameLeft); removeFile(dbNameRight); - renameFile(dbNameLeftTmp, dbNameLeft); //throw (FileError); - renameFile(dbNameRightTmp, dbNameRight); //throw (FileError); + renameFile(dbNameLeftTmp, dbNameLeft); //throw FileError; + renameFile(dbNameRightTmp, dbNameRight); //throw FileError; guardTempFileLeft. Dismiss(); //no need to delete temp file anymore guardTempFileRight.Dismiss(); // diff --git a/library/db_file.h b/library/db_file.h index dc9eb141..188d7d92 100644 --- a/library/db_file.h +++ b/library/db_file.h @@ -14,7 +14,7 @@ namespace zen { const Zstring SYNC_DB_FILE_ENDING = Zstr(".ffs_db"); -void saveToDisk(const BaseDirMapping& baseMapping); //throw (FileError) +void saveToDisk(const BaseDirMapping& baseMapping); //throw FileError struct DirInformation { @@ -25,7 +25,7 @@ typedef std::shared_ptr<const DirInformation> DirInfoPtr; DEFINE_NEW_FILE_ERROR(FileErrorDatabaseNotExisting); -std::pair<DirInfoPtr, DirInfoPtr> loadFromDisk(const BaseDirMapping& baseMapping); //throw (FileError, FileErrorDatabaseNotExisting) -> return value always bound! +std::pair<DirInfoPtr, DirInfoPtr> loadFromDisk(const BaseDirMapping& baseMapping); //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! } #endif // DBFILE_H_INCLUDED diff --git a/library/dir_exist_async.h b/library/dir_exist_async.h index 60fa4582..cab82aa2 100644 --- a/library/dir_exist_async.h +++ b/library/dir_exist_async.h @@ -21,7 +21,9 @@ bool dirExistsUpdating(const Zstring& dirname, ProcessCallback& procCallback) { using namespace util; - //do NOT include info messages here! this method is used by synchronization also, so avoid flooding! + std::wstring statusText = _("Searching for directory %x..."); + replace(statusText, L"%x", std::wstring(L"\"") + dirname + L"\"", false); + procCallback.reportStatus(statusText); auto ft = dirExistsAsync(dirname); while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL))) diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp index f653fa7c..7feb51ff 100644 --- a/library/dir_lock.cpp +++ b/library/dir_lock.cpp @@ -17,6 +17,7 @@ #include "../shared/serialize.h" #include "../shared/build_info.h" #include "../shared/int64.h" +#include "../shared/file_handling.h" #ifdef FFS_WIN #include <tlhelp32.h> @@ -24,7 +25,6 @@ #include "../shared/long_path_prefix.h" #elif defined FFS_LINUX -#include "../shared/file_handling.h" #include <sys/stat.h> #include <cerrno> #include <unistd.h> @@ -78,17 +78,15 @@ public: //although CreateFile/FILE_APPEND_DATA without SetFilePointerEx works locally, it MAY NOT work on some network shares creating a 4 gig file!!! const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename_.c_str()).c_str(), - FILE_GENERIC_WRITE, + GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp FILE_SHARE_READ, - NULL, + 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle == INVALID_HANDLE_VALUE) return; - - Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, fileHandle); - (void)dummy; //silence warning "unused variable" + LOKI_ON_BLOCK_EXIT2(::CloseHandle(fileHandle)); const LARGE_INTEGER moveDist = {}; if (!::SetFilePointerEx(fileHandle, //__in HANDLE hFile, @@ -108,9 +106,7 @@ public: const int fileHandle = ::open(lockfilename_.c_str(), O_WRONLY | O_APPEND); //O_EXCL contains a race condition on NFS file systems: http://linux.die.net/man/2/open if (fileHandle == -1) return; - - Loki::ScopeGuard dummy = Loki::MakeGuard(::close, fileHandle); - (void)dummy; //silence warning "unused variable" + LOKI_ON_BLOCK_EXIT2(::close(fileHandle)); const ssize_t bytesWritten = ::write(fileHandle, buffer, 1); (void)bytesWritten; @@ -124,20 +120,7 @@ private: namespace { -void deleteLockFile(const Zstring& filename) //throw (FileError) -{ -#ifdef FFS_WIN - if (!::DeleteFile(applyLongPathPrefix(filename).c_str())) -#elif defined FFS_LINUX - if (::unlink(filename.c_str()) != 0) -#endif - { - throw FileError(_("Error deleting file:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); - } -} - - -zen::UInt64 getLockFileSize(const Zstring& filename) //throw (FileError, ErrorNotExisting) +UInt64 getLockFileSize(const Zstring& filename) //throw FileError, ErrorNotExisting { #ifdef FFS_WIN WIN32_FIND_DATA fileInfo = {}; @@ -192,13 +175,13 @@ namespace { //read string from file stream inline -std::string readString(wxInputStream& stream) //throw (std::exception) +std::string readString(wxInputStream& stream) //throw std::exception { - const boost::uint32_t strLength = readPOD<boost::uint32_t>(stream); + const auto strLength = readPOD<boost::uint32_t>(stream); std::string output; if (strLength > 0) { - output.resize(strLength); //may throw for corrupted data + output.resize(strLength); //throw std::bad_alloc stream.Read(&output[0], strLength); } return output; @@ -208,9 +191,8 @@ std::string readString(wxInputStream& stream) //throw (std::exception) inline void writeString(wxOutputStream& stream, const std::string& str) //write string to filestream { - const boost::uint32_t strLength = static_cast<boost::uint32_t>(str.length()); - writePOD<boost::uint32_t>(stream, strLength); - stream.Write(str.c_str(), strLength); + writePOD(stream, static_cast<boost::uint32_t>(str.length())); + stream.Write(str.c_str(), str.length()); } @@ -242,7 +224,7 @@ struct LockInformation LockInformation(wxInputStream& stream) //read { char formatDescr[sizeof(LOCK_FORMAT_DESCR)] = {}; - stream.Read(formatDescr, sizeof(LOCK_FORMAT_DESCR)); //file format header + stream.Read(formatDescr, sizeof(LOCK_FORMAT_DESCR)); //file format header const int lockFileVersion = readPOD<boost::int32_t>(stream); // (void)lockFileVersion; @@ -286,6 +268,7 @@ enum ProcessStatus { PROC_STATUS_NOT_RUNNING, PROC_STATUS_RUNNING, + PROC_STATUS_ITS_US, PROC_STATUS_NO_IDEA }; ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDescr) @@ -295,15 +278,16 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe return PROC_STATUS_NO_IDEA; //lock owned by different computer #ifdef FFS_WIN + if (procDescr.processId == ::GetCurrentProcessId()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running + return PROC_STATUS_ITS_US; + //note: ::OpenProcess() is no option as it may successfully return for crashed processes! HANDLE snapshot = ::CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, //__in DWORD dwFlags, 0); //__in DWORD th32ProcessID if (snapshot == INVALID_HANDLE_VALUE) return PROC_STATUS_NO_IDEA; - - Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, snapshot); - (void)dummy; //silence warning "unused variable" + LOKI_ON_BLOCK_EXIT2(::CloseHandle(snapshot)); PROCESSENTRY32 processEntry = {}; processEntry.dwSize = sizeof(processEntry); @@ -316,11 +300,14 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe if (processEntry.th32ProcessID == procDescr.processId) return PROC_STATUS_RUNNING; //process still running } - while(::Process32Next(snapshot, &processEntry)); + while (::Process32Next(snapshot, &processEntry)); return PROC_STATUS_NOT_RUNNING; #elif defined FFS_LINUX + if (procDescr.processId == ::getpid()) //may seem obscure, but it's possible: a lock file is "stolen" and put back while the program is running + return PROC_STATUS_ITS_US; + if (procDescr.processId <= 0 || procDescr.processId >= 65536) return PROC_STATUS_NO_IDEA; //invalid process id @@ -329,30 +316,30 @@ ProcessStatus getProcessStatus(const LockInformation::ProcessDescription& procDe } -void writeLockInfo(const Zstring& lockfilename) //throw (FileError) +void writeLockInfo(const Zstring& lockfilename) //throw FileError { //write GUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, distributed network, etc.) - FileOutputStream lockFile(lockfilename); //throw (FileError) + FileOutputStream lockFile(lockfilename); //throw FileError LockInformation().toStream(lockFile); } -LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting) +LockInformation retrieveLockInfo(const Zstring& lockfilename) //throw FileError, ErrorNotExisting { //read GUID from beginning of file - FileInputStream lockFile(lockfilename); //throw (FileError, ErrorNotExisting) + FileInputStream lockFile(lockfilename); //throw FileError, ErrorNotExisting return LockInformation(lockFile); } -std::string retrieveLockId(const Zstring& lockfilename) //throw (FileError, ErrorNotExisting) +std::string retrieveLockId(const Zstring& lockfilename) //throw FileError, ErrorNotExisting { return retrieveLockInfo(lockfilename).lockId; } } -void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError) +void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError { std::wstring infoMsg = _("Waiting while directory is locked (%x)..."); replace(infoMsg, L"%x", std::wstring(L"\"") + lockfilename + "\""); @@ -361,15 +348,26 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr //--------------------------------------------------------------- try { - const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw (FileError, ErrorNotExisting) - const bool lockOwnderDead = getProcessStatus(lockInfo.procDescr) == PROC_STATUS_NOT_RUNNING; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec + const LockInformation lockInfo = retrieveLockInfo(lockfilename); //throw FileError, ErrorNotExisting + + bool lockOwnderDead = false; //convenience optimization: if we know the owning process crashed, we needn't wait DETECT_EXITUS_INTERVAL sec + switch (getProcessStatus(lockInfo.procDescr)) + { + case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process + case PROC_STATUS_NOT_RUNNING: + lockOwnderDead = true; + break; + case PROC_STATUS_RUNNING: + case PROC_STATUS_NO_IDEA: + break; + } zen::UInt64 fileSizeOld; wxLongLong lockSilentStart = wxGetLocalTimeMillis(); while (true) { - const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw (FileError, ErrorNotExisting) + const zen::UInt64 fileSizeNew = ::getLockFileSize(lockfilename); //throw FileError, ErrorNotExisting wxLongLong currentTime = wxGetLocalTimeMillis(); if (fileSizeNew != fileSizeOld) @@ -382,17 +380,17 @@ void waitOnDirLock(const Zstring& lockfilename, DirLockCallback* callback) //thr if (lockOwnderDead || //no need to wait any longer... currentTime - lockSilentStart > DETECT_EXITUS_INTERVAL) { - DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw (FileError) + DirLock dummy(deleteAbandonedLockName(lockfilename), callback); //throw FileError //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! - if (retrieveLockId(lockfilename) != lockInfo.lockId) //throw (FileError, ErrorNotExisting) + if (retrieveLockId(lockfilename) != lockInfo.lockId) //throw FileError, ErrorNotExisting return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... - if (getLockFileSize(lockfilename) != fileSizeOld) //throw (FileError, ErrorNotExisting) + if (getLockFileSize(lockfilename) != fileSizeOld) //throw FileError, ErrorNotExisting continue; //belated lifesign - ::deleteLockFile(lockfilename); //throw (FileError) + removeFile(lockfilename); //throw FileError return; } @@ -432,24 +430,28 @@ void releaseLock(const Zstring& lockfilename) //throw () { try { - ::deleteLockFile(lockfilename); + removeFile(lockfilename); } - catch(...) {} + catch (...) {} } -bool tryLock(const Zstring& lockfilename) //throw (FileError) +bool tryLock(const Zstring& lockfilename) //throw FileError { #ifdef FFS_WIN const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(lockfilename).c_str(), - GENERIC_WRITE, + GENERIC_READ | GENERIC_WRITE, //use both when writing over network, see comment in file_io.cpp FILE_SHARE_READ, - NULL, + 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { +#ifndef _MSC_VER +#warning fix this problem! + //read-only FTP may return: ERROR_FILE_EXISTS (NetDrive @ GNU) +#endif if (::GetLastError() == ERROR_FILE_EXISTS) return false; else @@ -471,10 +473,10 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError) ::close(fileHandle); #endif - Loki::ScopeGuard guardLockFile = Loki::MakeGuard(::releaseLock, lockfilename); + Loki::ScopeGuard guardLockFile = Loki::MakeGuard(zen::removeFile, lockfilename); //write UUID at the beginning of the file: this ID is a universal identifier for this lock (no matter what the path is, considering symlinks, etc.) - writeLockInfo(lockfilename); //throw (FileError) + writeLockInfo(lockfilename); //throw FileError guardLockFile.Dismiss(); //lockfile created successfully return true; @@ -485,10 +487,10 @@ bool tryLock(const Zstring& lockfilename) //throw (FileError) class DirLock::SharedDirLock { public: - SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL) : //throw (FileError) + SharedDirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL) : //throw FileError lockfilename_(lockfilename) { - while (!::tryLock(lockfilename)) //throw (FileError) + while (!::tryLock(lockfilename)) //throw FileError ::waitOnDirLock(lockfilename, callback); // threadObj = boost::thread(LifeSigns(lockfilename)); @@ -522,7 +524,7 @@ public: } //create or retrieve a SharedDirLock - std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError) + std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError { //optimization: check if there is an active(!) lock for "lockfilename" FileToUuidMap::const_iterator iterUuid = fileToUuid.find(lockfilename); @@ -535,7 +537,7 @@ public: try //actual check based on lock UUID, deadlock prevention: "lockfilename" may be an alternative name for an already active lock { - const std::string lockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting) + const std::string lockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting const std::shared_ptr<SharedDirLock>& activeLock = findActive(lockId); //returns null-lock if not found if (activeLock) @@ -544,11 +546,11 @@ public: return activeLock; } } - catch (const ErrorNotExisting&) {} //let other FileError(s) propagate! + catch (...) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock file //not yet in buffer, so create a new directory lock - std::shared_ptr<SharedDirLock> newLock(new SharedDirLock(lockfilename, callback)); //throw (FileError) - const std::string newLockId = retrieveLockId(lockfilename); //throw (FileError, ErrorNotExisting) + std::shared_ptr<SharedDirLock> newLock(new SharedDirLock(lockfilename, callback)); //throw FileError + const std::string newLockId = retrieveLockId(lockfilename); //throw FileError, ErrorNotExisting //update registry fileToUuid[lockfilename] = newLockId; //throw() @@ -579,7 +581,7 @@ private: }; -DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw (FileError) +DirLock::DirLock(const Zstring& lockfilename, DirLockCallback* callback) //throw FileError { #ifdef FFS_WIN std::vector<wchar_t> volName(std::max(lockfilename.size(), static_cast<size_t>(10000))); diff --git a/library/dir_lock.h b/library/dir_lock.h index f4720e61..8cec9d69 100644 --- a/library/dir_lock.h +++ b/library/dir_lock.h @@ -24,7 +24,7 @@ RAII structure to place a directory lock against other FFS processes: class DirLock { public: - DirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL); //throw (FileError), callback only used during construction + DirLock(const Zstring& lockfilename, DirLockCallback* callback = NULL); //throw FileError, callback only used during construction private: class LockAdmin; diff --git a/library/hard_filter.cpp b/library/hard_filter.cpp index c6d18f47..56fb091b 100644 --- a/library/hard_filter.cpp +++ b/library/hard_filter.cpp @@ -63,7 +63,7 @@ void addFilterEntry(const Zstring& filtername, std::set<Zstring>& fileFilter, st #ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case - MakeUpper(filterFormatted); + makeUpper(filterFormatted); #elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case: nothing to do here #endif @@ -133,19 +133,19 @@ bool matchesMask(const Zchar* str, const Zchar* mask) { switch (ch) { - case Zchar('?'): + case Zstr('?'): if (*str == 0) return false; break; - case Zchar('*'): + case Zstr('*'): //advance to next non-*/? char do { ++mask; ch = *mask; } - while (ch == Zchar('*') || ch == Zchar('?')); + while (ch == Zstr('*') || ch == Zstr('?')); //if mask ends with '*': if (ch == 0) return true; @@ -178,10 +178,10 @@ bool matchesMaskBegin(const Zchar* str, const Zchar* mask) switch (ch) { - case Zchar('?'): + case Zstr('?'): break; - case Zchar('*'): + case Zstr('*'): return true; default: @@ -273,7 +273,7 @@ bool NameFilter::passFileFilter(const Zstring& relFilename) const { #ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case Zstring nameFormatted = relFilename; - MakeUpper(nameFormatted); + makeUpper(nameFormatted); #elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case const Zstring& nameFormatted = relFilename; //nothing to do here #endif @@ -289,7 +289,7 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch #ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case Zstring nameFormatted = relDirname; - MakeUpper(nameFormatted); + makeUpper(nameFormatted); #elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case const Zstring& nameFormatted = relDirname; //nothing to do here #endif diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp index 2c17f6da..b8ee66cd 100644 --- a/library/icon_buffer.cpp +++ b/library/icon_buffer.cpp @@ -5,19 +5,16 @@ // ************************************************************************** #include "icon_buffer.h" -#include <wx/msgdlg.h> -#include <map> -#include <vector> #include <queue> #include <set> -#include <wx/log.h> -#include "../shared/i18n.h" #include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> #include "../shared/loki/ScopeGuard.h" #include <boost/thread/once.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" +#include "../shared/dll_loader.h" +#include "../shared/Thumbnail/thumbnail.h" #elif defined FFS_LINUX #include <giomm/file.h> @@ -25,50 +22,32 @@ #include <gtkmm/main.h> #endif - using namespace zen; -const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer - - -#ifdef FFS_WIN -Zstring getFileExtension(const Zstring& filename) -{ - const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! - - return shortName.find(Zchar('.')) != Zstring::npos ? - filename.AfterLast(Zchar('.')) : - Zstring(); -} +const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer -namespace -{ -std::set<Zstring, LessFilename> exceptions; //thread-safe! -boost::once_flag once = BOOST_ONCE_INIT; // -} -//test for extension for icons that physically have to be retrieved from disc -bool isPriceyExtension(const Zstring& extension) +int zen::IconBuffer::cvrtSize(IconSize sz) //get size in pixel { - boost::call_once(once, []() + switch (sz) { - exceptions.insert(L"exe"); - exceptions.insert(L"lnk"); - exceptions.insert(L"ico"); - exceptions.insert(L"ani"); - exceptions.insert(L"cur"); - exceptions.insert(L"url"); - exceptions.insert(L"msc"); - exceptions.insert(L"scr"); - }); - - return exceptions.find(extension) != exceptions.end(); -} + case SIZE_SMALL: +#ifdef FFS_WIN + return 16; +#elif defined FFS_LINUX + return 24; #endif + case SIZE_MEDIUM: + return 48; + case SIZE_LARGE: + return 128; + } + assert(false); + return 0; +} -//################################################################################################################################################ class IconHolder //handle HICON/GdkPixbuf ownership WITHOUT ref-counting to allow thread-safe usage (in contrast to wxIcon) { public: @@ -107,7 +86,7 @@ public: void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() - wxIcon toWxIcon() const //copy HandleType, caller needs to take ownership! + wxIcon toWxIcon(int expectedSize) const //copy HandleType, caller needs to take ownership! { if (handle_ == NULL) return wxNullIcon; @@ -116,8 +95,33 @@ public: wxIcon newIcon; //attention: wxIcon uses reference counting! #ifdef FFS_WIN - newIcon.SetHICON(clone.handle_); // - newIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); //icon is actually scaled to this size (just in case referenced HICON differs) + newIcon.SetHICON(clone.handle_); + + { //this block costs ~0.04 ms + ICONINFO icoInfo = {}; + if (::GetIconInfo(clone.handle_, &icoInfo)) + { + ::DeleteObject(icoInfo.hbmMask); //nice potential for a GDI leak! + LOKI_ON_BLOCK_EXIT2(::DeleteObject(icoInfo.hbmColor)); // + + BITMAP bmpInfo = {}; + if (::GetObject(icoInfo.hbmColor, //__in HGDIOBJ hgdiobj, + sizeof(BITMAP), //__in int cbBuffer, + &bmpInfo) != 0) // __out LPVOID lpvObject + { + const int maxExtent = std::max(bmpInfo.bmWidth, bmpInfo.bmHeight); + if (maxExtent > expectedSize) + { + bmpInfo.bmWidth = bmpInfo.bmWidth * expectedSize / maxExtent; //scale those Vista jumbo 256x256 icons down! + bmpInfo.bmHeight = bmpInfo.bmHeight * expectedSize / maxExtent; // + } + newIcon.SetSize(bmpInfo.bmWidth, bmpInfo.bmHeight); //wxIcon is stretched to this size + } + } + } + //no stretching for now + //newIcon.SetSize(defaultSize, defaultSize); //icon is stretched to this size if referenced HICON differs + #elif defined FFS_LINUX // newIcon.SetPixbuf(clone.handle_); // transfer ownership!! #endif // @@ -127,31 +131,206 @@ public: private: HandleType handle_; + struct ConversionToBool { int dummy; }; + +public: + //use member pointer as implicit conversion to bool (C++ Templates - Vandevoorde/Josuttis; chapter 20) + operator int ConversionToBool::* () const { return handle_ != NULL ? &ConversionToBool::dummy : NULL; } }; -IconHolder getAssociatedIcon(const Zstring& filename) -{ #ifdef FFS_WIN - //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" - //but no problem, directory formatting takes care that filenames are always absolute! +namespace +{ +Zstring getFileExtension(const Zstring& filename) +{ + const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! + + return shortName.find(Zchar('.')) != Zstring::npos ? + filename.AfterLast(Zchar('.')) : + Zstring(); +} + +std::set<Zstring, LessFilename> priceyExtensions; //thread-safe! +boost::once_flag initExtensionsOnce = BOOST_ONCE_INIT; // - SHFILEINFO fileInfo = {}; //initialize hIcon -> 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 +//test for extension for non-thumbnail icons that physically have to be retrieved from disc +bool isCheapExtension(const Zstring& extension) +{ + boost::call_once(initExtensionsOnce, []() + { + priceyExtensions.insert(L"exe"); + priceyExtensions.insert(L"lnk"); + priceyExtensions.insert(L"ico"); + priceyExtensions.insert(L"ani"); + priceyExtensions.insert(L"cur"); + priceyExtensions.insert(L"url"); + priceyExtensions.insert(L"msc"); + priceyExtensions.insert(L"scr"); + }); + return priceyExtensions.find(extension) == priceyExtensions.end(); +} + + +bool vistaOrLater() +{ + OSVERSIONINFO osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + //IFileOperation is supported with Vista and later + if (::GetVersionEx(&osvi)) + return osvi.dwMajorVersion > 5; + //XP has majorVersion == 5, minorVersion == 1 + //Vista has majorVersion == 6, minorVersion == 0 + //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx + return false; +} + + +bool wereVistaOrLater = false; +boost::once_flag initVistaFlagOnce = BOOST_ONCE_INIT; + + +int getShilIconType(IconBuffer::IconSize sz) +{ + boost::call_once(initVistaFlagOnce, []() + { + wereVistaOrLater = vistaOrLater(); + }); + + switch (sz) + { + case IconBuffer::SIZE_SMALL: + return SHIL_SMALL; //16x16, but the size can be customized by the user. + case IconBuffer::SIZE_MEDIUM: + return SHIL_EXTRALARGE; //typically 48x48, but the size can be customized by the user. + case IconBuffer::SIZE_LARGE: + return wereVistaOrLater ? SHIL_JUMBO //normally 256x256 pixels -> will be scaled down by IconHolder + : SHIL_EXTRALARGE; //XP doesn't have jumbo icons + } + return SHIL_SMALL; +} + + +util::DllFun<thumb::GetIconByIndexFct> getIconByIndex; +boost::once_flag initGetIconByIndexOnce = BOOST_ONCE_INIT; + + +IconHolder getIconByAttribute(LPCWSTR pszPath, DWORD dwFileAttributes, IconBuffer::IconSize sz) +{ //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - ::SHGetFileInfo(filename.c_str(), //zen::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! - 0, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON); + SHFILEINFO fileInfo = {}; //initialize hIcon + DWORD_PTR imgList = ::SHGetFileInfo(pszPath, //Windows 7 doesn't like this parameter to be an empty string + dwFileAttributes, + &fileInfo, + sizeof(fileInfo), + SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES); + //no need to IUnknown::Release() imgList! + if (!imgList) + return NULL; + + boost::call_once(initGetIconByIndexOnce, []() //thread-safe init + { + getIconByIndex = util::DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); + }); + return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; +} + + +IconHolder getAssociatedIconByExt(const Zstring& extension, IconBuffer::IconSize sz) +{ + //no read-access to disk! determine icon by extension + return getIconByAttribute((Zstr("dummy.") + extension).c_str(), FILE_ATTRIBUTE_NORMAL, sz); +} + + +util::DllFun<thumb::GetThumbnailFct> getThumbnailIcon; +boost::once_flag initThumbnailOnce = BOOST_ONCE_INIT; +} +#endif +//################################################################################################################################################ + + +IconHolder getThumbnail(const Zstring& filename, int requestedSize) //return 0 on failure +{ +#ifdef FFS_WIN + using namespace thumb; - return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) + boost::call_once(initThumbnailOnce, []() //note: "getThumbnail" function itself is already thread-safe + { + getThumbnailIcon = util::DllFun<GetThumbnailFct>(getDllName(), getThumbnailFctName); + }); + return getThumbnailIcon ? static_cast< ::HICON>(getThumbnailIcon(filename.c_str(), requestedSize)) : NULL; #elif defined FFS_LINUX //call Gtk::Main::init_gtkmm_internals() on application startup!! try { + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = Gdk::Pixbuf::create_from_file(filename.c_str(), requestedSize, requestedSize); + if (iconPixbuf) + return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) + } + catch (const Glib::Error&) {} + + return IconHolder(); +#endif +} + + +IconHolder getAssociatedIcon(const Zstring& filename, IconBuffer::IconSize sz) +{ + //1. try to load thumbnails + switch (sz) + { + case IconBuffer::SIZE_SMALL: + break; + case IconBuffer::SIZE_MEDIUM: + case IconBuffer::SIZE_LARGE: + { + IconHolder ico = getThumbnail(filename, IconBuffer::cvrtSize(sz)); + if (ico) + return ico; + //else: fallback to non-thumbnail icon + } + break; + } + + //2. retrieve file icons +#ifdef FFS_WIN + //perf: optimize fallback case for SIZE_MEDIUM and SIZE_LARGE: + const Zstring& extension = getFileExtension(filename); + if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension + return getAssociatedIconByExt(extension, sz); + //result will not be buffered under extension name, but full filename; this is okay, since we're in SIZE_MEDIUM or SIZE_LARGE context, + //which means the access to get thumbnail failed: thumbnail failure is not dependent from extension in general! + + SHFILEINFO fileInfo = {}; + DWORD_PTR imgList = ::SHGetFileInfo(filename.c_str(), //zen::removeLongPathPrefix(fileName), //::SHGetFileInfo() can't handle \\?\-prefix! + 0, + &fileInfo, + sizeof(fileInfo), + SHGFI_SYSICONINDEX); + //Quote: "The IImageList pointer type, such as that returned in the ppv parameter, can be cast as an HIMAGELIST as + // needed; for example, for use in a list view. Conversely, an HIMAGELIST can be cast as a pointer to an IImageList." + //http://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx + + if (!imgList) + return NULL; + //imgList->Release(); //empiric study: crash on XP if we release this! Seems we do not own it... -> also no GDI leak on Win7 -> okay + //another comment on http://msdn.microsoft.com/en-us/library/bb762179(v=VS.85).aspx describes exact same behavior on Win7/XP + + boost::call_once(initGetIconByIndexOnce, []() //thread-safe init + { + getIconByIndex = util::DllFun<thumb::GetIconByIndexFct>(thumb::getDllName(), thumb::getIconByIndexFctName); + }); + return getIconByIndex ? static_cast<HICON>(getIconByIndex(fileInfo.iIcon, getShilIconType(sz))) : NULL; + +#elif defined FFS_LINUX + const int requestedSize = IconBuffer::cvrtSize(sz); + //call Gtk::Main::init_gtkmm_internals() on application startup!! + try + { Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails Glib::RefPtr<Gio::FileInfo> fileInfo = fileObj->query_info(G_FILE_ATTRIBUTE_STANDARD_ICON); if (fileInfo) @@ -162,7 +341,7 @@ IconHolder getAssociatedIcon(const Zstring& filename) Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); if (iconTheme) { - Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system + Gtk::IconInfo iconInfo = iconTheme->lookup_icon(gicon, requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); //this may fail if icon is not installed on system if (iconInfo) { Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconInfo.load_icon(); //render icon into Pixbuf @@ -180,9 +359,9 @@ IconHolder getAssociatedIcon(const Zstring& filename) Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); if (iconTheme) { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + iconPixbuf = iconTheme->load_icon("text-x-generic", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (iconPixbuf) return IconHolder(iconPixbuf->gobj_copy()); //copy and pass icon ownership (may be 0) } @@ -194,91 +373,44 @@ IconHolder getAssociatedIcon(const Zstring& filename) #endif } -#ifdef FFS_WIN -IconHolder getAssociatedIconByExt(const Zstring& extension) -{ - SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - - //no read-access to disk! determine icon by extension - ::SHGetFileInfo((Zstr("dummy.") + extension).c_str(), //Windows Seven doesn't like this parameter to be without short name - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); - - return IconHolder(fileInfo.hIcon); //pass icon ownership (may be 0) -} -#endif - -wxIcon getDirectoryIcon() +IconHolder getDirectoryIcon(IconBuffer::IconSize sz) { #ifdef FFS_WIN - wxIcon folderIcon; - - SHFILEINFO fileInfo = {}; //initialize hIcon - - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_DIRECTORY, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - folderIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } - - return folderIcon; - + return getIconByAttribute(L"dummy", //Windows 7 doesn't like this parameter to be an empty string! + FILE_ATTRIBUTE_DIRECTORY, sz); #elif defined FFS_LINUX - return ::getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" + return ::getAssociatedIcon(Zstr("/usr/"), sz); //all directories will look like "/usr/" #endif } -wxIcon getFileIcon() +IconHolder getFileIcon(IconBuffer::IconSize sz) { - wxIcon fileIcon; - #ifdef FFS_WIN - SHFILEINFO fileInfo = {}; //initialize hIcon - - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - fileIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - fileIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } - + return getIconByAttribute(L"dummy", FILE_ATTRIBUTE_NORMAL, sz); #elif defined FFS_LINUX + const int requestedSize = IconBuffer::cvrtSize(sz); try { Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); if (iconTheme) { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + iconPixbuf = iconTheme->load_icon("text-x-generic", requestedSize, Gtk::ICON_LOOKUP_USE_BUILTIN); if (iconPixbuf) - fileIcon.SetPixbuf(iconPixbuf->gobj_copy()); // transfer ownership!! + return IconHolder(iconPixbuf->gobj_copy()); // transfer ownership!! } } catch (const Glib::Error&) {} + + return IconHolder(); #endif - return fileIcon; } +//################################################################################################################################################ -//################################################################################################################################################ //---------------------- Shared Data ------------------------- struct WorkLoad { @@ -318,73 +450,64 @@ typedef std::queue<Zstring> IconDbSequence; //entryName class Buffer { public: - bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); - void insertIntoBuffer(const Zstring& entryName, const IconHolder& icon); //called by worker thread - -private: - boost::mutex lockBuffer; - NameIconMap iconMappping; //use synchronisation when accessing this! - IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements -}; -//------------------------------------------------------------ - - -bool Buffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) //context of main AND worker thread -{ - boost::lock_guard<boost::mutex> dummy(lockBuffer); - -#ifdef FFS_WIN - //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - const Zstring extension = getFileExtension(fileName); - const Zstring searchString = isPriceyExtension(extension) ? fileName : extension; - auto iter = iconMappping.find(searchString); -#elif defined FFS_LINUX - auto iter = iconMappping.find(fileName); -#endif + bool requestFileIcon(const Zstring& fileName, IconHolder* icon = NULL) + { + boost::lock_guard<boost::mutex> dummy(lockBuffer); - if (iter == iconMappping.end()) + auto iter = iconMappping.find(fileName); + if (iter != iconMappping.end()) + { + if (icon != NULL) + *icon = iter->second; + return true; + } return false; + } - if (icon != NULL) - *icon = iter->second.toWxIcon(); - return true; -} - - -void Buffer::insertIntoBuffer(const Zstring& entryName, const IconHolder& icon) //called by worker thread -{ - boost::lock_guard<boost::mutex> dummy(lockBuffer); + void insertIntoBuffer(const Zstring& entryName, const IconHolder& icon) //called by worker thread + { + boost::lock_guard<boost::mutex> dummy(lockBuffer); - //thread saftey: icon uses ref-counting! But is NOT shared with main thread! - auto rc = iconMappping.insert(std::make_pair(entryName, icon)); - if (rc.second) //if insertion took place - iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! + //thread saftey: icon uses ref-counting! But is NOT shared with main thread! + auto rc = iconMappping.insert(std::make_pair(entryName, icon)); + if (rc.second) //if insertion took place + iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! - assert(iconMappping.size() == iconSequence.size()); + assert(iconMappping.size() == iconSequence.size()); - //remove elements if buffer becomes too big: - if (iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) - { - //remove oldest element - iconMappping.erase(iconSequence.front()); - iconSequence.pop(); + //remove elements if buffer becomes too big: + if (iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) + { + //remove oldest element + iconMappping.erase(iconSequence.front()); + iconSequence.pop(); + } } -} + +private: + boost::mutex lockBuffer; + NameIconMap iconMappping; //use synchronisation when accessing this! + IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements +}; +//------------------------------------------------------------- //################################################################################################################################################ class WorkerThread //lifetime is part of icon buffer { public: WorkerThread(const std::shared_ptr<WorkLoad>& workload, - const std::shared_ptr<Buffer>& buffer) : + const std::shared_ptr<Buffer>& buffer, + IconBuffer::IconSize sz) : workload_(workload), - buffer_(buffer) {} + buffer_(buffer), + icoSize(sz) {} void operator()(); //thread entry private: std::shared_ptr<WorkLoad> workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) std::shared_ptr<Buffer> buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety + const IconBuffer::IconSize icoSize; }; @@ -392,9 +515,18 @@ void WorkerThread::operator()() //thread entry { //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 #ifdef FFS_WIN - ::CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); - Loki::ScopeGuard dummy = Loki::MakeGuard([]() { ::CoUninitialize(); }); - (void)dummy; + //Prerequisites, see thumbnail.h + + //1. Initialize COM + ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + LOKI_ON_BLOCK_EXIT2(::CoUninitialize()); + + //2. Initialize system image list + typedef BOOL (WINAPI *FileIconInitFun)(BOOL fRestoreCache); + const util::DllFun<FileIconInitFun> fileIconInit(L"Shell32.dll", reinterpret_cast<LPCSTR>(660)); + assert(fileIconInit); + if (fileIconInit) + fileIconInit(false); //TRUE to restore the system image cache from disk; FALSE otherwise. #endif while (true) @@ -406,20 +538,9 @@ void WorkerThread::operator()() //thread entry if (buffer_->requestFileIcon(fileName)) continue; //icon already in buffer: skip -#ifdef FFS_WIN - const Zstring extension = getFileExtension(fileName); - if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - buffer_->insertIntoBuffer(fileName, getAssociatedIcon(fileName)); - else //no read-access to disk! determine icon by extension - buffer_->insertIntoBuffer(extension, getAssociatedIconByExt(extension)); - -#elif defined FFS_LINUX - const IconHolder newIcon = getAssociatedIcon(fileName); - buffer_->insertIntoBuffer(fileName, newIcon); -#endif + buffer_->insertIntoBuffer(fileName, getAssociatedIcon(fileName, icoSize)); } } - //######################### redirect to impl ##################################################### struct IconBuffer::Pimpl @@ -435,9 +556,13 @@ struct IconBuffer::Pimpl }; -IconBuffer::IconBuffer() : pimpl(new Pimpl) +IconBuffer::IconBuffer(IconSize sz) : + pimpl(new Pimpl), + icoSize(sz), + genDirIcon(::getDirectoryIcon(sz).toWxIcon(cvrtSize(icoSize))), + genFileIcon(::getFileIcon(sz).toWxIcon(cvrtSize(icoSize))) { - pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer)); + pimpl->worker = boost::thread(WorkerThread(pimpl->workload, pimpl->buffer, sz)); } @@ -449,24 +574,40 @@ IconBuffer::~IconBuffer() } -IconBuffer& IconBuffer::getInstance() +bool IconBuffer::requestFileIcon(const Zstring& filename, wxIcon* icon) { - static IconBuffer instance; - return instance; -} + auto getIcon = [&](const Zstring& entryName) -> bool + { + if (!icon) + return pimpl->buffer->requestFileIcon(entryName); -bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return pimpl->buffer->requestFileIcon(fileName, icon); } + IconHolder heldIcon; + if (!pimpl->buffer->requestFileIcon(entryName, &heldIcon)) + return false; + *icon = heldIcon.toWxIcon(cvrtSize(icoSize)); + return true; + }; -void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); } +#ifdef FFS_WIN + //perf: let's read icons which don't need file access right away! No async delay justified! + if (icoSize == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! + { + const Zstring& extension = getFileExtension(filename); + if (isCheapExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension + { + if (!getIcon(extension)) + { + IconHolder heldIcon = getAssociatedIconByExt(extension, icoSize); //fast! + pimpl->buffer->insertIntoBuffer(extension, heldIcon); + if (icon) + *icon = heldIcon.toWxIcon(cvrtSize(icoSize)); + } + return true; + } + } +#endif -const wxIcon& IconBuffer::getDirectoryIcon() -{ - static wxIcon dirIcon = ::getDirectoryIcon(); - return dirIcon; + return getIcon(filename); } -const wxIcon& IconBuffer::getFileIcon() -{ - static wxIcon fileIcon = ::getFileIcon(); - return fileIcon; -} +void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); } diff --git a/library/icon_buffer.h b/library/icon_buffer.h index 00370d1b..cbd582f2 100644 --- a/library/icon_buffer.h +++ b/library/icon_buffer.h @@ -17,25 +17,33 @@ namespace zen class IconBuffer { public: - static const wxIcon& getDirectoryIcon(); //generic icons - static const wxIcon& getFileIcon(); // + enum IconSize + { + SIZE_SMALL, + SIZE_MEDIUM, + SIZE_LARGE + }; + + IconBuffer(IconSize sz); + ~IconBuffer(); + + int getSize() const { return cvrtSize(icoSize); } //*maximum* icon size in pixel + + const wxIcon& genericDirIcon () { return genDirIcon; } + const wxIcon& genericFileIcon() { return genFileIcon; } - static IconBuffer& getInstance(); - bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); //returns false if icon is not in buffer + bool requestFileIcon(const Zstring& filename, wxIcon* icon = NULL); //returns false if icon is not in buffer void setWorkload(const std::vector<Zstring>& load); //(re-)set new workload of icons to be retrieved; -#ifdef FFS_WIN - static const int ICON_SIZE = 16; //size in pixel -#elif defined FFS_LINUX - static const int ICON_SIZE = 24; //size in pixel -#endif + static int cvrtSize(IconSize sz); private: - IconBuffer(); - ~IconBuffer(); - struct Pimpl; std::unique_ptr<Pimpl> pimpl; + + const IconSize icoSize; + const wxIcon genDirIcon; + const wxIcon genFileIcon; }; } diff --git a/library/lock_holder.h b/library/lock_holder.h index 65471d5f..b72b12c1 100644 --- a/library/lock_holder.h +++ b/library/lock_holder.h @@ -20,10 +20,6 @@ public: if (dirnameFmt.empty()) return; - std::wstring statusText = _("Searching for directory %x..."); - replace(statusText, L"%x", std::wstring(L"\"") + dirnameFmt + L"\"", false); - procCallback.reportInfo(statusText); - if (!dirExistsUpdating(dirnameFmt, procCallback)) return; @@ -35,7 +31,7 @@ public: public: WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {} virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions - virtual void reportInfo(const std::wstring& text) { pc_.reportInfo(text); } + virtual void reportInfo(const std::wstring& text) { pc_.reportStatus(text); } private: ProcessCallback& pc_; } callback(procCallback); diff --git a/library/parallel_scan.cpp b/library/parallel_scan.cpp index 5ccb05fe..2c24600f 100644 --- a/library/parallel_scan.cpp +++ b/library/parallel_scan.cpp @@ -12,7 +12,6 @@ #include "../shared/file_traverser.h" #include "../shared/file_error.h" #include "../shared/string_conv.h" -#include "../shared/check_exist.h" #include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> #include "loki/ScopeGuard.h" //#include "../shared/file_id.h" @@ -96,15 +95,13 @@ DiskInfo retrieveDiskInfo(const Zstring& pathName) HANDLE hVolume = ::CreateFile(volnameFmt.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, + 0, OPEN_EXISTING, 0, NULL); if (hVolume == INVALID_HANDLE_VALUE) return output; - - Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hVolume); - (void)dummy; //silence warning "unused variable" + LOKI_ON_BLOCK_EXIT2(::CloseHandle(hVolume)); std::vector<char> buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks! @@ -196,19 +193,22 @@ public: FillBufferCallback::HandleError reportError(const std::wstring& msg) //blocking call: context of worker thread { boost::unique_lock<boost::mutex> dummy(lockErrorMsg); - while(!errorMsg.empty() || errorResponse.get()) + while (!errorMsg.empty() || errorResponse.get()) conditionCanReportError.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! errorMsg = BasicWString(msg); - while(!errorResponse.get()) + while (!errorResponse.get()) conditionGotResponse.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! FillBufferCallback::HandleError rv = *errorResponse; errorMsg.clear(); errorResponse.reset(); + + //dummy.unlock(); conditionCanReportError.notify_one(); + return rv; } @@ -219,6 +219,8 @@ public: { FillBufferCallback::HandleError rv = callback.reportError(cvrtString<std::wstring>(errorMsg)); //throw! errorResponse.reset(new FillBufferCallback::HandleError(rv)); + + //dummy.unlock(); conditionGotResponse.notify_one(); } } @@ -324,7 +326,6 @@ public: const HardFilter::FilterRef filterInstance; //always bound! std::set<Zstring>& failedReads_; //relative postfixed names of directories that could not be read (empty for root) - std::set<DirContainer*> excludedDirs; AsyncCallback& acb_; int threadID_; @@ -428,14 +429,12 @@ TraverseCallback::ReturnValDir DirCallback::onDir(const Zchar* shortName, const bool subObjMightMatch = true; const bool passFilter = cfg.filterInstance->passDirFilter(relName, &subObjMightMatch); if (!passFilter && !subObjMightMatch) - return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //do NOT traverse subdirs - //else: attention! ensure directory filtering is applied later to exclude actually filtered directories + return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //do NOT traverse subdirs + //else: attention! ensure directory filtering is applied later to exclude actually filtered directories DirContainer& subDir = output_.addSubDir(shortName); if (passFilter) - cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator - else - cfg.excludedDirs.insert(&subDir); + cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator TraverserShared::CallbackPointer subDirCallback = std::make_shared<DirCallback>(cfg, relName + FILE_NAME_SEPARATOR, subDir); cfg.callBackBox.push_back(subDirCallback); //handle lifetime @@ -484,38 +483,6 @@ private: #endif //------------------------------------------------------------------------------------------ -template <class M, class Predicate> inline -void map_remove_if(M& map, Predicate p) -{ - for (auto iter = map.begin(); iter != map.end();) - if (p(*iter)) - map.erase(iter++); - else - ++iter; -} - -void removeFilteredDirs(DirContainer& dirCont, const std::set<DirContainer*>& excludedDirs) -{ - //process subdirs recursively - std::for_each(dirCont.dirs.begin(), dirCont.dirs.end(), - [&](std::pair<const Zstring, DirContainer>& item) - { - removeFilteredDirs(item.second, excludedDirs); - }); - - //remove superfluous directories - map_remove_if(dirCont.dirs, - [&](std::pair<const Zstring, DirContainer>& item) -> bool - { - DirContainer& subDir = item.second; - return - subDir.dirs .empty() && - subDir.files.empty() && - subDir.links.empty() && - excludedDirs.find(&subDir) != excludedDirs.end(); - }); -} - class WorkerThread { @@ -530,8 +497,7 @@ public: void operator()() //thread entry { acb_->incActiveWorker(); - Loki::ScopeGuard dummy = Loki::MakeGuard([&]() { acb_->decActiveWorker(); }); - (void)dummy; + LOKI_ON_BLOCK_EXIT2(acb_->decActiveWorker();); std::for_each(workload_.begin(), workload_.end(), [&](std::pair<DirectoryKey, DirectoryValue*>& item) @@ -539,50 +505,40 @@ public: const Zstring& directoryName = item.first.dirnameFull_; DirectoryValue& dirVal = *item.second; - acb_->reportCurrentFile(directoryName, threadID_); //just in case directory existence check is blocking! + acb_->reportCurrentFile(directoryName, threadID_); //just in case first directory access is blocking + + TraverserShared travCfg(threadID_, + item.first.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy + item.first.filter_, + dirVal.failedReads, + *acb_); - if (!directoryName.empty() && - util::dirExistsAsync(directoryName).get()) //blocking + interruption point! - //folder existence already checked in startCompareProcess(): do not treat as error when arriving here! - //perf note: missing network drives should not delay here, as Windows buffers result of last existence check for a short time + DirCallback traverser(travCfg, + Zstring(), + dirVal.dirCont); + + bool followSymlinks = false; + switch (item.first.handleSymlinks_) { - TraverserShared travCfg(threadID_, - item.first.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy - item.first.filter_, - dirVal.failedReads, - *acb_); - - DirCallback traverser(travCfg, - Zstring(), - dirVal.dirCont); - - bool followSymlinks = false; - switch (item.first.handleSymlinks_) - { - case SYMLINK_IGNORE: - followSymlinks = false; //=> symlinks will be reported via onSymlink() where they are excluded - break; - case SYMLINK_USE_DIRECTLY: - followSymlinks = false; - break; - case SYMLINK_FOLLOW_LINK: - followSymlinks = true; - break; - } - - DstHackCallback* dstCallbackPtr = NULL; + case SYMLINK_IGNORE: + followSymlinks = false; //=> symlinks will be reported via onSymlink() where they are excluded + break; + case SYMLINK_USE_DIRECTLY: + followSymlinks = false; + break; + case SYMLINK_FOLLOW_LINK: + followSymlinks = true; + break; + } + + DstHackCallback* dstCallbackPtr = NULL; #ifdef FFS_WIN - DstHackCallbackImpl dstCallback(*acb_, threadID_); - dstCallbackPtr = &dstCallback; + DstHackCallbackImpl dstCallback(*acb_, threadID_); + dstCallbackPtr = &dstCallback; #endif - //get all files and folders from directoryPostfixed (and subdirectories) - traverseFolder(directoryName, followSymlinks, traverser, dstCallbackPtr); //exceptions may be thrown! - - //attention: some filtered directories are still in the comparison result! (see include filter handling!) - if (!travCfg.excludedDirs.empty()) - removeFilteredDirs(dirVal.dirCont, travCfg.excludedDirs); //remove all excluded directories (but keeps those serving as parent folders for not excl. elements) - } + //get all files and folders from directoryPostfixed (and subdirectories) + traverseFolder(directoryName, followSymlinks, traverser, dstCallbackPtr); //exceptions may be thrown! }); } diff --git a/library/parallel_scan.h b/library/parallel_scan.h index 5eaa6bd9..5c998969 100644 --- a/library/parallel_scan.h +++ b/library/parallel_scan.h @@ -45,7 +45,7 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) struct DirectoryValue { DirContainer dirCont; - std::set<Zstring> failedReads; //relative postfixed names of directories that could not be read (empty for root), e.g. access denied, or temporal network drop + std::set<Zstring> failedReads; //relative postfixed names of directories that could not be read (empty string for root), e.g. access denied, or temporal network drop }; diff --git a/library/process_xml.cpp b/library/process_xml.cpp index aad96265..8362e073 100644 --- a/library/process_xml.cpp +++ b/library/process_xml.cpp @@ -98,22 +98,48 @@ xmlAccess::XmlGuiConfig xmlAccess::convertBatchToGui(const xmlAccess::XmlBatchCo { XmlGuiConfig output; output.mainCfg = batchCfg.mainCfg; + + switch (batchCfg.handleError) + { + case ON_ERROR_EXIT: + case ON_ERROR_POPUP: + output.handleError = ON_GUIERROR_POPUP; + break; + case ON_ERROR_IGNORE: + output.handleError = ON_GUIERROR_IGNORE; + break; + } return output; } -xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg) +xmlAccess::XmlBatchConfig xmlAccess::convertGuiToBatch(const xmlAccess::XmlGuiConfig& guiCfg, const wxString& referenceFile) { - XmlBatchConfig output; + //try to take over batch-specific settings from reference + if (!referenceFile.empty() && getXmlType(referenceFile) == XML_TYPE_BATCH) + try + { + XmlBatchConfig output; + + std::vector<wxString> filenames; + filenames.push_back(referenceFile); + convertConfig(filenames, output); //throw xmlAccess::FfsXmlError + + output.mainCfg = guiCfg.mainCfg; + return output; + } + catch (xmlAccess::FfsXmlError&) {} + + XmlBatchConfig output; //use default batch-settings output.mainCfg = guiCfg.mainCfg; switch (guiCfg.handleError) { case ON_GUIERROR_POPUP: - output.handleError = xmlAccess::ON_ERROR_POPUP; + output.handleError = ON_ERROR_POPUP; break; case ON_GUIERROR_IGNORE: - output.handleError = xmlAccess::ON_ERROR_IGNORE; + output.handleError = ON_ERROR_IGNORE; break; } @@ -158,12 +184,12 @@ xmlAccess::MergeType xmlAccess::getMergeType(const std::vector<wxString>& filena namespace { template <class XmlCfg> -XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlError>& exeption) //throw (xmlAccess::FfsXmlError) +XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlError>& exeption) //throw xmlAccess::FfsXmlError { XmlCfg cfg; try { - xmlAccess::readConfig(filename, cfg); //throw (xmlAccess::FfsXmlError); + xmlAccess::readConfig(filename, cfg); //throw xmlAccess::FfsXmlError } catch (const xmlAccess::FfsXmlError& e) { @@ -177,7 +203,7 @@ XmlCfg loadCfgImpl(const wxString& filename, std::unique_ptr<xmlAccess::FfsXmlEr template <class XmlCfg> -void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw (xmlAccess::FfsXmlError) +void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config) //throw xmlAccess::FfsXmlError { using namespace xmlAccess; @@ -193,11 +219,11 @@ void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config switch (getXmlType(*i)) { case XML_TYPE_GUI: - mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw (xmlAccess::FfsXmlError) + mainCfgs.push_back(loadCfgImpl<XmlGuiConfig>(*i, savedException).mainCfg); //throw xmlAccess::FfsXmlError break; case XML_TYPE_BATCH: - mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw (xmlAccess::FfsXmlError) + mainCfgs.push_back(loadCfgImpl<XmlBatchConfig>(*i, savedException).mainCfg); //throw xmlAccess::FfsXmlError break; case XML_TYPE_GLOBAL: @@ -211,9 +237,9 @@ void mergeConfigFilesImpl(const std::vector<wxString>& filenames, XmlCfg& config try //...to init all non-"mainCfg" settings with first config file { - xmlAccess::readConfig(filenames[0], config); //throw (xmlAccess::FfsXmlError); + xmlAccess::readConfig(filenames[0], config); //throw xmlAccess::FfsXmlError } - catch (...) {} + catch (xmlAccess::FfsXmlError&) {} config.mainCfg = merge(mainCfgs); @@ -364,6 +390,41 @@ bool readText(const std::string& input, OnGuiError& value) template <> inline +void writeText(const FileIconSize& value, std::string& output) +{ + switch (value) + { + case ICON_SIZE_SMALL: + output = "Small"; + break; + case ICON_SIZE_MEDIUM: + output = "Medium"; + break; + case ICON_SIZE_LARGE: + output = "Large"; + break; + } +} + + +template <> inline +bool readText(const std::string& input, FileIconSize& value) +{ + std::string tmp = input; + zen::trim(tmp); + if (tmp == "Small") + value = ICON_SIZE_SMALL; + else if (tmp == "Medium") + value = ICON_SIZE_MEDIUM; + else if (tmp == "Large") + value = ICON_SIZE_LARGE; + else + return false; + return true; +} + + +template <> inline void writeText(const DeletionPolicy& value, std::string& output) { switch (value) @@ -439,17 +500,20 @@ void writeText(const UnitTime& value, std::string& output) case UTIME_NONE: output = "Inactive"; break; - case UTIME_SEC: - output = "Second"; + // case UTIME_LAST_X_HOURS: + // output = "x-hours"; + // break; + case UTIME_TODAY: + output = "Today"; break; - case UTIME_MIN: - output = "Minute"; + case UTIME_THIS_WEEK: + output = "Week"; break; - case UTIME_HOUR: - output = "Hour"; + case UTIME_THIS_MONTH: + output = "Month"; break; - case UTIME_DAY: - output = "Day"; + case UTIME_THIS_YEAR: + output = "Year"; break; } } @@ -461,14 +525,16 @@ bool readText(const std::string& input, UnitTime& value) zen::trim(tmp); if (tmp == "Inactive") value = UTIME_NONE; - else if (tmp == "Second") - value = UTIME_SEC; - else if (tmp == "Minute") - value = UTIME_MIN; - else if (tmp == "Hour") - value = UTIME_HOUR; - else if (tmp == "Day") - value = UTIME_DAY; + // else if (tmp == "x-hours") + // value = UTIME_LAST_X_HOURS; + else if (tmp == "Today") + value = UTIME_TODAY; + else if (tmp == "Week") + value = UTIME_THIS_WEEK; + else if (tmp == "Month") + value = UTIME_THIS_MONTH; + else if (tmp == "Year") + value = UTIME_THIS_YEAR; else return false; return true; @@ -529,38 +595,38 @@ bool readText(const std::string& input, UnitSize& value) template <> inline -void writeText(const SyncConfig::Variant& value, std::string& output) +void writeText(const DirectionConfig::Variant& value, std::string& output) { switch (value) { - case SyncConfig::AUTOMATIC: + case DirectionConfig::AUTOMATIC: output = "Automatic"; break; - case SyncConfig::MIRROR: + case DirectionConfig::MIRROR: output = "Mirror"; break; - case SyncConfig::UPDATE: + case DirectionConfig::UPDATE: output = "Update"; break; - case SyncConfig::CUSTOM: + case DirectionConfig::CUSTOM: output = "Custom"; break; } } template <> inline -bool readText(const std::string& input, SyncConfig::Variant& value) +bool readText(const std::string& input, DirectionConfig::Variant& value) { std::string tmp = input; zen::trim(tmp); if (tmp == "Automatic") - value = SyncConfig::AUTOMATIC; + value = DirectionConfig::AUTOMATIC; else if (tmp == "Mirror") - value = SyncConfig::MIRROR; + value = DirectionConfig::MIRROR; else if (tmp == "Update") - value = SyncConfig::UPDATE; + value = DirectionConfig::UPDATE; else if (tmp == "Custom") - value = SyncConfig::CUSTOM; + value = DirectionConfig::CUSTOM; else return false; return true; @@ -591,17 +657,33 @@ void writeValue(const ColumnAttrib& value, XmlElement& output) namespace { -void readConfig(const XmlIn& in, SyncConfig& syncCfg) +void readConfig(const XmlIn& in, CompConfig& cmpConfig) +{ + in["Variant" ](cmpConfig.compareVar); + in["HandleSymlinks"](cmpConfig.handleSymlinks); +} + + +void readConfig(const XmlIn& in, DirectionConfig& directCfg) { - in["Variant"](syncCfg.var); + in["Variant"](directCfg.var); XmlIn inCustDir = in["CustomDirections"]; - inCustDir["LeftOnly" ](syncCfg.custom.exLeftSideOnly); - inCustDir["RightOnly" ](syncCfg.custom.exRightSideOnly); - inCustDir["LeftNewer" ](syncCfg.custom.leftNewer); - inCustDir["RightNewer"](syncCfg.custom.rightNewer); - inCustDir["Different" ](syncCfg.custom.different); - inCustDir["Conflict" ](syncCfg.custom.conflict); + inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); + inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); + inCustDir["LeftNewer" ](directCfg.custom.leftNewer); + inCustDir["RightNewer"](directCfg.custom.rightNewer); + inCustDir["Different" ](directCfg.custom.different); + inCustDir["Conflict" ](directCfg.custom.conflict); +} + + +void readConfig(const XmlIn& in, SyncConfig& syncCfg) +{ + readConfig(in, syncCfg.directionCfg); + + in["DeletionPolicy" ](syncCfg.handleDeletion); + in["CustomDeletionFolder"](syncCfg.customDeletionDirectory); } @@ -628,18 +710,24 @@ void readConfig(const XmlIn& in, FolderPairEnh& enhPair) in["Right"](enhPair.rightDirectory); //########################################################### - //alternate sync configuration (optional) - XmlIn inAlt = in["AlternateSyncConfig"]; - if (inAlt) + //alternate comp configuration (optional) + XmlIn inAltCmp = in["AlternateCompareConfig"]; + if (inAltCmp) { - std::shared_ptr<AlternateSyncConfig> altSyncCfg = std::make_shared<AlternateSyncConfig>(); - enhPair.altSyncConfig = altSyncCfg; + CompConfig altCmpCfg; + readConfig(inAltCmp, altCmpCfg); - //read sync configuration - readConfig(inAlt, altSyncCfg->syncConfiguration); + enhPair.altCmpConfig = std::make_shared<CompConfig>(altCmpCfg);; + } + //########################################################### + //alternate sync configuration (optional) + XmlIn inAltSync = in["SyncConfig"]; + if (inAltSync) + { + SyncConfig altSyncCfg; + readConfig(inAltSync, altSyncCfg); - inAlt["DeletionPolicy" ](altSyncCfg->handleDeletion); - inAlt["CustomDeletionFolder"](altSyncCfg->customDeletionDirectory); + enhPair.altSyncConfig = std::make_shared<SyncConfig>(altSyncCfg); } //########################################################### @@ -650,24 +738,16 @@ void readConfig(const XmlIn& in, FolderPairEnh& enhPair) void readConfig(const XmlIn& in, MainConfiguration& mainCfg) { - XmlIn inCmp = in["MainConfig"]["Comparison"]; - - //read compare variant - inCmp["Variant"](mainCfg.compareVar); - - //include symbolic links at all? - inCmp["HandleSymlinks"](mainCfg.handleSymlinks); + //read compare settings + XmlIn inCmp = in["MainConfig"]["Comparison"]; + readConfig(inCmp, mainCfg.cmpConfig); //########################################################### + XmlIn inSync = in["MainConfig"]["SyncConfig"]; //read sync configuration - readConfig(inSync, mainCfg.syncConfiguration); - //########################################################### - - //misc - inSync["DeletionPolicy" ](mainCfg.handleDeletion); - inSync["CustomDeletionFolder"](mainCfg.customDeletionDirectory); + readConfig(inSync, mainCfg.syncCfg); //########################################################### XmlIn inFilter = in["MainConfig"]["GlobalFilter"]; @@ -731,7 +811,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inShared["CopyLockedFiles" ](config.copyLockedFiles); inShared["CopyFilePermissions" ](config.copyFilePermissions); - inShared["TransactionalFileCopy"](config.transactionalFileCopy); + inShared["TransactionalFileCopy"](config.transactionalFileCopy); inShared["VerifyCopiedFiles" ](config.verifyFileCopy); //max. allowed file time deviation @@ -765,13 +845,12 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) inWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); inWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); - //########################################################### + inWnd["IconSize"](config.gui.iconSize); + //########################################################### //read column attributes XmlIn inColLeft = inWnd["LeftColumns"]; - inColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft); - inColLeft.attribute("ShowFileIcons", config.gui.showFileIconsLeft); inColLeft(config.gui.columnAttribLeft); for (size_t i = 0; i < config.gui.columnAttribLeft.size(); ++i) @@ -779,9 +858,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config) //########################################################### XmlIn inColRight = inWnd["RightColumns"]; - inColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight); - inColRight.attribute("ShowFileIcons", config.gui.showFileIconsRight); inColRight(config.gui.columnAttribRight); for (size_t i = 0; i < config.gui.columnAttribRight.size(); ++i) @@ -850,17 +927,33 @@ void xmlAccess::readConfig(xmlAccess::XmlGlobalSettings& config) //################################################################################################ namespace { -void writeConfig(const SyncConfig& syncCfg, XmlOut& out) +void writeConfig(const CompConfig& cmpConfig, XmlOut& out) { - out["Variant"](syncCfg.var); + out["Variant" ](cmpConfig.compareVar); + out["HandleSymlinks"](cmpConfig.handleSymlinks); +} + + +void writeConfig(const DirectionConfig& directCfg, XmlOut& out) +{ + out["Variant"](directCfg.var); XmlOut outCustDir = out["CustomDirections"]; - outCustDir["LeftOnly" ](syncCfg.custom.exLeftSideOnly); - outCustDir["RightOnly" ](syncCfg.custom.exRightSideOnly); - outCustDir["LeftNewer" ](syncCfg.custom.leftNewer); - outCustDir["RightNewer"](syncCfg.custom.rightNewer); - outCustDir["Different" ](syncCfg.custom.different); - outCustDir["Conflict" ](syncCfg.custom.conflict); + outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); + outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); + outCustDir["LeftNewer" ](directCfg.custom.leftNewer); + outCustDir["RightNewer"](directCfg.custom.rightNewer); + outCustDir["Different" ](directCfg.custom.different); + outCustDir["Conflict" ](directCfg.custom.conflict); +} + + +void writeConfig(const SyncConfig& syncCfg, XmlOut& out) +{ + writeConfig(syncCfg.directionCfg, out); + + out["DeletionPolicy" ](syncCfg.handleDeletion); + out["CustomDeletionFolder"](syncCfg.customDeletionDirectory); } @@ -889,16 +982,20 @@ void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out) outPair["Right"](enhPair.rightDirectory); //########################################################### + //alternate comp configuration (optional) + if (enhPair.altCmpConfig.get()) + { + XmlOut outAlt = outPair["AlternateCompareConfig"]; + + writeConfig(*enhPair.altCmpConfig, outAlt); + } + //########################################################### //alternate sync configuration (optional) if (enhPair.altSyncConfig.get()) { - XmlOut outAlt = outPair["AlternateSyncConfig"]; - - //read sync configuration - writeConfig(enhPair.altSyncConfig->syncConfiguration, outAlt); + XmlOut outAltSync = outPair["SyncConfig"]; - outAlt["DeletionPolicy" ](enhPair.altSyncConfig->handleDeletion); - outAlt["CustomDeletionFolder"](enhPair.altSyncConfig->customDeletionDirectory); + writeConfig(*enhPair.altSyncConfig, outAltSync); } //########################################################### @@ -910,24 +1007,14 @@ void writeConfigFolderPair(const FolderPairEnh& enhPair, XmlOut& out) void writeConfig(const MainConfiguration& mainCfg, XmlOut& out) { - XmlOut outCmp = out["MainConfig"]["Comparison"]; - - //write compare variant - outCmp["Variant"](mainCfg.compareVar); - - //include symbolic links at all? - outCmp["HandleSymlinks"](mainCfg.handleSymlinks); + XmlOut outCmp = out["MainConfig"]["Comparison"]; + writeConfig(mainCfg.cmpConfig, outCmp); //########################################################### - XmlOut outSync = out["MainConfig"]["SyncConfig"]; - //write sync configuration - writeConfig(mainCfg.syncConfiguration, outSync); - //########################################################### + XmlOut outSync = out["MainConfig"]["SyncConfig"]; - //misc - outSync["DeletionPolicy" ](mainCfg.handleDeletion); - outSync["CustomDeletionFolder"](mainCfg.customDeletionDirectory); + writeConfig(mainCfg.syncCfg, outSync); //########################################################### XmlOut outFilter = out["MainConfig"]["GlobalFilter"]; @@ -1018,21 +1105,19 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outWnd["ManualDeletionUseRecycler"](config.gui.useRecyclerForManualDeletion); outWnd["RespectCaseOnSearch" ](config.gui.textSearchRespectCase); + outWnd["IconSize"](config.gui.iconSize); + //########################################################### //write column attributes XmlOut outColLeft = outWnd["LeftColumns"]; - outColLeft.attribute("AutoAdjust", config.gui.autoAdjustColumnsLeft); - outColLeft.attribute("ShowFileIcons", config.gui.showFileIconsLeft); outColLeft(config.gui.columnAttribLeft); //########################################################### XmlOut outColRight = outWnd["RightColumns"]; - outColRight.attribute("AutoAdjust", config.gui.autoAdjustColumnsRight); - outColRight.attribute("ShowFileIcons", config.gui.showFileIconsRight); outColRight(config.gui.columnAttribRight); diff --git a/library/process_xml.h b/library/process_xml.h index 8815e3fd..851b0d9c 100644 --- a/library/process_xml.h +++ b/library/process_xml.h @@ -75,22 +75,18 @@ struct XmlGuiConfig zen::MainConfiguration mainCfg; bool hideFilteredElements; - OnGuiError handleError; //reaction on error situation during synchronization bool syncPreviewEnabled; bool operator==(const XmlGuiConfig& other) const { - return mainCfg == other.mainCfg && - hideFilteredElements == other.hideFilteredElements && - handleError == other.handleError && - syncPreviewEnabled == other.syncPreviewEnabled; + return mainCfg == other.mainCfg && + hideFilteredElements == other.hideFilteredElements && + handleError == other.handleError && + syncPreviewEnabled == other.syncPreviewEnabled; } - bool operator!=(const XmlGuiConfig& other) const - { - return !(*this == other); - } + bool operator!=(const XmlGuiConfig& other) const { return !(*this == other); } }; @@ -112,10 +108,7 @@ struct XmlBatchConfig struct OptionalDialogs { - OptionalDialogs() - { - resetDialogs(); - } + OptionalDialogs() { resetDialogs();} void resetDialogs(); @@ -131,6 +124,14 @@ struct OptionalDialogs }; +enum FileIconSize +{ + ICON_SIZE_SMALL, + ICON_SIZE_MEDIUM, + ICON_SIZE_LARGE +}; + + wxString getGlobalConfigFile(); struct XmlGlobalSettings @@ -143,7 +144,7 @@ struct XmlGlobalSettings copyFilePermissions(false), fileTimeTolerance(2), //default 2s: FAT vs NTFS verifyFileCopy(false), - transactionalFileCopy(true) {} + transactionalFileCopy(true) {} int programLanguage; bool copyLockedFiles; //VSS usage @@ -151,7 +152,7 @@ struct XmlGlobalSettings size_t fileTimeTolerance; //max. allowed file time deviation bool verifyFileCopy; //verify copied files - bool transactionalFileCopy; + bool transactionalFileCopy; OptionalDialogs optDialogs; @@ -165,7 +166,7 @@ struct XmlGlobalSettings maxFolderPairsVisible(6), autoAdjustColumnsLeft(false), autoAdjustColumnsRight(false), - folderHistMax(12), + folderHistMax(15), deleteOnBothSides(false), useRecyclerForManualDeletion(true), //enable if OS supports it; else user will have to activate first and then get an error message #ifdef FFS_WIN @@ -173,8 +174,7 @@ struct XmlGlobalSettings #elif defined FFS_LINUX textSearchRespectCase(true), #endif - showFileIconsLeft(true), - showFileIconsRight(true), + iconSize(ICON_SIZE_MEDIUM), lastUpdateCheck(0) { //default external apps will be translated "on the fly"!!! @@ -208,15 +208,15 @@ struct XmlGlobalSettings std::vector<wxString> cfgFileHistory; std::vector<wxString> lastUsedConfigFiles; - std::vector<wxString> folderHistoryLeft; - std::vector<wxString> folderHistoryRight; + std::vector<Zstring> folderHistoryLeft; + std::vector<Zstring> folderHistoryRight; unsigned int folderHistMax; bool deleteOnBothSides; bool useRecyclerForManualDeletion; bool textSearchRespectCase; - bool showFileIconsLeft; - bool showFileIconsRight; + + FileIconSize iconSize; long lastUpdateCheck; //time of last update check @@ -252,17 +252,17 @@ bool sortByPositionAndVisibility(const ColumnAttrib& a, const ColumnAttrib& b) return a.position < b.position; } -void readConfig(const wxString& filename, XmlGuiConfig& config); //throw (xmlAccess::FfsXmlError) -void readConfig(const wxString& filename, XmlBatchConfig& config); //throw (xmlAccess::FfsXmlError) -void readConfig( XmlGlobalSettings& config); //throw (xmlAccess::FfsXmlError) +void readConfig(const wxString& filename, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError +void readConfig(const wxString& filename, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError +void readConfig( XmlGlobalSettings& config); //throw xmlAccess::FfsXmlError -void writeConfig(const XmlGuiConfig& config, const wxString& filename); //throw (xmlAccess::FfsXmlError) -void writeConfig(const XmlBatchConfig& config, const wxString& filename); //throw (xmlAccess::FfsXmlError) -void writeConfig(const XmlGlobalSettings& config); //throw (xmlAccess::FfsXmlError) +void writeConfig(const XmlGuiConfig& config, const wxString& filename); //throw xmlAccess::FfsXmlError +void writeConfig(const XmlBatchConfig& config, const wxString& filename); //throw xmlAccess::FfsXmlError +void writeConfig(const XmlGlobalSettings& config); //throw xmlAccess::FfsXmlError //config conversion utilities XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); -XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg); +XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg, const wxString& referenceFile); //convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: @@ -275,8 +275,8 @@ enum MergeType }; MergeType getMergeType(const std::vector<wxString>& filenames); //throw () -void convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config); //throw (xmlAccess::FfsXmlError) -void convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config); //throw (xmlAccess::FfsXmlError) +void convertConfig(const std::vector<wxString>& filenames, XmlGuiConfig& config); //throw xmlAccess::FfsXmlError +void convertConfig(const std::vector<wxString>& filenames, XmlBatchConfig& config); //throw xmlAccess::FfsXmlError } diff --git a/library/resources.cpp b/library/resources.cpp index cbfd7c06..d8dacc06 100644 --- a/library/resources.cpp +++ b/library/resources.cpp @@ -19,35 +19,13 @@ using namespace zen; const GlobalResources& GlobalResources::instance() { - static GlobalResources instance; - return instance; + static GlobalResources inst; + return inst; } -GlobalResources::GlobalResources() -{ - //init all the other resource files - animationMoney = new wxAnimation(wxNullAnimation); - animationSync = new wxAnimation(wxNullAnimation); - programIcon = new wxIcon(wxNullIcon); - - load(); -} - - -GlobalResources::~GlobalResources() +namespace { - //free bitmap resources - for (std::map<wxString, wxBitmap*>::iterator i = bitmapResource.begin(); i != bitmapResource.end(); ++i) - delete i->second; - - //free other resources - delete animationMoney; - delete animationSync; - delete programIcon; -} - - void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation* animation) { //Workaround for wxWidgets: @@ -64,11 +42,17 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation* animation) animation->Load(seekAbleStream, wxANIMATION_TYPE_GIF); } +} -void GlobalResources::load() +GlobalResources::GlobalResources() { - wxFFileInputStream input(zen::getResourceDir() + wxT("Resources.dat")); + //init all the other resource files + animationMoney = new wxAnimation(wxNullAnimation); + animationSync = new wxAnimation(wxNullAnimation); + programIcon = new wxIcon(wxNullIcon); + + wxFFileInputStream input(zen::getResourceDir() + wxT("Resources.zip")); if (input.IsOk()) //if not... we don't want to react too harsh here { //activate support for .png files @@ -78,7 +62,7 @@ void GlobalResources::load() while (true) { - std::auto_ptr<wxZipEntry> entry(resourceFile.GetNextEntry()); + std::unique_ptr<wxZipEntry> entry(resourceFile.GetNextEntry()); if (entry.get() == NULL) break; @@ -102,19 +86,37 @@ void GlobalResources::load() *programIcon = wxIcon(wxT("A_PROGRAM_ICON")); #else //use big logo bitmap for better quality - programIcon->CopyFromBitmap(getImage(wxT("FreeFileSync.png"))); + programIcon->CopyFromBitmap(getImageInt(wxT("FreeFileSync.png"))); + //attention: this is the reason we need a member getImage -> it must not implicitly create static object instance!!! + //erroneously calling static object constructor twice will deadlock on Linux!! #endif } -const wxBitmap& GlobalResources::getImage(const wxString& imageName) const +GlobalResources::~GlobalResources() { - const std::map<wxString, wxBitmap*>::const_iterator bmp = imageName.Find(wxChar('.')) == wxNOT_FOUND ? //assume .png ending if nothing else specified - bitmapResource.find(imageName + wxT(".png")) : - bitmapResource.find(imageName); + //free bitmap resources + for (std::map<wxString, wxBitmap*>::iterator i = bitmapResource.begin(); i != bitmapResource.end(); ++i) + delete i->second; + //free other resources + delete animationMoney; + delete animationSync; + delete programIcon; +} + + +const wxBitmap& GlobalResources::getImageInt(const wxString& imageName) const +{ + const std::map<wxString, wxBitmap*>::const_iterator bmp = bitmapResource.find( + imageName.Find(L'.') == wxNOT_FOUND ? //assume .png ending if nothing else specified + imageName + wxT(".png") : + imageName); if (bmp != bitmapResource.end()) return *bmp->second; else + { + assert(false); return wxNullBitmap; + } } diff --git a/library/resources.h b/library/resources.h index f812ac90..e985d9bf 100644 --- a/library/resources.h +++ b/library/resources.h @@ -16,9 +16,13 @@ class GlobalResources { public: - static const GlobalResources& instance(); + static const wxBitmap& getImage(const wxString& name) + { + const GlobalResources& inst = instance(); + return inst.getImageInt(name); + } - const wxBitmap& getImage(const wxString& imageName) const; + static const GlobalResources& instance(); //global image resource objects wxAnimation* animationMoney; @@ -26,10 +30,13 @@ public: wxIcon* programIcon; private: - void load(); //loads bitmap resources on program startup - GlobalResources(); ~GlobalResources(); + GlobalResources(const GlobalResources&); + GlobalResources& operator=(const GlobalResources&); + + const wxBitmap& getImageInt(const wxString& name) const; + //resource mapping std::map<wxString, wxBitmap*> bitmapResource; diff --git a/library/soft_filter.h b/library/soft_filter.h index bde812b0..f3c1bd41 100644 --- a/library/soft_filter.h +++ b/library/soft_filter.h @@ -28,9 +28,9 @@ public: size_t sizeMin, UnitSize unitSizeMin, size_t sizeMax, UnitSize unitSizeMax); - bool matchTime(Int64 writeTime) const { return currentTime - writeTime <= timeSpan_; } + bool matchTime(Int64 writeTime) const { return timeFrom_ <= writeTime; } bool matchSize(UInt64 fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } - bool matchFolder() const { return timeSpan_ == std::numeric_limits<Int64>::max(); } + bool matchFolder() const { return timeFrom_ == std::numeric_limits<Int64>::min(); } //if date filter is active we deactivate all folders: effectively gets rid of empty folders! bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower @@ -39,14 +39,13 @@ public: friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second); private: - SoftFilter(Int64 timeSpan, - UInt64 sizeMin, - UInt64 sizeMax); - - Int64 timeSpan_; //unit: seconds - UInt64 sizeMin_; //unit: bytes - UInt64 sizeMax_; //unit: bytes - Int64 currentTime; + SoftFilter(const Int64& timeFrom, + const UInt64& sizeMin, + const UInt64& sizeMax); + + Int64 timeFrom_; //unit: UTC, seconds + UInt64 sizeMin_; //unit: bytes + UInt64 sizeMax_; //unit: bytes }; } @@ -84,30 +83,28 @@ namespace zen inline SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, size_t sizeMin, UnitSize unitSizeMin, - size_t sizeMax, UnitSize unitSizeMax) : - currentTime(wxGetUTCTime()) + size_t sizeMax, UnitSize unitSizeMax) { resolveUnits(timeSpan, unitTimeSpan, sizeMin, unitSizeMin, sizeMax, unitSizeMax, - timeSpan_, //unit: seconds - sizeMin_, //unit: bytes - sizeMax_); //unit: bytes + timeFrom_, + sizeMin_, + sizeMax_); } inline -SoftFilter::SoftFilter(Int64 timeSpan, - UInt64 sizeMin, - UInt64 sizeMax) : - timeSpan_(timeSpan), +SoftFilter::SoftFilter(const Int64& timeFrom, + const UInt64& sizeMin, + const UInt64& sizeMax) : + timeFrom_(timeFrom), sizeMin_ (sizeMin), - sizeMax_ (sizeMax), - currentTime(wxGetUTCTime()) {} + sizeMax_ (sizeMax) {} inline SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second) { - return SoftFilter(std::min(first.timeSpan_, second.timeSpan_), + return SoftFilter(std::max(first.timeFrom_, second.timeFrom_), std::max(first.sizeMin_, second.sizeMin_), std::min(first.sizeMax_, second.sizeMax_)); } @@ -115,7 +112,7 @@ SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second) inline bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower { - return timeSpan_ == std::numeric_limits<Int64>::max() && + return timeFrom_ == std::numeric_limits<Int64>::min() && sizeMin_ == 0U && sizeMax_ == std::numeric_limits<UInt64>::max(); } diff --git a/library/statistics.cpp b/library/statistics.cpp index a29f60af..f6ec01ad 100644 --- a/library/statistics.cpp +++ b/library/statistics.cpp @@ -24,8 +24,6 @@ RetrieveStatistics::RetrieveStatistics() : RetrieveStatistics::~RetrieveStatistics() { - //keep non-inline destructor for std::auto_ptr to work with forward declaration - //write statistics to a file wxFFile outputFile(wxT("statistics.dat"), wxT("w")); diff --git a/library/statistics.h b/library/statistics.h index 0ec82c14..cfa3e07c 100644 --- a/library/statistics.h +++ b/library/statistics.h @@ -35,7 +35,7 @@ private: }; std::vector<StatEntry> data; - std::auto_ptr<wxStopWatch> timer; + std::unique_ptr<wxStopWatch> timer; }; diff --git a/library/status_handler.cpp b/library/status_handler.cpp index 9c2fdd67..55f82c64 100644 --- a/library/status_handler.cpp +++ b/library/status_handler.cpp @@ -6,8 +6,7 @@ #include "status_handler.h" #include <wx/app.h> -#include <wx/timer.h> - +#include <ctime> void updateUiNow() { @@ -21,12 +20,14 @@ void updateUiNow() bool updateUiIsAllowed() { - static wxMilliClock_t lastExec = 0; - const wxMilliClock_t newExec = wxGetLocalTimeMillis(); + const std::clock_t CLOCK_UPDATE_INTERVAL = UI_UPDATE_INTERVAL * CLOCKS_PER_SEC / 1000; + + static std::clock_t lastExec = 0; + const std::clock_t now = std::clock(); //this is quite fast: 2 * 10^-5 - if (newExec - lastExec >= UI_UPDATE_INTERVAL) //perform ui updates not more often than necessary + if (now - lastExec >= CLOCK_UPDATE_INTERVAL) //perform ui updates not more often than necessary { - lastExec = newExec; + lastExec = now; return true; } return false; diff --git a/library/status_handler.h b/library/status_handler.h index a295b6a0..1282f9f1 100644 --- a/library/status_handler.h +++ b/library/status_handler.h @@ -11,7 +11,7 @@ #include <string> #include "../shared/int64.h" -const int UI_UPDATE_INTERVAL = 100; //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss +const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss bool updateUiIsAllowed(); //test if a specific amount of time is over void updateUiNow(); //do the updating @@ -37,9 +37,6 @@ struct ProcessCallback //these methods have to be implemented in the derived classes to handle error and status information virtual void initNewProcess(int objectsTotal, zen::Int64 dataTotal, Process processID) = 0; //informs about the total amount of data that will be processed from now on - //called periodically after data was processed: expected(!) to update GUI! - virtual void reportInfo(const wxString& text) = 0; - //note: this one must NOT throw in order to properly allow undoing setting of statistics! //it is in general paired with a call to requestUiRefresh() to compensate! virtual void updateProcessedData(int objectsProcessed, zen::Int64 dataProcessed) = 0; //throw() @@ -50,15 +47,22 @@ struct ProcessCallback //this method is triggered repeatedly by requestUiRefresh() and can be used to refresh the ui by dispatching pending events virtual void forceUiRefresh() = 0; + //called periodically after data was processed: expected(!) to request GUI update + virtual void reportStatus(const wxString& text) = 0; //status info only, should not be logged! + + //called periodically after data was processed: expected(!) to request GUI update + virtual void reportInfo(const wxString& text) = 0; + + virtual void reportWarning(const wxString& warningMessage, bool& warningActive) = 0; + //error handling: enum Response { IGNORE_ERROR = 10, RETRY }; - virtual Response reportError (const wxString& errorMessage) = 0; //recoverable error situation - virtual void reportFatalError(const wxString& errorMessage) = 0; //non-recoverable error situation, implement abort! - virtual void reportWarning (const wxString& warningMessage, bool& warningActive) = 0; + virtual Response reportError (const wxString& errorMessage) = 0; //recoverable error situation + virtual void reportFatalError(const wxString& errorMessage) = 0; //non-recoverable error situation }; |