-
-
Save taxilian/1368648 to your computer and use it in GitHub Desktop.
#ifndef DialogManager_h__ | |
#define DialogManager_h__ | |
#include <boost/noncopyable.hpp> | |
#include <boost/function.hpp> | |
#include "BrowserHost.h" | |
typedef boost::function<void (const std::string&)> PathCallback; | |
namespace FB { class PluginWindow; } | |
class DialogManager | |
{ | |
public: | |
static DialogManager* get(); | |
virtual void OpenFolderDialog(const FB::BrowserHostPtr& host, FB::PluginWindow* win, const PathCallback& cb) = 0; | |
protected: | |
DialogManager() {} | |
virtual ~DialogManager() {} | |
}; | |
#endif // DialogManager_h__ |
#ifndef DialogManagerMac_h__ | |
#define DialogManagerMac_h__ | |
#include <boost/noncopyable.hpp> | |
#include <string> | |
#include "DialogManager.h" | |
class DialogManagerMac : public DialogManager | |
{ | |
public: | |
void OpenFolderDialog(const FB::BrowserHostPtr& host, FB::PluginWindow* win, const PathCallback& cb); | |
void _showFolderDialog(FB::PluginWindow* win, const PathCallback& cb); | |
protected: | |
DialogManagerMac() {}; | |
~DialogManagerMac() {}; | |
friend class DialogManager; | |
}; | |
#endif // DialogManagerMac_h__ |
#include <string> | |
#include <boost/thread.hpp> | |
#include <AppKit/AppKit.h> | |
#include <Cocoa/Cocoa.h> | |
#include "logging.h" | |
#include "DialogManagerMac.h" | |
#include "BrowserHost.h" | |
DialogManager* DialogManager::get() | |
{ | |
static DialogManagerMac inst; | |
return &inst; | |
} | |
void DialogManagerMac::OpenFolderDialog(const FB::BrowserHostPtr& host, FB::PluginWindow* win, const PathCallback& cb) | |
{ | |
host->ScheduleOnMainThread(boost::shared_ptr<DialogManagerMac>(), boost::bind(&DialogManagerMac::_showFolderDialog, this, win, cb)); | |
} | |
void DialogManagerMac::_showFolderDialog(FB::PluginWindow* win, const PathCallback& cb) | |
{ | |
FBLOG_INFO("DialogManagerMac", "Showing folder dialog"); | |
std::string out; | |
int result; | |
NSAutoreleasePool* pool = [NSAutoreleasePool new]; | |
NSOpenPanel *oPanel = [NSOpenPanel openPanel]; | |
[oPanel setAllowsMultipleSelection:NO]; | |
[oPanel setCanChooseFiles:NO]; | |
[oPanel setCanChooseDirectories:YES]; | |
result = [oPanel runModalForDirectory:nil | |
file:nil types:nil]; | |
if (result == NSOKButton) { | |
NSArray *filesToOpen = [oPanel filenames]; | |
NSString *aFile = [filesToOpen objectAtIndex:0]; | |
out = [aFile cStringUsingEncoding:[NSString defaultCStringEncoding]]; | |
FBLOG_INFO("DialogManagerMac", "Folder selected: " << out); | |
} | |
[pool release]; | |
cb(out); | |
} |
#include "win_common.h" | |
#include <commdlg.h> | |
#include <string> | |
#include <boost/thread.hpp> | |
#include "utf8_tools.h" | |
#include "Win/PluginWindowlessWin.h" | |
#include "Win/PluginWindowWin.h" | |
#include "DialogManagerWin.h" | |
#include <shlobj.h> | |
#include "precompiled_headers.h" // Anything before this is PCH on windows | |
DialogManager* DialogManager::get() | |
{ | |
static DialogManagerWin inst; | |
return &inst; | |
} | |
void DialogManagerWin::OpenFolderDialog(const FB::BrowserHostPtr& host, FB::PluginWindow* win, const PathCallback& cb) { | |
FB::PluginWindowWin* wndWin = dynamic_cast<FB::PluginWindowWin*>(win); | |
FB::PluginWindowlessWin* wndlessWin = dynamic_cast<FB::PluginWindowlessWin*>(win); | |
HWND browserWindow = wndWin ? wndWin->getBrowserHWND() : wndlessWin->getHWND(); | |
boost::thread dlgThread(&DialogManagerWin::_showFolderDialog, this, browserWindow, cb); | |
} | |
void DialogManagerWin::_showFolderDialog(HWND wnd, const PathCallback& cb) { | |
BROWSEINFO bi = { 0 }; | |
bi.lpszTitle = _T("Select a folder to import"); | |
bi.hwndOwner = wnd; | |
LPITEMIDLIST pidl = SHBrowseForFolder ( &bi ); | |
if ( pidl != 0 ) | |
{ | |
std::wstring out; | |
// get the name of the folder | |
TCHAR path[MAX_PATH]; | |
if ( SHGetPathFromIDList ( pidl, path ) ) | |
{ | |
out = path; | |
} | |
// free memory used | |
IMalloc * imalloc = 0; | |
if ( SUCCEEDED( SHGetMalloc ( &imalloc )) ) | |
{ | |
imalloc->Free ( pidl ); | |
imalloc->Release ( ); | |
} | |
cb(FB::wstring_to_utf8(path)); | |
} else { | |
cb(""); | |
} | |
} | |
void DialogManagerWin::_showFileDialog(HWND wnd, const std::string& path, const std::string& filter, const PathCallback& cb) | |
{ | |
wchar_t Filestring[256]; | |
std::string out; | |
std::wstring wFilter(FB::utf8_to_wstring(filter)); | |
std::wstring wPath(FB::utf8_to_wstring(filter)); | |
OPENFILENAME opf; | |
opf.hwndOwner = wnd; | |
opf.lpstrFilter = wFilter.c_str(); | |
opf.lpstrCustomFilter = 0; | |
opf.nMaxCustFilter = 0L; | |
opf.nFilterIndex = 1L; | |
opf.lpstrFile = Filestring; | |
opf.lpstrFile[0] = '\0'; | |
opf.nMaxFile = 256; | |
opf.lpstrFileTitle = 0; | |
opf.nMaxFileTitle=50; | |
opf.lpstrInitialDir = wPath.c_str(); | |
opf.lpstrTitle = L"Choose directory to import"; | |
opf.nFileOffset = 0; | |
opf.nFileExtension = 0; | |
opf.lpstrDefExt = L"*.*"; | |
opf.lpfnHook = NULL; | |
opf.lCustData = 0; | |
opf.Flags = (OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT) & ~OFN_ALLOWMULTISELECT; | |
opf.lStructSize = sizeof(OPENFILENAME); | |
if(GetOpenFileName(&opf)) | |
{ | |
out = FB::wstring_to_utf8(std::wstring(opf.lpstrFile)); | |
} | |
cb(out); | |
} |
#ifndef DialogManagerWin_h__ | |
#define DialogManagerWin_h__ | |
#include <boost/noncopyable.hpp> | |
#include <string> | |
#include "DialogManager.h" | |
class DialogManagerWin : public DialogManager | |
{ | |
public: | |
void OpenFolderDialog(const FB::BrowserHostPtr& host, FB::PluginWindow* win, const PathCallback& cb); | |
protected: | |
DialogManagerWin() {}; | |
~DialogManagerWin() {}; | |
void _showFileDialog(HWND wnd, const std::string& path, const std::string& filter, const PathCallback& cb); | |
void _showFolderDialog(HWND wnd, const PathCallback& cb); | |
friend class DialogManager; | |
}; | |
#endif // DialogManagerWin_h__ |
These original ones did have that same crash, but I found a solution; I just barely posted it. Basically the trick, though, is instead of creating a new thread on OS X you use m_host->ScheduleOnMainThread . See the updated gist above for an example! These are working AFAIK.
Note that it has been suggested that a modal dialog should work fine on the main thread as long as it is started using ScheduleOnMainThread (and thus occurs on the main thread but not as part of one of the NPAPI methods). I haven't actually tried that, but the theory sounds promising.
Hi can you give an example how I should use your code? I'm trying to use it by the following code:
void LocalExecuteAPI::onReturn(const std::string& path) {
}
FB::variant LocalExecuteAPI::selectBracFile(const FB::variant& msg) {
DialogManager* dlg_mgr = DialogManager::get();
boost::shared_ptr loc_exe = m_plugin.lock();
FB::PluginWindow* wh = loc_exe->GetWindow();
dlg_mgr->OpenFolderDialog(m_host, wh, &LocalExecuteAPI::onReturn);
}
but it shows this error:
...src dir\3rdParty\boost\boost/function/function_template.hpp(225): error C2064: term does not evaluate to a function taking 1 arguments
class does not define an 'operator()' or a user defined conversion operator to a pointer-to-function or reference-to-function that takes appropriate number of arguments
Do your OSX dialogs work well? I'm doing something similar (fork a thread that shows a NSOpenPanel), but it crashes sometimes. Where the crash occurs seems inconsistent as well, and it crashes more often on some machines than others (maybe on 10.7? - not so much on mine).