From: James Turner Date: Mon, 20 Jan 2014 18:43:02 +0000 (+0000) Subject: Windows native file-dialog, menu-bar X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=1b585fa4151254c970e55c0db964fec45a57d2ad;p=flightgear.git Windows native file-dialog, menu-bar By Gijs with additions by James. Menubar is disabled at present since it's not usable and potentially inferior to other solutions, but committing the code so it doesn't rot. --- diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 239faf284..e51a32679 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -46,10 +46,12 @@ set(HEADERS ) if(WIN32) - message(STATUS "on Windows") - - list(APPEND HEADERS WindowsMouseCursor.hxx) - list(APPEND SOURCES WindowsMouseCursor.cxx) + list(APPEND HEADERS WindowsMouseCursor.hxx + FGWindowsMenuBar.hxx + WindowsFileDialog.hxx) + list(APPEND SOURCES WindowsMouseCursor.cxx + FGWindowsMenuBar.cxx + WindowsFileDialog.cxx) endif() if (APPLE) diff --git a/src/GUI/FGWindowsMenuBar.cxx b/src/GUI/FGWindowsMenuBar.cxx new file mode 100644 index 000000000..212e26bc7 --- /dev/null +++ b/src/GUI/FGWindowsMenuBar.cxx @@ -0,0 +1,220 @@ +#include "FGWindowsMenuBar.hxx" + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include
+#include
+#include + +#include + +using namespace simgear; + +namespace { + +HWND getMainViewerHWND() +{ + osgViewer::Viewer::Windows windows; + if (!globals->get_renderer() || !globals->get_renderer()->getViewer()) { + return 0; + } + + globals->get_renderer()->getViewer()->getWindows(windows); + osgViewer::Viewer::Windows::const_iterator it = windows.begin(); + for(; it != windows.end(); ++it) { + if (strcmp((*it)->className(), "GraphicsWindowWin32")) { + continue; + } + + osgViewer::GraphicsWindowWin32* platformWin = + static_cast(*it); + return platformWin->getHWND(); + } + + return 0; +} + +bool labelIsSeparator(const std::string& s) +{ + std::string t = "---"; + if (s.compare(0, t.length(), t) == 0) + return true; + else + return false; +} + +LRESULT CALLBACK menubarWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + SG_LOG(SG_GENERAL, SG_INFO, "called window proc"); + + + return ::DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +} // of anonymous namespace + +// + +class FGWindowsMenuBar::WindowsMenuBarPrivate +{ +public: + WindowsMenuBarPrivate(); + ~WindowsMenuBarPrivate(); + + void fireBindingsForItem(UINT commandId) + { + fireBindingList(itemBindings[commandId]); + } + + HWND mainWindow; + HMENU menuBar; + bool visible; + WNDPROC baseMenuProc; + + typedef std::vector MenuItemBindings; + MenuItemBindings itemBindings; + +}; + +FGWindowsMenuBar::FGWindowsMenuBar() : + p(new WindowsMenuBarPrivate) +{ + +} + +FGWindowsMenuBar::~FGWindowsMenuBar() +{ + +} + +FGWindowsMenuBar::WindowsMenuBarPrivate::WindowsMenuBarPrivate() : + visible(true) +{ + mainWindow = getMainViewerHWND(); + menuBar = 0; +} + +FGWindowsMenuBar::WindowsMenuBarPrivate::~WindowsMenuBarPrivate() +{ + if (menuBar) { + SetMenu(mainWindow, NULL); + DestroyMenu(menuBar); + } +} + +void FGWindowsMenuBar::init() +{ + int menuIndex = 0; + SGPropertyNode_ptr props = fgGetNode("/sim/menubar/default",true); + + p->menuBar = CreateMenu(); +// p->baseMenuProc = (WNDPROC) ::SetWindowLongPtr((HWND) p->mainWindow, GWL_WNDPROC, (LONG_PTR) menubarWindowProc); + + BOOST_FOREACH(SGPropertyNode_ptr n, props->getChildren("menu")) { + // synchronise menu with properties + std::string l = getLocalizedLabel(n); + std::string label = strutils::simplify(l).c_str(); + HMENU menuItems = CreatePopupMenu(); + + if (!n->hasValue("enabled")) { + n->setBoolValue("enabled", true); + } + + bool enabled = n->getBoolValue("enabled"); + + UINT flags = MF_POPUP; + AppendMenu(p->menuBar, flags, (UINT) menuItems, label.c_str()); + + // submenu + int subMenuIndex = 0; + SGPropertyNode* menuNode = n; + BOOST_FOREACH(SGPropertyNode_ptr n2, menuNode->getChildren("item")) { + + if (!n2->hasValue("enabled")) { + n2->setBoolValue("enabled", true); + } + + std::string l2 = getLocalizedLabel(n2); + std::string label2 = strutils::simplify(l2).c_str(); + std::string shortcut = n2->getStringValue("key"); + + SGBindingList bl = readBindingList(n->getChildren("binding"), globals->get_props()); + UINT commandId = p->itemBindings.size(); + p->itemBindings.push_back(bl); + + if (labelIsSeparator(label2)) { + AppendMenu(menuItems, MF_SEPARATOR, NULL, NULL); + } else { + if (!shortcut.empty()) { + label2 += "\t"+shortcut; + } + BOOL enabled = n2->getBoolValue("enabled"); + + UINT flags = MF_STRING; + AppendMenu(menuItems, flags, commandId, label2.c_str()); + } + subMenuIndex++; + } + menuIndex++; + } + + show(); +} + +bool FGWindowsMenuBar::isVisible() const +{ + return p->visible; +} + +void FGWindowsMenuBar::show() +{ + SetMenu(p->mainWindow, p->menuBar); + p->visible = true; +} + +void FGWindowsMenuBar::hide() +{ + SetMenu(p->mainWindow, NULL); + p->visible = false; +} + +#if 0 +LRESULT CALLBACK WndProcedure(HWND hwnd, UINT Msg, + WPARAM wParam, LPARAM lParam) +{ + switch(Msg) + { + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case MY_MENU: + MessageBox(hwnd, "Menu Item Selected = Large", "Message", MB_OK); + break; + } + return 0; + + case WM_DESTROY: + PostQuitMessage(WM_QUIT); + break; + + default: + return DefWindowProc(hwnd, Msg, wParam, lParam); + } + + return 0; +} +#endif diff --git a/src/GUI/FGWindowsMenuBar.hxx b/src/GUI/FGWindowsMenuBar.hxx new file mode 100644 index 000000000..de9deb98a --- /dev/null +++ b/src/GUI/FGWindowsMenuBar.hxx @@ -0,0 +1,64 @@ +// menubar.hxx - XML-configured menu bar. + +#ifndef FG_WINDOWS_MENUBAR_HXX +#define FG_WINDOWS_MENUBAR_HXX 1 + +#include + +#include + +/** + * XML-configured Windows menu bar. + * + * This class creates a menu bar from a tree of XML properties. These + * properties are not part of the main FlightGear property tree, but + * are read from a separate file ($FG_ROOT/gui/menubar.xml). + * + * WARNING: because PUI provides no easy way to attach user data to a + * menu item, all menu item strings must be unique; otherwise, this + * class will always use the first binding with any given name. + */ +class FGWindowsMenuBar : public FGMenuBar +{ +public: + + /** + * Constructor. + */ + FGWindowsMenuBar (); + + + /** + * Destructor. + */ + virtual ~FGWindowsMenuBar (); + + + /** + * Initialize the menu bar from $FG_ROOT/gui/menubar.xml + */ + virtual void init (); + + /** + * Make the menu bar visible. + */ + virtual void show (); + + + /** + * Make the menu bar invisible. + */ + virtual void hide (); + + + /** + * Test whether the menu bar is visible. + */ + virtual bool isVisible () const; + + class WindowsMenuBarPrivate; +private: + std::auto_ptr p; +}; + +#endif // __MENUBAR_HXX diff --git a/src/GUI/FileDialog.cxx b/src/GUI/FileDialog.cxx index 200b15690..f7dfcc116 100644 --- a/src/GUI/FileDialog.cxx +++ b/src/GUI/FileDialog.cxx @@ -35,10 +35,14 @@ #include #include "PUIFileDialog.hxx" -#ifdef SG_MAC +#if defined(SG_MAC) #include "CocoaFileDialog.hxx" #endif +#if defined(SG_WINDOWS) + #include "WindowsFileDialog.hxx" +#endif + FGFileDialog::FGFileDialog(Usage use) : _usage(use), _showHidden(false) @@ -142,8 +146,10 @@ static naRef f_createFileDialog(naContext c, naRef me, int argc, naRef* args) nasal::CallContext ctx(c, argc, args); FGFileDialog::Usage usage = (FGFileDialog::Usage) ctx.requireArg(0); -#ifdef SG_MAC +#if defined(SG_MAC) FileDialogPtr fd(new CocoaFileDialog(usage)); +#elif defined(SG_WINDOWS) + FileDialogPtr fd(new WindowsFileDialog(usage)); #else FileDialogPtr fd(new PUIFileDialog(usage)); #endif diff --git a/src/GUI/WindowsFileDialog.cxx b/src/GUI/WindowsFileDialog.cxx new file mode 100644 index 000000000..b082efa72 --- /dev/null +++ b/src/GUI/WindowsFileDialog.cxx @@ -0,0 +1,146 @@ + + +#include "WindowsFileDialog.hxx" + +#include +#include + +#include + +#include +#include + +#include +#include + +#include
+#include
+#include + +namespace { + +HWND getMainViewerHWND() +{ + osgViewer::Viewer::Windows windows; + if (!globals->get_renderer() || !globals->get_renderer()->getViewer()) { + return 0; + } + + globals->get_renderer()->getViewer()->getWindows(windows); + osgViewer::Viewer::Windows::const_iterator it = windows.begin(); + for(; it != windows.end(); ++it) { + if (strcmp((*it)->className(), "GraphicsWindowWin32")) { + continue; + } + + osgViewer::GraphicsWindowWin32* platformWin = + static_cast(*it); + return platformWin->getHWND(); + } + + return 0; +} + +static int CALLBACK BrowseFolderCallback( + HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + if (uMsg == BFFM_INITIALIZED) { + // set the initial directory now + WindowsFileDialog* dlg = reinterpret_cast(lpData); + LPCTSTR path = dlg->getDirectory().c_str(); + ::SendMessage(hwnd, BFFM_SETSELECTION, true, (LPARAM) path); + } + return 0; +} + +} // of anonymous namespace + +WindowsFileDialog::WindowsFileDialog(FGFileDialog::Usage use) : + FGFileDialog(use) +{ + +} + +WindowsFileDialog::~WindowsFileDialog() +{ + +} + +void WindowsFileDialog::exec() +{ + char Filestring[MAX_PATH] = "\0"; + OPENFILENAME opf={0}; + opf.lStructSize = sizeof(OPENFILENAME); + opf.lpstrFile = Filestring; + opf.lpstrTitle = const_cast(_title.c_str()); + opf.nMaxFile = MAX_PATH; + + std::string extensions; + size_t extensionsLen; + if (!_filterPatterns.empty()) { + BOOST_FOREACH(std::string ext, _filterPatterns) { + if (!simgear::strutils::starts_with(ext, "*.")) { + SG_LOG(SG_GENERAL, SG_ALERT, "WindowsFileDialog: can't use pattern on Windows:" << ext); + continue; + } + extensions += "("+ext+")\0"+ext+"\0"; + extensionsLen += ext.size()*2+4; + } + opf.lpstrFilter = (LPCSTR) malloc(extensionsLen); + memcpy((void*)opf.lpstrFilter, (void*)extensions.data(), extensionsLen); + } + + opf.lpstrInitialDir = const_cast(_initialPath.c_str()); + + if (_showHidden) { + opf.Flags = OFN_PATHMUSTEXIST; + } + + if (_usage == USE_SAVE_FILE) { + if (GetSaveFileNameA(&opf)) { + std::string stringPath(opf.lpstrFile); + _callback->onFileDialogDone(this, stringPath); + } + } else if (_usage == USE_CHOOSE_DIR) { + chooseDir(); + } else { + if (GetOpenFileNameA(&opf)) { + std::string stringPath(opf.lpstrFile); + _callback->onFileDialogDone(this, stringPath); + } + } +} + +void WindowsFileDialog::close() +{ + +} + +void WindowsFileDialog::chooseDir() +{ + // MSDN says this needs to be called first + OleInitialize(NULL); + + char pathBuf[MAX_PATH] = "\0"; + + BROWSEINFO binfo; + memset(&binfo, 0, sizeof(BROWSEINFO)); + binfo.hwndOwner = getMainViewerHWND(); + binfo.ulFlags = BIF_USENEWUI | BIF_RETURNONLYFSDIRS | BIF_EDITBOX; + + binfo.pidlRoot = NULL; // can browse anywhere + binfo.lpszTitle = const_cast(_title.c_str()); + binfo.lpfn = BrowseFolderCallback; + binfo.lParam = reinterpret_cast(this); + + PIDLIST_ABSOLUTE results = SHBrowseForFolder(&binfo); + if (results == NULL) { + // user cancelled + return; + } + + SHGetPathFromIDList(results, pathBuf); + CoTaskMemFree(results); + + _callback->onFileDialogDone(this, SGPath(pathBuf)); +} \ No newline at end of file diff --git a/src/GUI/WindowsFileDialog.hxx b/src/GUI/WindowsFileDialog.hxx new file mode 100644 index 000000000..0af0d4bc4 --- /dev/null +++ b/src/GUI/WindowsFileDialog.hxx @@ -0,0 +1,22 @@ +// WindowsFileDialog.hxx - file dialog implemented using Windows + +#ifndef FG_WINDOWS_FILE_DIALOG_HXX +#define FG_WINDOWS_FILE_DIALOG_HXX 1 + +#include + +class WindowsFileDialog : public FGFileDialog +{ +public: + WindowsFileDialog(FGFileDialog::Usage use); + + virtual ~WindowsFileDialog(); + + virtual void exec(); + virtual void close(); +private: + void chooseDir(); + +}; + +#endif // FG_WINDOWS_FILE_DIALOG_HXX diff --git a/src/GUI/new_gui.cxx b/src/GUI/new_gui.cxx index d679c44c9..66f4a115a 100644 --- a/src/GUI/new_gui.cxx +++ b/src/GUI/new_gui.cxx @@ -33,6 +33,10 @@ #include "FGCocoaMenuBar.hxx" #endif +#if defined(SG_WINDOWS) +#include "FGWindowsMenuBar.hxx" +#endif + #include "FGPUIDialog.hxx" #include "FGFontCache.hxx" #include "FGColor.hxx" @@ -109,6 +113,13 @@ NewGUI::createMenuBarImplementation() if (fgGetBool("/sim/menubar/native", true)) { _menubar.reset(new FGCocoaMenuBar); } +#endif +#if defined(SG_WINDOWS) + if (fgGetBool("/sim/menubar/native", true)) { + // Windows-native menubar disabled for the moment, fall-through + // to PUI version + // _menubar.reset(new FGWindowsMenuBar); + } #endif if (!_menubar.get()) { _menubar.reset(new FGPUIMenuBar);