File Browser (in progress)

Make file and folder add/delete/rename detection from outside work.
This commit is contained in:
Don Ho 2016-01-28 19:05:41 +01:00
parent a1c608ba61
commit 0ad1964d1d
4 changed files with 440 additions and 319 deletions

View File

@ -580,7 +580,7 @@ POINT FileBrowser::getMenuDisplayPoint(int iButton)
return p; return p;
} }
HTREEITEM FileBrowser::addFolder(HTREEITEM hTreeItem, const TCHAR *folderName) HTREEITEM FileBrowser::createNewFolder(HTREEITEM hTreeItem, const TCHAR *folderName)
{ {
HTREEITEM addedItem = _treeView.addItem(folderName, hTreeItem, INDEX_CLOSED_NODE); HTREEITEM addedItem = _treeView.addItem(folderName, hTreeItem, INDEX_CLOSED_NODE);
@ -639,7 +639,7 @@ void FileBrowser::popupMenuCmd(int cmdID)
{ {
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance())->getNativeLangSpeaker(); NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance())->getNativeLangSpeaker();
generic_string newFolderLabel = pNativeSpeaker->getAttrNameStr(PM_NEWFOLDERNAME, "ProjectManager", "NewFolderName"); generic_string newFolderLabel = pNativeSpeaker->getAttrNameStr(PM_NEWFOLDERNAME, "ProjectManager", "NewFolderName");
addFolder(hTreeItem, newFolderLabel.c_str()); createNewFolder(hTreeItem, newFolderLabel.c_str());
} }
break; break;
@ -655,6 +655,7 @@ void FileBrowser::popupMenuCmd(int cmdID)
} }
break; break;
/*
case IDM_FILEBROWSER_ADDFILES : case IDM_FILEBROWSER_ADDFILES :
{ {
addFiles(hTreeItem); addFiles(hTreeItem);
@ -662,6 +663,7 @@ void FileBrowser::popupMenuCmd(int cmdID)
_treeView.setItemImage(hTreeItem, INDEX_OPEN_NODE, INDEX_OPEN_NODE); _treeView.setItemImage(hTreeItem, INDEX_OPEN_NODE, INDEX_OPEN_NODE);
} }
break; break;
*/
case IDM_FILEBROWSER_DELETEFOLDER : case IDM_FILEBROWSER_DELETEFOLDER :
{ {
@ -701,84 +703,12 @@ void FileBrowser::popupMenuCmd(int cmdID)
} }
} }
void FileBrowser::addFiles(HTREEITEM hTreeItem)
{
FileDialog fDlg(_hSelf, ::GetModuleHandle(NULL));
fDlg.setExtFilter(TEXT("All types"), TEXT(".*"), NULL);
if (stringVector *pfns = fDlg.doOpenMultiFilesDlg())
{
size_t sz = pfns->size();
for (size_t i = 0 ; i < sz ; ++i)
{
TCHAR *strValueLabel = ::PathFindFileName(pfns->at(i).c_str());
_treeView.addItem(strValueLabel, hTreeItem, INDEX_LEAF, pfns->at(i).c_str());
}
_treeView.expand(hTreeItem);
}
}
void FileBrowser::recursiveAddFilesFrom(const TCHAR *folderPath, HTREEITEM hTreeItem)
{
bool isRecursive = true;
bool isInHiddenDir = false;
generic_string dirFilter(folderPath);
if (folderPath[lstrlen(folderPath)-1] != '\\')
dirFilter += TEXT("\\");
dirFilter += TEXT("*.*");
WIN32_FIND_DATA foundData;
std::vector<generic_string> files;
HANDLE hFile = ::FindFirstFile(dirFilter.c_str(), &foundData);
do {
if (hFile == INVALID_HANDLE_VALUE)
break;
if (foundData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (!isInHiddenDir && (foundData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
{
// do nothing
}
else if (isRecursive)
{
if ((lstrcmp(foundData.cFileName, TEXT("."))) && (lstrcmp(foundData.cFileName, TEXT(".."))))
{
generic_string pathDir(folderPath);
if (folderPath[lstrlen(folderPath)-1] != '\\')
pathDir += TEXT("\\");
pathDir += foundData.cFileName;
pathDir += TEXT("\\");
HTREEITEM addedItem = addFolder(hTreeItem, foundData.cFileName);
recursiveAddFilesFrom(pathDir.c_str(), addedItem);
}
}
}
else
{
files.push_back(foundData.cFileName);
}
} while (::FindNextFile(hFile, &foundData));
for (size_t i = 0, len = files.size() ; i < len ; ++i)
{
generic_string pathFile(folderPath);
if (folderPath[lstrlen(folderPath)-1] != '\\')
pathFile += TEXT("\\");
pathFile += files[i];
_treeView.addItem(files[i].c_str(), hTreeItem, INDEX_LEAF, pathFile.c_str());
}
::FindClose(hFile);
}
void FileBrowser::getDirectoryStructure(const TCHAR *dir, const std::vector<generic_string> & patterns, FolderInfo & directoryStructure, bool isRecursive, bool isInHiddenDir) void FileBrowser::getDirectoryStructure(const TCHAR *dir, const std::vector<generic_string> & patterns, FolderInfo & directoryStructure, bool isRecursive, bool isInHiddenDir)
{ {
directoryStructure.setPath(dir); if (directoryStructure._parent == nullptr) // Root!
directoryStructure.setRootPath(dir);
generic_string dirFilter(dir); generic_string dirFilter(dir);
dirFilter += TEXT("*.*"); dirFilter += TEXT("*.*");
@ -803,7 +733,7 @@ void FileBrowser::getDirectoryStructure(const TCHAR *dir, const std::vector<gene
pathDir += foundData.cFileName; pathDir += foundData.cFileName;
pathDir += TEXT("\\"); pathDir += TEXT("\\");
FolderInfo subDirectoryStructure; FolderInfo subDirectoryStructure(foundData.cFileName, &directoryStructure);
getDirectoryStructure(pathDir.c_str(), patterns, subDirectoryStructure, isRecursive, isInHiddenDir); getDirectoryStructure(pathDir.c_str(), patterns, subDirectoryStructure, isRecursive, isInHiddenDir);
directoryStructure.addSubFolder(subDirectoryStructure); directoryStructure.addSubFolder(subDirectoryStructure);
} }
@ -813,12 +743,11 @@ void FileBrowser::getDirectoryStructure(const TCHAR *dir, const std::vector<gene
{ {
if (matchInList(foundData.cFileName, patterns)) if (matchInList(foundData.cFileName, patterns))
{ {
generic_string pathFile(dir); directoryStructure.addFile(foundData.cFileName);
pathFile += foundData.cFileName;
directoryStructure.addFile(pathFile.c_str());
} }
} }
} }
while (::FindNextFile(hFile, &foundData)) while (::FindNextFile(hFile, &foundData))
{ {
if (foundData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (foundData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
@ -835,7 +764,7 @@ void FileBrowser::getDirectoryStructure(const TCHAR *dir, const std::vector<gene
pathDir += foundData.cFileName; pathDir += foundData.cFileName;
pathDir += TEXT("\\"); pathDir += TEXT("\\");
FolderInfo subDirectoryStructure; FolderInfo subDirectoryStructure(foundData.cFileName, &directoryStructure);
getDirectoryStructure(pathDir.c_str(), patterns, subDirectoryStructure, isRecursive, isInHiddenDir); getDirectoryStructure(pathDir.c_str(), patterns, subDirectoryStructure, isRecursive, isInHiddenDir);
directoryStructure.addSubFolder(subDirectoryStructure); directoryStructure.addSubFolder(subDirectoryStructure);
} }
@ -845,9 +774,7 @@ void FileBrowser::getDirectoryStructure(const TCHAR *dir, const std::vector<gene
{ {
if (matchInList(foundData.cFileName, patterns)) if (matchInList(foundData.cFileName, patterns))
{ {
generic_string pathFile(dir); directoryStructure.addFile(foundData.cFileName);
pathFile += foundData.cFileName;
directoryStructure.addFile(pathFile.c_str());
} }
} }
} }
@ -856,40 +783,40 @@ void FileBrowser::getDirectoryStructure(const TCHAR *dir, const std::vector<gene
void FileBrowser::addRootFolder(generic_string rootFolderPath) void FileBrowser::addRootFolder(generic_string rootFolderPath)
{ {
for (size_t i = 0; i < _folderUpdaters.size(); ++i) size_t nbFolderUpdaters = _folderUpdaters.size();
for (size_t i = 0; i < nbFolderUpdaters; ++i)
{ {
if (_folderUpdaters[i]._rootFolder._path == rootFolderPath) if (_folderUpdaters[i]._rootFolder._rootPath == rootFolderPath)
return; return;
} }
std::vector<generic_string> patterns2Match; std::vector<generic_string> patterns2Match;
patterns2Match.push_back(TEXT("*.*")); patterns2Match.push_back(TEXT("*.*"));
FolderInfo directoryStructure; TCHAR *label = ::PathFindFileName(rootFolderPath.c_str());
FolderInfo directoryStructure(label, nullptr);
getDirectoryStructure(rootFolderPath.c_str(), patterns2Match, directoryStructure, true, false); getDirectoryStructure(rootFolderPath.c_str(), patterns2Match, directoryStructure, true, false);
HTREEITEM hRootItem = createFolderItemsFromDirStruct(nullptr, directoryStructure); HTREEITEM hRootItem = createFolderItemsFromDirStruct(nullptr, directoryStructure);
_treeView.expand(hRootItem); _treeView.expand(hRootItem);
_folderUpdaters.push_back(FolderUpdater(directoryStructure, _hSelf)); _folderUpdaters.push_back(FolderUpdater(directoryStructure, this));
_folderUpdaters[_folderUpdaters.size() - 1].startWatcher(); _folderUpdaters[_folderUpdaters.size() - 1].startWatcher();
} }
HTREEITEM FileBrowser::createFolderItemsFromDirStruct(HTREEITEM hParentItem, const FolderInfo & directoryStructure) HTREEITEM FileBrowser::createFolderItemsFromDirStruct(HTREEITEM hParentItem, const FolderInfo & directoryStructure)
{
HTREEITEM hFolderItem = nullptr;
if (directoryStructure._parent == nullptr && hParentItem == nullptr)
{ {
TCHAR rootPath[MAX_PATH]; TCHAR rootPath[MAX_PATH];
lstrcpy(rootPath, directoryStructure._path.c_str()); lstrcpy(rootPath, directoryStructure._rootPath.c_str());
size_t len = lstrlen(rootPath); size_t len = lstrlen(rootPath);
if (rootPath[len - 1] == '\\') if (rootPath[len - 1] == '\\')
rootPath[len - 1] = '\0'; rootPath[len - 1] = '\0';
hFolderItem = _treeView.addItem(directoryStructure._name.c_str(), TVI_ROOT, INDEX_CLOSED_NODE, rootPath);
TCHAR *rootName = ::PathFindFileName(rootPath);
HTREEITEM hFolderItem = nullptr;
if (hParentItem == nullptr)
{
hFolderItem = _treeView.addItem(rootName, TVI_ROOT, INDEX_CLOSED_NODE);
} }
else else
{ {
hFolderItem = addFolder(hParentItem, rootName); hFolderItem = _treeView.addItem(directoryStructure._name.c_str(), hParentItem, INDEX_CLOSED_NODE);
} }
for (size_t i = 0; i < directoryStructure._subFolders.size(); ++i) for (size_t i = 0; i < directoryStructure._subFolders.size(); ++i)
@ -899,216 +826,326 @@ HTREEITEM FileBrowser::createFolderItemsFromDirStruct(HTREEITEM hParentItem, con
for (size_t i = 0; i < directoryStructure._files.size(); ++i) for (size_t i = 0; i < directoryStructure._files.size(); ++i)
{ {
TCHAR filePath[MAX_PATH]; _treeView.addItem(directoryStructure._files[i]._name.c_str(), hFolderItem, INDEX_LEAF);
lstrcpy(filePath, directoryStructure._files[i]._path.c_str());
TCHAR *fileName = ::PathFindFileName(filePath);
_treeView.addItem(fileName, hFolderItem, INDEX_LEAF, directoryStructure._files[i]._path.c_str());
} }
_treeView.fold(hParentItem); _treeView.fold(hParentItem);
return hFolderItem; return hFolderItem;
} }
bool FolderInfo::compare(const FolderInfo & struct2compare, std::vector<changeInfo> & result) HTREEITEM FileBrowser::getRootFromFullPath(generic_string rootPath)
{ {
if (_contentHash == struct2compare._contentHash) HTREEITEM node = nullptr;
for (HTREEITEM hItemNode = _treeView.getRoot();
hItemNode != nullptr && node == nullptr;
hItemNode = _treeView.getNextSibling(hItemNode))
{ {
if (_path == struct2compare._path) // Everything is fine TVITEM tvItem;
{ tvItem.mask = TVIF_PARAM;
return true; tvItem.cchTextMax = MAX_PATH;
tvItem.hItem = hItemNode;
SendMessage(_treeView.getHSelf(), TVM_GETITEM, 0, (LPARAM)&tvItem);
if (tvItem.lParam != 0 && rootPath == *((generic_string *)tvItem.lParam))
node = hItemNode;
} }
else // folder is renamed return node;
{
// put result in the vector
changeInfo info;
info._action = info.rename;
info._fullFilePath = struct2compare._path;
return false;
}
}
else // sub-folders or files/sub-files deleted or renamed
{
if (_path != struct2compare._path) // both content and path are different, stop to compare
{
return true; // everything could be fine
} }
//check folder (maybe go deeper) HTREEITEM FileBrowser::findChildNodeFromName(HTREEITEM parent, generic_string label)
for (size_t i = 0; i < _subFolders.size(); ++i)
{ {
bool isFound = false; HTREEITEM childNodeFound = nullptr;
for (size_t j = 0; j < struct2compare._subFolders.size(); ++j)
for (HTREEITEM hItemNode = _treeView.getChildFrom(parent);
hItemNode != NULL && childNodeFound == nullptr;
hItemNode = _treeView.getNextSibling(hItemNode))
{ {
if ((_subFolders[i]._path == struct2compare._subFolders[j]._path) && TCHAR textBuffer[MAX_PATH];
(_subFolders[i]._contentHash == struct2compare._subFolders[j]._contentHash)) TVITEM tvItem;
tvItem.mask = TVIF_TEXT;
tvItem.pszText = textBuffer;
tvItem.cchTextMax = MAX_PATH;
tvItem.hItem = hItemNode;
SendMessage(_treeView.getHSelf(), TVM_GETITEM, 0, (LPARAM)&tvItem);
if (label == tvItem.pszText)
{ {
isFound = true; childNodeFound = hItemNode;
}
}
return childNodeFound;
}
bool FileBrowser::addInTree(generic_string rootPath, generic_string addItemFullPath, HTREEITEM node, vector<generic_string> linarPathArray)
{
if (node == nullptr) // it's a root. Search the right root with rootPath
{
// Search
if ((node = getRootFromFullPath(rootPath)) == nullptr)
return false;
}
if (linarPathArray.size() == 1)
{
// Search : if no found, add
HTREEITEM childNodeFound = findChildNodeFromName(node, linarPathArray[0]);
if (childNodeFound != nullptr)
return false;
// No found, good - Action
if (::PathIsDirectory(addItemFullPath.c_str()))
{
_treeView.addItem(linarPathArray[0].c_str(), node, INDEX_CLOSED_NODE);
} }
else else
{ {
if ((_subFolders[i]._path != struct2compare._subFolders[j]._path) && _treeView.addItem(linarPathArray[0].c_str(), node, INDEX_LEAF);
(_subFolders[i]._contentHash == struct2compare._subFolders[j]._contentHash)) // rename }
return true;
}
else
{ {
changeInfo info; HTREEITEM childNodeFound = nullptr;
info._action = info.rename; for (HTREEITEM hItemNode = _treeView.getChildFrom(node);
info._fullFilePath = struct2compare._path; hItemNode != NULL && childNodeFound == nullptr;
} hItemNode = _treeView.getNextSibling(hItemNode))
else if ((_subFolders[i]._path == struct2compare._subFolders[j]._path) &&
(_subFolders[i]._contentHash != struct2compare._subFolders[j]._contentHash)) // problem of sub-files or sub-folders. go deeper
{ {
_subFolders[i].compare(struct2compare._subFolders[j], result); TCHAR textBuffer[MAX_PATH];
} TVITEM tvItem;
} tvItem.mask = TVIF_TEXT;
} tvItem.pszText = textBuffer;
if (not isFound) // folder is deleted tvItem.cchTextMax = MAX_PATH;
tvItem.hItem = hItemNode;
SendMessage(_treeView.getHSelf(), TVM_GETITEM, 0, (LPARAM)&tvItem);
if (linarPathArray[0] == tvItem.pszText)
{ {
// put result in the vector // search recursively the node for an action
changeInfo info; linarPathArray.erase(linarPathArray.begin());
info._action = info.remove; return addInTree(rootPath, addItemFullPath, hItemNode, linarPathArray);
info._fullFilePath = _subFolders[i]._path; }
result.push_back(info); }
return false;
} }
} }
//check files bool FileBrowser::deleteFromTree(generic_string rootPath, HTREEITEM node, std::vector<generic_string> linarPathArray)
{
if (node == nullptr) // it's a root. Search the right root with rootPath
{
// Search
if ((node = getRootFromFullPath(rootPath)) == nullptr)
return false;
}
if (linarPathArray.size() == 1)
{
// Search
HTREEITEM childNodeFound = findChildNodeFromName(node, linarPathArray[0]);
if (childNodeFound == nullptr)
return false;
// found it, delete it
_treeView.removeItem(childNodeFound);
return true;
}
else
{
HTREEITEM childNodeFound = nullptr;
for (HTREEITEM hItemNode = _treeView.getChildFrom(node);
hItemNode != NULL && childNodeFound == nullptr;
hItemNode = _treeView.getNextSibling(hItemNode))
{
TCHAR textBuffer[MAX_PATH];
TVITEM tvItem;
tvItem.mask = TVIF_TEXT;
tvItem.pszText = textBuffer;
tvItem.cchTextMax = MAX_PATH;
tvItem.hItem = hItemNode;
SendMessage(_treeView.getHSelf(), TVM_GETITEM, 0, (LPARAM)&tvItem);
if (linarPathArray[0] == tvItem.pszText)
{
// search recursively the node for an action
linarPathArray.erase(linarPathArray.begin());
return deleteFromTree(rootPath, hItemNode, linarPathArray);
}
}
return false;
}
}
bool FileBrowser::renameInTree(generic_string rootPath, HTREEITEM node, std::vector<generic_string> linarPathArrayFrom, std::vector<generic_string> linarPathArrayTo)
{
if (node == nullptr) // it's a root. Search the right root with rootPath
{
// Search
if ((node = getRootFromFullPath(rootPath)) == nullptr)
return false;
}
if (linarPathArrayFrom.size() == 1)
{
// Search
HTREEITEM childNodeFound = findChildNodeFromName(node, linarPathArrayFrom[0]);
if (childNodeFound == nullptr)
return false;
// found it, rename it
_treeView.renameItem(childNodeFound, linarPathArrayTo[0].c_str());
return true;
}
else
{
HTREEITEM childNodeFound = nullptr;
for (HTREEITEM hItemNode = _treeView.getChildFrom(node);
hItemNode != NULL && childNodeFound == nullptr;
hItemNode = _treeView.getNextSibling(hItemNode))
{
TCHAR textBuffer[MAX_PATH];
TVITEM tvItem;
tvItem.mask = TVIF_TEXT;
tvItem.pszText = textBuffer;
tvItem.cchTextMax = MAX_PATH;
tvItem.hItem = hItemNode;
SendMessage(_treeView.getHSelf(), TVM_GETITEM, 0, (LPARAM)&tvItem);
if (linarPathArrayFrom[0] == tvItem.pszText)
{
// search recursively the node for an action
linarPathArrayFrom.erase(linarPathArrayFrom.begin());
linarPathArrayTo.erase(linarPathArrayTo.begin());
return renameInTree(rootPath, hItemNode, linarPathArrayFrom, linarPathArrayTo);
}
}
return false;
}
}
bool FolderInfo::addToStructure(generic_string & fullpath, std::vector<generic_string> linarPathArray)
{
if (linarPathArray.size() == 1) // could be file or folder
{
fullpath += TEXT("\\");
fullpath += linarPathArray[0];
if (PathIsDirectory(fullpath.c_str()))
{
// search in folders, if found - no good
size_t nbFolder = _subFolders.size();
for (size_t i = 0; i < nbFolder; ++i)
{
if (linarPathArray[0] == _subFolders[i].getName())
return false; // Maybe already added?
}
_subFolders.push_back(FolderInfo(linarPathArray[0], this));
return true;
}
else
{
// search in files, if found - no good
size_t nbFile = _files.size();
for (size_t i = 0; i < nbFile; ++i)
{
if (linarPathArray[0] == _files[i].getName())
return false; // Maybe already added?
}
_files.push_back(FileInfo(linarPathArray[0], this));
return true;
}
}
else // folder
{
size_t nbFolder = _subFolders.size();
for (size_t i = 0; i < nbFolder; ++i)
{
if (_subFolders[i].getName() == linarPathArray[0])
{
fullpath += TEXT("\\");
fullpath += linarPathArray[0];
linarPathArray.erase(linarPathArray.begin());
return _subFolders[i].addToStructure(fullpath, linarPathArray);
}
}
return false;
}
}
bool FolderInfo::removeFromStructure(std::vector<generic_string> linarPathArray)
{
if (linarPathArray.size() == 1) // could be file or folder
{
for (size_t i = 0; i < _files.size(); ++i) for (size_t i = 0; i < _files.size(); ++i)
{ {
bool isFound = false; if (_files[i].getName() == linarPathArray[0])
for (size_t j = 0; not isFound && j < struct2compare._files.size(); ++j)
{ {
if (_files[i]._path == struct2compare._files[j]._path) // remove this file
isFound = true; _files.erase(_files.begin() + i);
}
if (not isFound) // file is deleted
{
// put result in the vector
changeInfo info;
info._action = info.remove;
info._fullFilePath = _files[i]._path;
result.push_back(info);
}
}
/*
for (size_t i = 0; i < struct2compare._files.size(); ++i)
{
bool isFound = false;
for (size_t j = 0; not isFound && j < _files.size(); ++j)
{
if (_files[i]._path == struct2compare._files[j]._path)
isFound = true;
}
if (not isFound) // file is deleted
{
// put result in the vector
changeInfo info;
info._action = info.add;
info._fullFilePath = struct2compare._files[i]._path;
result.push_back(info);
}
}
*/
return true; return true;
} }
} }
bool FolderInfo::makeDiff(FolderInfo & struct1, FolderInfo & struct2, std::vector<changeInfo> result) for (size_t i = 0; i < _subFolders.size(); ++i)
{ {
if (struct1._contentHash == struct2._contentHash) if (_subFolders[i].getName() == linarPathArray[0])
{ {
if (struct1._path == struct2._path) // Everything is fine // remove this folder
{ _subFolders.erase(_subFolders.begin() + i);
// remove both
return true; return true;
} }
else // folder is renamed }
}
else // folder
{ {
// put result in the vector for (size_t i = 0; i < _subFolders.size(); ++i)
changeInfo info; {
info._action = info.rename; if (_subFolders[i].getName() == linarPathArray[0])
info._fullFilePath = struct2._path; {
result.push_back(info); linarPathArray.erase(linarPathArray.begin());
return _subFolders[i].removeFromStructure(linarPathArray);
}
}
}
return false; return false;
} }
}
else // sub-folders or files/sub-files deleted or renamed
{
if (struct1._path != struct2._path) // both content and path are different, stop to compare
{
return true; // everything could be fine
}
//check folder (maybe go deeper) bool FolderInfo::renameInStructure(std::vector<generic_string> linarPathArrayFrom, std::vector<generic_string> linarPathArrayTo)
for (int i = struct1._subFolders.size(); i >= 0; --i)
{ {
bool isFound = false; if (linarPathArrayFrom.size() == 1) // could be file or folder
for (int j = struct2._subFolders.size(); not isFound && j >= 0; --j)
{ {
if ((struct1._subFolders[i]._path == struct2._subFolders[j]._path) && for (size_t i = 0; i < _files.size(); ++i)
(struct1._subFolders[i]._contentHash == struct2._subFolders[j]._contentHash))
{ {
struct1._subFolders.erase(struct1._subFolders.begin() + i); if (_files[i].getName() == linarPathArrayFrom[0])
struct2._subFolders.erase(struct2._subFolders.begin() + j);
isFound = true;
}
else
{ {
if ((struct1._subFolders[i]._path != struct2._subFolders[j]._path) && // rename this file
(struct1._subFolders[i]._contentHash == struct2._subFolders[j]._contentHash)) // rename _files[i].setName(linarPathArrayTo[0]);
{
changeInfo info;
info._action = info.rename;
info._fullFilePath = struct2._path;
result.push_back(info);
}
else if ((struct1._subFolders[i]._path == struct2._subFolders[j]._path) &&
(struct1._subFolders[i]._contentHash != struct2._subFolders[j]._contentHash)) // problem of sub-files or sub-folders. go deeper
{
makeDiff(struct1._subFolders[i], struct2._subFolders[j], result);
}
}
}
if (not isFound) // folder is deleted
{
// put result in the vector
changeInfo info;
info._action = info.remove;
info._fullFilePath = struct1._subFolders[i]._path;
result.push_back(info);
}
}
//check files
for (int i = struct1._files.size(); i >= 0; --i)
{
bool isFound = false;
for (int j = struct2._files.size(); not isFound && j >= 0; --j)
{
if (struct1._files[i]._path == struct2._files[j]._path)
{
struct1._subFolders.erase(struct1._subFolders.begin() + i);
struct2._subFolders.erase(struct2._subFolders.begin() + j);
isFound = true;
}
}
if (not isFound) // file is deleted
{
// put result in the vector
changeInfo info;
info._action = info.remove;
info._fullFilePath = struct1._files[i]._path;
result.push_back(info);
}
}
return true; return true;
} }
} }
generic_string FolderInfo::getLabel() for (size_t i = 0; i < _subFolders.size(); ++i)
{ {
return ::PathFindFileName(_path.c_str()); if (_subFolders[i].getName() == linarPathArrayFrom[0])
{
// rename this folder
_subFolders[i].setName(linarPathArrayTo[0]);
return true;
}
}
return false;
}
else // folder
{
for (size_t i = 0; i < _subFolders.size(); ++i)
{
if (_subFolders[i].getName() == linarPathArrayFrom[0])
{
linarPathArrayFrom.erase(linarPathArrayFrom.begin());
linarPathArrayTo.erase(linarPathArrayTo.begin());
return _subFolders[i].renameInStructure(linarPathArrayFrom, linarPathArrayTo);
}
}
return false;
}
} }
void FolderUpdater::startWatcher() void FolderUpdater::startWatcher()
@ -1144,45 +1181,113 @@ LPCWSTR explainAction(DWORD dwAction)
} }
}; };
vector<generic_string> split(const generic_string & string2split, TCHAR sep)
{
vector<generic_string> splitedStrings;
size_t len = string2split.length();
size_t beginPos = 0;
for (size_t i = 0; i < len + 1; ++i)
{
if (string2split[i] == sep || string2split[i] == '\0')
{
splitedStrings.push_back(string2split.substr(beginPos, i - beginPos));
beginPos = i + 1;
}
}
return splitedStrings;
};
bool FolderUpdater::updateTree(DWORD action, const std::vector<generic_string> & file2Change) bool FolderUpdater::updateTree(DWORD action, const std::vector<generic_string> & file2Change)
{ {
//TCHAR msg2show[1024]; /*
TCHAR msg2show[1024];
switch (action) switch (action)
{ {
case FILE_ACTION_ADDED: case FILE_ACTION_ADDED:
//swprintf(msg2show, L"%s %s\n", explainAction(action), file2Change[0].c_str()); swprintf(msg2show, L"%s %s\n", explainAction(action), file2Change[0].c_str());
//printStr(msg2show); printStr(msg2show);
//::PostMessage(thisFolderUpdater->_hFileBrowser, FB_ADDFILE, nullptr, (LPARAM)wstrFilename.GetString()); //::PostMessage(thisFolderUpdater->_hFileBrowser, FB_ADDFILE, nullptr, (LPARAM)wstrFilename.GetString());
break; break;
case FILE_ACTION_REMOVED: case FILE_ACTION_REMOVED:
//swprintf(msg2show, L"%s %s\n", explainAction(action), file2Change[0].c_str()); swprintf(msg2show, L"%s %s\n", explainAction(action), file2Change[0].c_str());
//printStr(msg2show); printStr(msg2show);
break; break;
case FILE_ACTION_RENAMED_NEW_NAME: case FILE_ACTION_RENAMED_NEW_NAME:
//swprintf(msg2show, L"%s from %s \rto %s", explainAction(action), file2Change[0].c_str(), file2Change[1].c_str()); swprintf(msg2show, L"%s from %s \rto %s", explainAction(action), file2Change[0].c_str(), file2Change[1].c_str());
//printStr(msg2show); printStr(msg2show);
break; break;
default: default:
break; break;
} }
*/
generic_string separator = TEXT("\\\\"); generic_string separator = TEXT("\\\\");
size_t sepPos = file2Change[0].find(separator); size_t sepPos = file2Change[0].find(separator);
if (sepPos == generic_string::npos) if (sepPos == generic_string::npos)
return false; return false;
generic_string rootPrefix = file2Change[0].substr(0, sepPos);
generic_string pathSuffix = file2Change[0].substr(sepPos + separator.length(), file2Change[0].length() - 1); generic_string pathSuffix = file2Change[0].substr(sepPos + separator.length(), file2Change[0].length() - 1);
// found: remove prefix of file/folder in changeInfo, splite the remained path // remove prefix of file/folder in changeInfo, splite the remained path
vector<generic_string> linarPathArray = split(pathSuffix, '\\');
// search recursively according indication (file or folder) if (action == FILE_ACTION_ADDED)
{
generic_string rootPath = file2Change[0].substr(0, sepPos);
generic_string path = rootPath;
// search recursively and modify the tree structure
bool foundAndModified = _rootFolder.addToStructure(path, linarPathArray);
if (foundAndModified)
{
generic_string addedFilePath = file2Change[0].substr(0, sepPos + 1);
addedFilePath += pathSuffix;
bool isAdded = _pFileBrowser->addInTree(rootPath, addedFilePath, nullptr, linarPathArray);
if (not isAdded)
MessageBox(NULL, addedFilePath.c_str(), TEXT("file/folder is not added"), MB_OK);
}
else
printStr(TEXT("addToStructure pb"));
}
else if (action == FILE_ACTION_REMOVED)
{
generic_string rootPath = file2Change[0].substr(0, sepPos);
// search recursively and modify the tree structure
bool foundAndModified = _rootFolder.removeFromStructure(linarPathArray);
if (foundAndModified)
{
bool isRemoved = _pFileBrowser->deleteFromTree(rootPath, nullptr, linarPathArray);
if (not isRemoved)
MessageBox(NULL, file2Change[0].c_str(), TEXT("file/folder is not removed"), MB_OK);
}
else
printStr(TEXT("removeFromStructure pb"));
}
else if (action == FILE_ACTION_RENAMED_NEW_NAME)
{
generic_string rootPath = file2Change[0].substr(0, sepPos);
size_t sepPos2 = file2Change[1].find(separator);
if (sepPos2 == generic_string::npos)
return false;
generic_string pathSuffix2 = file2Change[1].substr(sepPos2 + separator.length(), file2Change[1].length() - 1);
vector<generic_string> linarPathArray2 = split(pathSuffix2, '\\');
bool foundAndModified = _rootFolder.renameInStructure(linarPathArray, linarPathArray2);
if (foundAndModified)
{
bool isRenamed = _pFileBrowser->renameInTree(rootPath, nullptr, linarPathArray, linarPathArray2);
if (not isRenamed)
MessageBox(NULL, file2Change[0].c_str(), TEXT("file/folder is not removed"), MB_OK);
}
else
printStr(TEXT("removeFromStructure pb"));
}
return true; return true;
} }
@ -1191,7 +1296,7 @@ bool FolderUpdater::updateTree(DWORD action, const std::vector<generic_string> &
DWORD WINAPI FolderUpdater::watching(void *params) DWORD WINAPI FolderUpdater::watching(void *params)
{ {
FolderUpdater *thisFolderUpdater = (FolderUpdater *)params; FolderUpdater *thisFolderUpdater = (FolderUpdater *)params;
const TCHAR *dir2Watch = (thisFolderUpdater->_rootFolder)._path.c_str(); const TCHAR *dir2Watch = (thisFolderUpdater->_rootFolder)._rootPath.c_str();
const DWORD dwNotificationFlags = FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME; const DWORD dwNotificationFlags = FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME;

View File

@ -60,52 +60,48 @@
class TiXmlNode; class TiXmlNode;
class changeInfo final
{
friend class FolderInfo;
public:
enum folderChangeAction{
add, remove, rename
};
private:
bool isFile; // true: file, false: folder
generic_string _fullFilePath;
std::vector<generic_string> _relativePath;
folderChangeAction _action;
};
class FileInfo final class FileInfo final
{ {
friend class FileBrowser; friend class FileBrowser;
friend class FolderInfo; friend class FolderInfo;
public: public:
FileInfo(const generic_string & fn) { _path = fn; }; FileInfo(const generic_string & name, FolderInfo *parent) : _name(name), _parent(parent) {};
generic_string getLabel() { return ::PathFindFileName(_path.c_str()); }; generic_string getName() const { return _name; };
void setName(generic_string name) { _name = name; };
private: private:
generic_string _path; FileInfo(){}; // constructor by default is forbidden
FolderInfo *_parent = nullptr;
generic_string _name;
}; };
class FolderInfo final class FolderInfo final
{ {
friend class FileBrowser; friend class FileBrowser;
friend class FolderUpdater; friend class FolderUpdater;
public: public:
void setPath(generic_string dn) { _path = dn; }; FolderInfo(const generic_string & name, FolderInfo *parent) : _name(name), _parent(parent) {};
void addFile(generic_string fn) { _files.push_back(FileInfo(fn)); }; void setRootPath(generic_string rootPath) { _rootPath = rootPath; };
generic_string getRootPath() const { return _rootPath; };
void setName(generic_string name) { _name = name; };
generic_string getName() const { return _name; };
void addFile(generic_string fn) { _files.push_back(FileInfo(fn, this)); };
void addSubFolder(FolderInfo subDirectoryStructure) { _subFolders.push_back(subDirectoryStructure); }; void addSubFolder(FolderInfo subDirectoryStructure) { _subFolders.push_back(subDirectoryStructure); };
bool compare(const FolderInfo & struct2compare, std::vector<changeInfo> & result);
static bool makeDiff(FolderInfo & struct1, FolderInfo & struct2static, std::vector<changeInfo> result); bool addToStructure(generic_string & fullpath, std::vector<generic_string> linarPathArray);
generic_string getLabel(); bool removeFromStructure(std::vector<generic_string> linarPathArray);
bool renameInStructure(std::vector<generic_string> linarPathArrayFrom, std::vector<generic_string> linarPathArrayTo);
private: private:
FolderInfo(){}; // constructor by default is forbidden
std::vector<FolderInfo> _subFolders; std::vector<FolderInfo> _subFolders;
std::vector<FileInfo> _files; std::vector<FileInfo> _files;
generic_string _path; FolderInfo *_parent = nullptr;
generic_string _contentHash; generic_string _name;
generic_string _rootPath; // set only for root folder; empty for normal folder
}; };
enum BrowserNodeType { enum BrowserNodeType {
@ -115,17 +111,16 @@ enum BrowserNodeType {
class FolderUpdater { class FolderUpdater {
friend class FileBrowser; friend class FileBrowser;
public: public:
FolderUpdater(FolderInfo fi, HWND hFileBrowser) : _rootFolder(fi), _hFileBrowser(hFileBrowser) {}; FolderUpdater(FolderInfo fi, FileBrowser *pFileBrowser) : _rootFolder(fi), _pFileBrowser(pFileBrowser) {};
~FolderUpdater() {}; ~FolderUpdater() {};
bool updateTree(DWORD action, const std::vector<generic_string> & file2Change); // postMessage to FileBrowser to upgrade GUI bool updateTree(DWORD action, const std::vector<generic_string> & file2Change); // postMessage to FileBrowser to upgrade GUI
void startWatcher(); void startWatcher();
void stopWatcher(); void stopWatcher();
private: private:
FolderInfo _rootFolder; FolderInfo _rootFolder;
HWND _hFileBrowser = nullptr; FileBrowser *_pFileBrowser = nullptr;
HANDLE _watchThreadHandle = nullptr; HANDLE _watchThreadHandle = nullptr;
HANDLE _EventHandle = nullptr; HANDLE _EventHandle = nullptr;
@ -157,6 +152,13 @@ public:
void addRootFolder(generic_string); void addRootFolder(generic_string);
HTREEITEM getRootFromFullPath(generic_string rootPath);
HTREEITEM FileBrowser::findChildNodeFromName(HTREEITEM parent, generic_string);
bool addInTree(generic_string rootPath, generic_string addItemFullPath, HTREEITEM node, std::vector<generic_string> linarPathArray);
bool deleteFromTree(generic_string rootPath, HTREEITEM node, std::vector<generic_string> linarPathArray);
bool renameInTree(generic_string rootPath, HTREEITEM node, std::vector<generic_string> linarPathArrayFrom, std::vector<generic_string> linarPathArrayTo);
protected: protected:
TreeView _treeView; TreeView _treeView;
HIMAGELIST _hImaLst; HIMAGELIST _hImaLst;
@ -170,9 +172,8 @@ protected:
void initMenus(); void initMenus();
void destroyMenus(); void destroyMenus();
BOOL setImageList(int root_clean_id, int root_dirty_id, int project_id, int open_node_id, int closed_node_id, int leaf_id, int ivalid_leaf_id); BOOL setImageList(int root_clean_id, int root_dirty_id, int project_id, int open_node_id, int closed_node_id, int leaf_id, int ivalid_leaf_id);
void addFiles(HTREEITEM hTreeItem);
void recursiveAddFilesFrom(const TCHAR *folderPath, HTREEITEM hTreeItem); HTREEITEM createNewFolder(HTREEITEM hTreeItem, const TCHAR *folderName);
HTREEITEM addFolder(HTREEITEM hTreeItem, const TCHAR *folderName);
generic_string getRelativePath(const generic_string & fn, const TCHAR *workSpaceFileName); generic_string getRelativePath(const generic_string & fn, const TCHAR *workSpaceFileName);
void buildProjectXml(TiXmlNode *root, HTREEITEM hItem, const TCHAR* fn2write); void buildProjectXml(TiXmlNode *root, HTREEITEM hItem, const TCHAR* fn2write);

View File

@ -91,6 +91,20 @@ bool TreeView::setItemParam(HTREEITEM Item2Set, const TCHAR *paramStr)
return true; return true;
} }
bool TreeView::renameItem(HTREEITEM Item2Set, const TCHAR *newName)
{
if (not Item2Set || not newName)
return false;
TVITEM tvItem;
tvItem.hItem = Item2Set;
tvItem.mask = TVIF_TEXT;
tvItem.pszText = (LPWSTR)newName;
tvItem.cchTextMax = MAX_PATH;
SendMessage(_hSelf, TVM_SETITEM, 0, (LPARAM)&tvItem);
return true;
}
HTREEITEM TreeView::addItem(const TCHAR *itemName, HTREEITEM hParentItem, int iImage, const TCHAR *filePath) HTREEITEM TreeView::addItem(const TCHAR *itemName, HTREEITEM hParentItem, int iImage, const TCHAR *filePath)
{ {
TVITEM tvi; TVITEM tvi;

View File

@ -54,6 +54,7 @@ public:
HTREEITEM searchSubItemByName(const TCHAR *itemName, HTREEITEM hParentItem); HTREEITEM searchSubItemByName(const TCHAR *itemName, HTREEITEM hParentItem);
void removeItem(HTREEITEM hTreeItem); void removeItem(HTREEITEM hTreeItem);
void removeAllItems(); void removeAllItems();
bool renameItem(HTREEITEM Item2Set, const TCHAR *newName);
HTREEITEM getChildFrom(HTREEITEM hTreeItem) const { HTREEITEM getChildFrom(HTREEITEM hTreeItem) const {
return TreeView_GetChild(_hSelf, hTreeItem); return TreeView_GetChild(_hSelf, hTreeItem);