Show a file picker when launched with no target file.

Fixes #293.
This commit is contained in:
Ben Vanik 2015-06-26 20:27:36 -07:00
parent dd41f01c41
commit 951b2aa5bc
8 changed files with 364 additions and 2 deletions

View File

@ -18,7 +18,7 @@
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(SolutionDir)build\bin\$(Configuration)\</AdditionalLibraryDirectories>
<AdditionalDependencies>ntdll.lib;wsock32.lib;ws2_32.lib;xinput.lib;xaudio2.lib;glu32.lib;opengl32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>ntdll.lib;wsock32.lib;ws2_32.lib;xinput.lib;xaudio2.lib;glu32.lib;opengl32.lib;comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/ignore:4006 /ignore:4221 %(AdditionalOptions)</AdditionalOptions>
<IgnoreSpecificDefaultLibraries>MSVCRTD</IgnoreSpecificDefaultLibraries>
</Link>

View File

@ -203,6 +203,7 @@
<ClCompile Include="src\xenia\ui\main_window.cc" />
<ClCompile Include="src\xenia\ui\menu_item.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_control.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_file_picker.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_loop.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_menu_item.cc" />
<ClCompile Include="src\xenia\ui\win32\win32_window.cc" />
@ -459,11 +460,13 @@
<ClInclude Include="src\xenia\memory.h" />
<ClInclude Include="src\xenia\profiling.h" />
<ClInclude Include="src\xenia\ui\control.h" />
<ClInclude Include="src\xenia\ui\file_picker.h" />
<ClInclude Include="src\xenia\ui\loop.h" />
<ClInclude Include="src\xenia\ui\main_window.h" />
<ClInclude Include="src\xenia\ui\menu_item.h" />
<ClInclude Include="src\xenia\ui\ui_event.h" />
<ClInclude Include="src\xenia\ui\win32\win32_control.h" />
<ClInclude Include="src\xenia\ui\win32\win32_file_picker.h" />
<ClInclude Include="src\xenia\ui\win32\win32_loop.h" />
<ClInclude Include="src\xenia\ui\win32\win32_menu_item.h" />
<ClInclude Include="src\xenia\ui\win32\win32_window.h" />

View File

@ -778,6 +778,9 @@
<ClCompile Include="src\xenia\base\platform_win.cc">
<Filter>src\xenia\base</Filter>
</ClCompile>
<ClCompile Include="src\xenia\ui\win32\win32_file_picker.cc">
<Filter>src\xenia\ui\win32</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\xenia\emulator.h">
@ -1506,6 +1509,12 @@
<ClInclude Include="src\xenia\apu\xma_context.h">
<Filter>src\xenia\apu</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\file_picker.h">
<Filter>src\xenia\ui</Filter>
</ClInclude>
<ClInclude Include="src\xenia\ui\win32\win32_file_picker.h">
<Filter>src\xenia\ui\win32</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="src\xenia\cpu\backend\x64\x64_sequence.inl">

View File

@ -0,0 +1,79 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_FILE_PICKER_H_
#define XENIA_UI_FILE_PICKER_H_
#include <string>
#include <vector>
namespace xe {
namespace ui {
class FilePicker {
public:
enum class Mode {
kOpen = 0,
kSave = 1,
};
enum class Type {
kFile = 0,
kDirectory = 1,
};
FilePicker()
: mode_(Mode::kOpen),
type_(Type::kFile),
title_(L"Select Files"),
multi_selection_(false) {}
virtual ~FilePicker() = default;
Mode mode() const { return mode_; }
void set_mode(Mode mode) { mode_ = mode; }
Type type() const { return type_; }
void set_type(Type type) { type_ = type; }
std::wstring title() const { return title_; }
void set_title(std::wstring title) { title_ = std::move(title); }
std::vector<std::pair<std::wstring, std::wstring>> extensions() const {
return extensions_;
}
void set_extensions(
std::vector<std::pair<std::wstring, std::wstring>> extensions) {
extensions_ = std::move(extensions);
}
bool multi_selection() const { return multi_selection_; }
void set_multi_selection(bool multi_selection) {
multi_selection_ = multi_selection;
}
std::vector<std::wstring> selected_files() const { return selected_files_; }
void set_selected_files(std::vector<std::wstring> selected_files) {
selected_files_ = std::move(selected_files);
}
virtual bool Show(void* parent_window_handle = nullptr) = 0;
private:
Mode mode_;
Type type_;
std::wstring title_;
std::vector<std::pair<std::wstring, std::wstring>> extensions_;
bool multi_selection_;
std::vector<std::wstring> selected_files_;
};
} // namespace ui
} // namespace xe
#endif // XENIA_UI_FILE_PICKER_H_

View File

@ -14,6 +14,7 @@
#include "xenia/xbox.h"
// TODO(benvanik): only on windows.
#include "xenia/ui/win32/win32_file_picker.h"
#include "xenia/ui/win32/win32_loop.h"
#include "xenia/ui/win32/win32_menu_item.h"
#include "xenia/ui/win32/win32_window.h"
@ -28,6 +29,7 @@ namespace ui {
using PlatformLoop = xe::ui::win32::Win32Loop;
using PlatformWindow = xe::ui::win32::Win32Window;
using PlatformMenu = xe::ui::win32::Win32MenuItem;
using PlatformFilePicker = xe::ui::win32::Win32FilePicker;
class MainWindow : public PlatformWindow {
public:

View File

@ -0,0 +1,210 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/win32/win32_file_picker.h"
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <shobjidl.h>
#include "xenia/base/assert.h"
namespace xe {
namespace ui {
namespace win32 {
class CDialogEventHandler : public IFileDialogEvents,
public IFileDialogControlEvents {
public:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
static const QITAB qit[] = {
{&__uuidof(IFileDialogEvents),
(int)OFFSETOFCLASS(IFileDialogEvents, CDialogEventHandler)},
{&__uuidof(IFileDialogControlEvents),
(int)OFFSETOFCLASS(IFileDialogControlEvents, CDialogEventHandler)},
{0},
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
IFACEMETHODIMP_(ULONG) Release() {
long cRef = InterlockedDecrement(&_cRef);
if (!cRef) delete this;
return cRef;
}
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*,
FDE_SHAREVIOLATION_RESPONSE*) {
return S_OK;
};
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd) { return S_OK; };
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*,
FDE_OVERWRITE_RESPONSE*) {
return S_OK;
};
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl,
DWORD dwIDItem) {
return S_OK;
};
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) {
return S_OK;
};
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) {
return S_OK;
};
CDialogEventHandler() : _cRef(1){};
private:
~CDialogEventHandler(){};
long _cRef;
};
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv) {
*ppv = NULL;
auto dialog_event_handler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = dialog_event_handler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr)) {
hr = dialog_event_handler->QueryInterface(riid, ppv);
dialog_event_handler->Release();
}
return hr;
}
Win32FilePicker::Win32FilePicker() = default;
Win32FilePicker::~Win32FilePicker() = default;
template <typename T>
struct com_ptr {
com_ptr() : value(nullptr) {}
~com_ptr() { reset(); }
void reset() {
if (value) {
value->Release();
value = nullptr;
}
}
operator T*() { return value; }
T* operator->() const { return value; }
T** addressof() { return &value; }
T* value;
};
bool Win32FilePicker::Show(void* parent_window_handle) {
// TODO(benvanik): FileSaveDialog.
assert_true(mode() == Mode::kOpen);
// TODO(benvanik): folder dialogs.
assert_true(type() == Type::kFile);
com_ptr<IFileDialog> file_dialog;
HRESULT hr =
CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(file_dialog.addressof()));
if (!SUCCEEDED(hr)) {
return false;
}
hr = file_dialog->SetTitle(title().c_str());
if (!SUCCEEDED(hr)) {
return false;
}
DWORD flags;
hr = file_dialog->GetOptions(&flags);
if (!SUCCEEDED(hr)) {
return false;
}
// FOS_PICKFOLDERS
// FOS_FILEMUSTEXIST
// FOS_PATHMUSTEXIST
flags |= FOS_FORCEFILESYSTEM;
if (multi_selection()) {
flags |= FOS_ALLOWMULTISELECT;
}
hr = file_dialog->SetOptions(flags);
if (!SUCCEEDED(hr)) {
return false;
}
// Set the file types to display only. Notice that this is a 1-based array.
std::vector<COMDLG_FILTERSPEC> save_types;
for (auto& extension : extensions()) {
save_types.push_back({extension.first.c_str(), extension.second.c_str()});
}
hr = file_dialog->SetFileTypes(static_cast<UINT>(save_types.size()),
save_types.data());
if (!SUCCEEDED(hr)) {
return false;
}
hr = file_dialog->SetFileTypeIndex(1);
if (!SUCCEEDED(hr)) {
return false;
}
// Create an event handling object, and hook it up to the dialog.
com_ptr<IFileDialogEvents> file_dialog_events;
hr = CDialogEventHandler_CreateInstance(
IID_PPV_ARGS(file_dialog_events.addressof()));
if (!SUCCEEDED(hr)) {
return false;
}
DWORD cookie;
hr = file_dialog->Advise(file_dialog_events, &cookie);
if (!SUCCEEDED(hr)) {
return false;
}
// Show the dialog modally.
hr = file_dialog->Show(static_cast<HWND>(parent_window_handle));
file_dialog->Unadvise(cookie);
if (!SUCCEEDED(hr)) {
return false;
}
// Obtain the result once the user clicks the 'Open' button.
// The result is an IShellItem object.
com_ptr<IShellItem> shell_item;
hr = file_dialog->GetResult(shell_item.addressof());
if (!SUCCEEDED(hr)) {
return false;
}
// We are just going to print out the name of the file for sample sake.
PWSTR file_path = nullptr;
hr = shell_item->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
if (!SUCCEEDED(hr)) {
return false;
}
std::vector<std::wstring> selected_files;
selected_files.push_back(std::wstring(file_path));
set_selected_files(selected_files);
CoTaskMemFree(file_path);
return true;
}
} // namespace win32
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,33 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_UI_WIN32_WIN32_FILE_PICKER_H_
#define XENIA_UI_WIN32_WIN32_FILE_PICKER_H_
#include "xenia/ui/file_picker.h"
namespace xe {
namespace ui {
namespace win32 {
class Win32FilePicker : public FilePicker {
public:
Win32FilePicker();
~Win32FilePicker() override;
bool Show(void* parent_window_handle) override;
private:
};
} // namespace win32
} // namespace ui
} // namespace xe
#endif // XENIA_UI_WIN32_WIN32_FILE_PICKER_H_

View File

@ -14,6 +14,7 @@
#include "xenia/emulator.h"
#include "xenia/kernel/kernel.h"
#include "xenia/profiling.h"
#include "xenia/ui/file_picker.h"
#include "xenia/ui/main_window.h"
DEFINE_string(target, "", "Specifies the target .xex or .iso to execute.");
@ -33,8 +34,8 @@ int xenia_main(std::vector<std::wstring>& args) {
}
// Grab path from the flag or unnamed argument.
std::wstring path;
if (!FLAGS_target.empty() || args.size() >= 2) {
std::wstring path;
if (!FLAGS_target.empty()) {
// Passed as a named argument.
// TODO(benvanik): find something better than gflags that supports
@ -44,6 +45,31 @@ int xenia_main(std::vector<std::wstring>& args) {
// Passed as an unnamed argument.
path = args[1];
}
}
// If no path passed, ask the user.
if (path.empty()) {
ui::PlatformFilePicker file_picker;
file_picker.set_mode(ui::FilePicker::Mode::kOpen);
file_picker.set_type(ui::FilePicker::Type::kFile);
file_picker.set_multi_selection(false);
file_picker.set_title(L"Select Content Package");
file_picker.set_extensions({
{L"Supported Files", L"*.iso;*.xex;*.xcp;*.*"},
{L"Disc Image (*.iso)", L"*.iso"},
{L"Xbox Executable (*.xex)", L"*.xex"},
//{ L"Content Package (*.xcp)", L"*.xcp" },
{L"All Files (*.*)", L"*.*"},
});
if (file_picker.Show(emulator->main_window()->hwnd())) {
auto selected_files = file_picker.selected_files();
if (!selected_files.empty()) {
path = selected_files[0];
}
}
}
if (!path.empty()) {
// Normalize the path and make absolute.
std::wstring abs_path = xe::to_absolute_path(path);