diff options
Diffstat (limited to 'CPP/7zip/UI/FileManager/LinkDialog.cpp')
-rw-r--r-- | CPP/7zip/UI/FileManager/LinkDialog.cpp | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/CPP/7zip/UI/FileManager/LinkDialog.cpp b/CPP/7zip/UI/FileManager/LinkDialog.cpp new file mode 100644 index 00000000..a24ec601 --- /dev/null +++ b/CPP/7zip/UI/FileManager/LinkDialog.cpp @@ -0,0 +1,342 @@ +// LinkDialog.cpp + +#include "StdAfx.h" + +#include "../../../Windows/ErrorMsg.h" +#include "../../../Windows/FileDir.h" +#include "../../../Windows/FileFind.h" +#include "../../../Windows/FileIO.h" +#include "../../../Windows/FileName.h" + +#ifdef LANG +#include "LangUtils.h" +#endif + +#include "BrowseDialog.h" +#include "CopyDialogRes.h" +#include "LinkDialog.h" +#include "resourceGui.h" + +#include "App.h" + +#include "resource.h" + +extern bool g_SymLink_Supported; + +using namespace NWindows; +using namespace NFile; + +#ifdef LANG +static const UInt32 kLangIDs[] = +{ + IDB_LINK_LINK, + IDT_LINK_PATH_FROM, + IDT_LINK_PATH_TO, + IDG_LINK_TYPE, + IDR_LINK_TYPE_HARD, + IDR_LINK_TYPE_SYM_FILE, + IDR_LINK_TYPE_SYM_DIR, + IDR_LINK_TYPE_JUNCTION +}; +#endif + +static bool GetSymLink(CFSTR path, CReparseAttr &attr) +{ + NFile::NIO::CInFile file; + if (!file.Open(path, + FILE_SHARE_READ, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)) + return false; + + const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; + CByteArr buf(kBufSize); + DWORD returnedSize; + if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize)) + return false; + + if (!attr.Parse(buf, returnedSize)) + return false; + + CByteBuffer data2; + if (!FillLinkData(data2, attr.GetPath(), attr.IsSymLink())) + return false; + + if (data2.Size() != returnedSize || + memcmp(data2, buf, returnedSize) != 0) + return false; + + return true; +} + +static const int k_LinkType_Buttons[] = +{ + IDR_LINK_TYPE_HARD, + IDR_LINK_TYPE_SYM_FILE, + IDR_LINK_TYPE_SYM_DIR, + IDR_LINK_TYPE_JUNCTION +}; + +void CLinkDialog::Set_LinkType_Radio(int idb) +{ + CheckRadioButton(k_LinkType_Buttons[0], k_LinkType_Buttons[ARRAY_SIZE(k_LinkType_Buttons) - 1], idb); +} + +bool CLinkDialog::OnInit() +{ + #ifdef LANG + LangSetWindowText(*this, IDD_LINK); + LangSetDlgItems(*this, kLangIDs, ARRAY_SIZE(kLangIDs)); + #endif + _pathFromCombo.Attach(GetItem(IDC_LINK_PATH_FROM)); + _pathToCombo.Attach(GetItem(IDC_LINK_PATH_TO)); + + if (!FilePath.IsEmpty()) + { + NFile::NFind::CFileInfo fi; + int linkType = 0; + if (!fi.Find(us2fs(FilePath))) + linkType = IDR_LINK_TYPE_SYM_FILE; + else + { + if (fi.HasReparsePoint()) + { + CReparseAttr attr; + bool res = GetSymLink(us2fs(FilePath), attr); + + UString s = attr.PrintName; + if (!attr.IsOkNamePair()) + { + s += L" : "; + s += attr.SubsName; + } + if (!res) + s = L"ERROR: " + s; + + SetItemText(IDT_LINK_PATH_TO_CUR, s); + + UString destPath = attr.GetPath(); + _pathFromCombo.SetText(FilePath); + _pathToCombo.SetText(destPath); + + if (res) + { + if (attr.IsMountPoint()) + linkType = IDR_LINK_TYPE_JUNCTION; + if (attr.IsSymLink()) + { + linkType = + fi.IsDir() ? + IDR_LINK_TYPE_SYM_DIR : + IDR_LINK_TYPE_SYM_FILE; + // if (attr.IsRelative()) linkType = IDR_LINK_TYPE_SYM_RELATIVE; + } + + if (linkType != 0) + Set_LinkType_Radio(linkType); + } + } + else + { + _pathFromCombo.SetText(AnotherPath); + _pathToCombo.SetText(FilePath); + if (fi.IsDir()) + linkType = g_SymLink_Supported ? + IDR_LINK_TYPE_SYM_DIR : + IDR_LINK_TYPE_JUNCTION; + else + linkType = IDR_LINK_TYPE_HARD; + } + } + if (linkType != 0) + Set_LinkType_Radio(linkType); + } + + NormalizeSize(); + return CModalDialog::OnInit(); +} + +bool CLinkDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize) +{ + int mx, my; + GetMargins(8, mx, my); + int bx1, bx2, by; + GetItemSizes(IDCANCEL, bx1, by); + GetItemSizes(IDB_LINK_LINK, bx2, by); + int yPos = ySize - my - by; + int xPos = xSize - mx - bx1; + + InvalidateRect(NULL); + + { + RECT r, r2; + GetClientRectOfItem(IDB_LINK_PATH_FROM, r); + GetClientRectOfItem(IDB_LINK_PATH_TO, r2); + int bx = RECT_SIZE_X(r); + int newButtonXpos = xSize - mx - bx; + + MoveItem(IDB_LINK_PATH_FROM, newButtonXpos, r.top, bx, RECT_SIZE_Y(r)); + MoveItem(IDB_LINK_PATH_TO, newButtonXpos, r2.top, bx, RECT_SIZE_Y(r2)); + + int newComboXsize = newButtonXpos - mx - mx; + ChangeSubWindowSizeX(_pathFromCombo, newComboXsize); + ChangeSubWindowSizeX(_pathToCombo, newComboXsize); + } + + MoveItem(IDCANCEL, xPos, yPos, bx1, by); + MoveItem(IDB_LINK_LINK, xPos - mx - bx2, yPos, bx2, by); + + return false; +} + +bool CLinkDialog::OnButtonClicked(int buttonID, HWND buttonHWND) +{ + switch (buttonID) + { + case IDB_LINK_PATH_FROM: + OnButton_SetPath(false); + return true; + case IDB_LINK_PATH_TO: + OnButton_SetPath(true); + return true; + case IDB_LINK_LINK: + OnButton_Link(); + return true; + } + return CModalDialog::OnButtonClicked(buttonID, buttonHWND); +} + +void CLinkDialog::OnButton_SetPath(bool to) +{ + UString currentPath; + NWindows::NControl::CComboBox &combo = to ? + _pathToCombo : + _pathFromCombo; + combo.GetText(currentPath); + // UString title = L"Specify a location for output folder"; + UString title = LangString(IDS_SET_FOLDER); + + UString resultPath; + if (!MyBrowseForFolder(*this, title, currentPath, resultPath)) + return; + NFile::NName::NormalizeDirPathPrefix(resultPath); + combo.SetCurSel(-1); + combo.SetText(resultPath); +} + +void CLinkDialog::ShowError(const wchar_t *s) +{ + ::MessageBoxW(*this, s, L"7-Zip", MB_ICONERROR); +} + +void CLinkDialog::ShowLastErrorMessage() +{ + ShowError(NError::MyFormatMessage(GetLastError())); +} + +void CLinkDialog::OnButton_Link() +{ + UString from, to; + _pathFromCombo.GetText(from); + _pathToCombo.GetText(to); + int i; + for (i = 0; i < ARRAY_SIZE(k_LinkType_Buttons); i++) + if (IsButtonCheckedBool(k_LinkType_Buttons[i])) + break; + if (i >= ARRAY_SIZE(k_LinkType_Buttons)) + return; + + int idb = k_LinkType_Buttons[i]; + + NFile::NFind::CFileInfo info1, info2; + bool finded1 = info1.Find(us2fs(from)); + bool finded2 = info2.Find(us2fs(to)); + + bool isDirLink = ( + idb == IDR_LINK_TYPE_SYM_DIR || + idb == IDR_LINK_TYPE_JUNCTION); + + if (finded1 && info1.IsDir() != isDirLink || + finded2 && info2.IsDir() != isDirLink) + { + ShowError(L"Incorrect linkType"); + return; + } + + if (idb == IDR_LINK_TYPE_HARD) + { + bool res = NFile::NDir::MyCreateHardLink(us2fs(from), us2fs(to)); + if (!res) + { + ShowLastErrorMessage(); + return; + } + } + else + { + bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION); + + CByteBuffer data; + if (!FillLinkData(data, to, isSymLink)) + { + ShowError(L"Incorrect link"); + return; + } + + CReparseAttr attr; + if (!attr.Parse(data, data.Size())) + { + ShowError(L"Internal conversion error"); + return; + } + + + if (!NFile::NIO::SetReparseData(us2fs(from), isDirLink, data, (DWORD)data.Size())) + { + ShowLastErrorMessage(); + } + } + + End(IDOK); +} + +void CApp::Link() +{ + int srcPanelIndex = GetFocusedPanelIndex(); + CPanel &srcPanel = Panels[srcPanelIndex]; + if (!srcPanel.IsFSFolder()) + { + srcPanel.MessageBoxErrorLang(IDS_OPERATION_IS_NOT_SUPPORTED); + return; + } + CRecordVector<UInt32> indices; + srcPanel.GetOperatedItemIndices(indices); + if (indices.IsEmpty()) + return; + if (indices.Size() != 1) + { + srcPanel.MessageBoxErrorLang(IDS_SELECT_ONE_FILE); + return; + } + int index = indices[0]; + const UString itemName = srcPanel.GetItemName(index); + + UString srcPath = srcPanel._currentFolderPrefix + srcPanel.GetItemPrefix(index); + UString path = srcPath; + { + int destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex); + CPanel &destPanel = Panels[destPanelIndex]; + if (NumPanels > 1) + if (destPanel.IsFSFolder()) + path = destPanel._currentFolderPrefix; + } + + CLinkDialog dlg; + dlg.FilePath = srcPath + itemName; + dlg.AnotherPath = path; + + if (dlg.Create(srcPanel.GetParent()) != IDOK) + return; + + RefreshTitleAlways(); +} |