Skip to content

Instantly share code, notes, and snippets.

@taxilian
Created November 15, 2011 22:58
Show Gist options
  • Save taxilian/1368648 to your computer and use it in GitHub Desktop.
Save taxilian/1368648 to your computer and use it in GitHub Desktop.
DialogManager abstraction for FireBreath
#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__
@saferoll
Copy link

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).

@taxilian
Copy link
Author

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.

@taxilian
Copy link
Author

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.

@Faham
Copy link

Faham commented Jul 18, 2012

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment