BsWin32BrowseDialogs.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Utility/BsEditorUtility.h"
  4. #include "Threading/BsAsyncOp.h"
  5. #include "CoreThread/BsCoreThread.h"
  6. #include "String/BsUnicode.h"
  7. #include "Private/Win32/BsWin32Window.h"
  8. #include <ShObjIdl.h>
  9. using namespace std::placeholders;
  10. namespace bs
  11. {
  12. void addFiltersToDialog(IFileDialog* fileDialog, const String& filterList)
  13. {
  14. const wchar_t EMPTY_WSTR[] = L"";
  15. if (filterList.empty())
  16. return;
  17. WString wideFilterList = UTF8::toWide(filterList);
  18. Vector<WString> filters = StringUtil::split(wideFilterList, L";");
  19. UINT32 numFilters = (UINT32)filters.size();
  20. if (numFilters == 0)
  21. return;
  22. COMDLG_FILTERSPEC* specList = bs_newN<COMDLG_FILTERSPEC>(numFilters);
  23. for (size_t i = 0; i < numFilters; ++i)
  24. {
  25. specList[i].pszName = EMPTY_WSTR;
  26. specList[i].pszSpec = filters[i].c_str();
  27. }
  28. fileDialog->SetFileTypes(numFilters, specList);
  29. bs_deleteN(specList, numFilters);
  30. }
  31. void setDefaultPath(IFileDialog* dialog, const Path& defaultPath)
  32. {
  33. WString pathStr = UTF8::toWide(defaultPath.toString());
  34. const wchar_t* defaultPathW = pathStr.c_str();
  35. IShellItem* folder;
  36. HRESULT result = SHCreateItemFromParsingName(defaultPathW, NULL, IID_PPV_ARGS(&folder));
  37. // Valid non results.
  38. if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE))
  39. return;
  40. if (!SUCCEEDED(result))
  41. return;
  42. dialog->SetFolder(folder);
  43. folder->Release();
  44. }
  45. void getPaths(IShellItemArray* shellItems, Vector<Path>& paths)
  46. {
  47. DWORD numShellItems;
  48. shellItems->GetCount(&numShellItems);
  49. for (DWORD i = 0; i < numShellItems; ++i)
  50. {
  51. IShellItem* shellItem = nullptr;
  52. shellItems->GetItemAt(i, &shellItem);
  53. SFGAOF attribs;
  54. shellItem->GetAttributes(SFGAO_FILESYSTEM, &attribs);
  55. if (!(attribs & SFGAO_FILESYSTEM))
  56. continue;
  57. LPWSTR name;
  58. shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name);
  59. paths.push_back((Path)UTF8::fromWide(WString(name)));
  60. CoTaskMemFree(name);
  61. }
  62. }
  63. void openBrowseDialogCore(FileDialogType type, const Path& defaultPath, const String& filterList,
  64. Vector<Path>& paths, AsyncOp& returnValue)
  65. {
  66. // Init COM library.
  67. CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
  68. IFileDialog* fileDialog = nullptr;
  69. UINT32 dialogType = ((UINT32)type & (UINT32)FileDialogType::TypeMask);
  70. bool isOpenDialog = dialogType == (UINT32)FileDialogType::OpenFile || dialogType == (UINT32)FileDialogType::OpenFolder;
  71. // Create dialog
  72. IID classId = isOpenDialog ? CLSID_FileOpenDialog : CLSID_FileSaveDialog;
  73. CoCreateInstance(classId, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&fileDialog));
  74. addFiltersToDialog(fileDialog, filterList);
  75. setDefaultPath(fileDialog, defaultPath);
  76. // Apply multiselect flags
  77. bool isMultiselect = false;
  78. if (isOpenDialog)
  79. {
  80. if (dialogType == (UINT32)FileDialogType::OpenFile)
  81. {
  82. if (((UINT32)type & (UINT32)FileDialogType::Multiselect) != 0)
  83. {
  84. DWORD dwFlags;
  85. fileDialog->GetOptions(&dwFlags);
  86. fileDialog->SetOptions(dwFlags | FOS_ALLOWMULTISELECT);
  87. isMultiselect = true;
  88. }
  89. }
  90. else
  91. {
  92. DWORD dwFlags;
  93. fileDialog->GetOptions(&dwFlags);
  94. fileDialog->SetOptions(dwFlags | FOS_PICKFOLDERS);
  95. }
  96. }
  97. // Show the dialog
  98. bool finalResult = false;
  99. // Need to enable all windows, otherwise when the browse dialog closes the active window will become some
  100. // background window
  101. Win32Window::_enableAllWindows();
  102. if (SUCCEEDED(fileDialog->Show(nullptr)))
  103. {
  104. if (isMultiselect)
  105. {
  106. // Get file names
  107. IFileOpenDialog* fileOpenDialog;
  108. fileDialog->QueryInterface(IID_IFileOpenDialog, (void**)&fileOpenDialog);
  109. IShellItemArray* shellItems = nullptr;
  110. fileOpenDialog->GetResults(&shellItems);
  111. getPaths(shellItems, paths);
  112. shellItems->Release();
  113. fileOpenDialog->Release();
  114. }
  115. else
  116. {
  117. // Get the file name
  118. IShellItem* shellItem = nullptr;
  119. fileDialog->GetResult(&shellItem);
  120. LPWSTR filePath = nullptr;
  121. shellItem->GetDisplayName(SIGDN_FILESYSPATH, &filePath);
  122. paths.push_back((Path)UTF8::fromWide(WString(filePath)));
  123. CoTaskMemFree(filePath);
  124. shellItem->Release();
  125. }
  126. finalResult = true;
  127. }
  128. // Restore modal window state (before we enabled all windows)
  129. Win32Window::_restoreModalWindows();
  130. CoUninitialize();
  131. returnValue._completeOperation(finalResult);
  132. }
  133. bool EditorUtility::openBrowseDialog(FileDialogType type, const Path& defaultPath, const String& filterList,
  134. Vector<Path>& paths)
  135. {
  136. AsyncOp returnValue = gCoreThread().queueReturnCommand(std::bind(&openBrowseDialogCore, type,
  137. std::cref(defaultPath), std::cref(filterList), std::ref(paths), _1),
  138. CTQF_InternalQueue | CTQF_BlockUntilComplete);
  139. return any_cast<bool>(returnValue.getGenericReturnValue());
  140. }
  141. }