BsWin32BrowseDialogs.cpp 5.0 KB

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