summaryrefslogtreecommitdiff
path: root/library
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:14:37 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:14:37 +0200
commit8bf668665b107469086f16cb8ad23e47d479d2b4 (patch)
tree66a91ef06a8caa7cd6819dcbe1860693d3eda8d5 /library
parent3.21 (diff)
downloadFreeFileSync-8bf668665b107469086f16cb8ad23e47d479d2b4.tar.gz
FreeFileSync-8bf668665b107469086f16cb8ad23e47d479d2b4.tar.bz2
FreeFileSync-8bf668665b107469086f16cb8ad23e47d479d2b4.zip
4.0
Diffstat (limited to 'library')
-rw-r--r--library/binary.cpp15
-rw-r--r--library/custom_grid.cpp499
-rw-r--r--library/custom_grid.h41
-rw-r--r--library/db_file.cpp111
-rw-r--r--library/db_file.h4
-rw-r--r--library/dir_exist_async.h4
-rw-r--r--library/dir_lock.cpp126
-rw-r--r--library/dir_lock.h2
-rw-r--r--library/hard_filter.cpp16
-rw-r--r--library/icon_buffer.cpp533
-rw-r--r--library/icon_buffer.h32
-rw-r--r--library/lock_holder.h6
-rw-r--r--library/parallel_scan.cpp128
-rw-r--r--library/parallel_scan.h2
-rw-r--r--library/process_xml.cpp295
-rw-r--r--library/process_xml.h62
-rw-r--r--library/resources.cpp68
-rw-r--r--library/resources.h15
-rw-r--r--library/soft_filter.h43
-rw-r--r--library/statistics.cpp2
-rw-r--r--library/statistics.h2
-rw-r--r--library/status_handler.cpp13
-rw-r--r--library/status_handler.h18
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
};
bgstack15