DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/menu.cpp
00001 /*
00002  *  Copyright (C) 2002-2013  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include "../dos/drives.h"
00020 #include "control.h"
00021 #include "cpu.h"
00022 #include "render.h"
00023 #include "menu.h"
00024 #include "SDL.h"
00025 #include "SDL_syswm.h"
00026 #include "bios_disk.h"
00027 #include "ide.h" // for ide support
00028 #include "mapper.h"
00029 #include "keyboard.h"
00030 #include "timer.h"
00031 #include "inout.h"
00032 
00033 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00034 void sdl_hax_nsMenuAddApplicationMenu(void *nsMenu);
00035 void *sdl_hax_nsMenuItemFromTag(void *nsMenu, unsigned int tag);
00036 void sdl_hax_nsMenuItemUpdateFromItem(void *nsMenuItem, DOSBoxMenu::item &item);
00037 void sdl_hax_nsMenuItemSetTag(void *nsMenuItem, unsigned int id);
00038 void sdl_hax_nsMenuItemSetSubmenu(void *nsMenuItem,void *nsMenu);
00039 void sdl_hax_nsMenuAddItem(void *nsMenu,void *nsMenuItem);
00040 void* sdl_hax_nsMenuAllocSeparator(void);
00041 void* sdl_hax_nsMenuAlloc(const char *initWithText);
00042 void sdl_hax_nsMenuRelease(void *nsMenu);
00043 void* sdl_hax_nsMenuItemAlloc(const char *initWithText);
00044 void sdl_hax_nsMenuItemRelease(void *nsMenuItem);
00045 #endif
00046 
00047 const DOSBoxMenu::mapper_event_t DOSBoxMenu::unassigned_mapper_event; /* empty std::string */
00048 
00049 DOSBoxMenu::DOSBoxMenu() {
00050 }
00051 
00052 DOSBoxMenu::~DOSBoxMenu() {
00053     unbuild();
00054     clear_all_menu_items();
00055 }
00056 
00057 DOSBoxMenu::displaylist::displaylist() {
00058 }
00059 
00060 DOSBoxMenu::displaylist::~displaylist() {
00061 }
00062         
00063 bool DOSBoxMenu::item_exists(const std::string &name) {
00064     auto i = name_map.find(name);
00065 
00066     if (i == name_map.end())
00067        return false;
00068 
00069     return item_exists(i->second);
00070 }
00071 
00072 bool DOSBoxMenu::item_exists(const item_handle_t i) {
00073     if (i == unassigned_item_handle)
00074         return false;
00075     else if (i >= master_list.size())
00076         return false;
00077 
00078     item &ret = master_list[(size_t)i];
00079 
00080     if (!ret.status.allocated || ret.master_id == unassigned_item_handle)
00081         return false;
00082     else if (ret.master_id != i)
00083         return false;
00084 
00085     return true;
00086 }
00087 
00088 DOSBoxMenu::item_handle_t DOSBoxMenu::get_item_id_by_name(const std::string &name) {
00089     auto i = name_map.find(name);
00090 
00091     if (i == name_map.end())
00092         return unassigned_item_handle;
00093 
00094     return i->second;
00095 }
00096 
00097 DOSBoxMenu::item& DOSBoxMenu::get_item(const std::string &name) {
00098     item_handle_t handle = get_item_id_by_name(name);
00099 
00100     if (handle == unassigned_item_handle)
00101         E_Exit("DOSBoxMenu::get_item() No such item '%s'",name.c_str());
00102 
00103     return get_item(handle);
00104 }
00105 
00106 DOSBoxMenu::item& DOSBoxMenu::get_item(const item_handle_t i) {
00107     if (i == unassigned_item_handle)
00108         E_Exit("DOSBoxMenu::get_item() attempt to get unassigned handle");
00109     else if (i >= master_list.size())
00110         E_Exit("DOSBoxMenu::get_item() attempt to get out of range handle");
00111 
00112     item &ret = master_list[(size_t)i];
00113 
00114     if (!ret.status.allocated || ret.master_id == unassigned_item_handle)
00115         E_Exit("DOSBoxMenu::get_item() attempt to read unallocated item");
00116     else if (ret.master_id != i)
00117         E_Exit("DOSBoxMenu::get_item() ID mismatch");
00118 
00119     return ret;
00120 }
00121 
00122 DOSBoxMenu::item& DOSBoxMenu::alloc_item(const enum item_type_t type,const std::string &name) {
00123     if (type >= MAX_id)
00124         E_Exit("DOSBoxMenu::alloc_item() illegal menu type value");
00125 
00126     if (name_map.find(name) != name_map.end())
00127         E_Exit("DOSBoxMenu::alloc_item() name '%s' already taken",name.c_str());
00128 
00129     while (master_list_alloc < master_list.size()) {
00130         if (!master_list[master_list_alloc].status.allocated) {
00131             name_map[name] = master_list_alloc;
00132             return master_list[master_list_alloc].allocate(master_list_alloc,type,name);
00133         }
00134 
00135         master_list_alloc++;
00136     }
00137 
00138     if (master_list_alloc >= master_list_limit)
00139         E_Exit("DOSBoxMenu::alloc_item() no slots are free");
00140 
00141     size_t newsize = master_list.size() + (master_list.size() / 2);
00142     if (newsize < 64) newsize = 64;
00143     if (newsize > master_list_limit) newsize = master_list_limit;
00144     master_list.resize(newsize);
00145 
00146     assert(master_list_alloc < master_list.size());
00147 
00148     name_map[name] = master_list_alloc;
00149     return master_list[master_list_alloc].allocate(master_list_alloc,type,name);
00150 }
00151 
00152 void DOSBoxMenu::delete_item(const item_handle_t i) {
00153     if (i == unassigned_item_handle)
00154         E_Exit("DOSBoxMenu::delete_item() attempt to get unassigned handle");
00155     else if (i >= master_list.size())
00156         E_Exit("DOSBoxMenu::delete_item() attempt to get out of range handle");
00157 
00158     {
00159         auto it = name_map.find(master_list[i].name);
00160         if (it != name_map.end()) {
00161             if (it->second != i) E_Exit("DOSBoxMenu::delete_item() master_id mismatch");
00162             name_map.erase(it);
00163         }
00164     }
00165 
00166     master_list[i].deallocate();
00167     master_list_alloc = i;
00168 }
00169 
00170 const char *DOSBoxMenu::TypeToString(const enum item_type_t type) {
00171     switch (type) {
00172         case item_type_id:              return "Item";
00173         case submenu_type_id:           return "Submenu";
00174         case separator_type_id:         return "Separator";
00175         case vseparator_type_id:        return "VSeparator";
00176         default:                        break;
00177     };
00178 
00179     return "";
00180 }
00181 
00182 void DOSBoxMenu::dump_log_displaylist(DOSBoxMenu::displaylist &ls, unsigned int indent) {
00183     std::string prep;
00184 
00185     for (unsigned int i=0;i < indent;i++)
00186         prep += "+ ";
00187 
00188     for (auto &id : ls.disp_list) {
00189         DOSBoxMenu::item &item = get_item(id);
00190 
00191         if (!item.is_allocated()) {
00192             LOG_MSG("%s (NOT ALLOCATED!!!)",prep.c_str());
00193             continue;
00194         }
00195 
00196         LOG_MSG("%sid=%u type=\"%s\" name=\"%s\" text=\"%s\"",
00197             prep.c_str(),
00198             (unsigned int)item.master_id,
00199             TypeToString(item.type),
00200             item.name.c_str(),
00201             item.text.c_str());
00202 
00203         if (item.type == submenu_type_id)
00204             dump_log_displaylist(item.display_list, indent+1);
00205     }
00206 }
00207 
00208 void DOSBoxMenu::dump_log_debug(void) {
00209     LOG_MSG("Menu dump log (%p)",(void*)this);
00210     LOG_MSG("---- Master list ----");
00211     for (auto &id : master_list) {
00212         if (id.is_allocated()) {
00213             LOG_MSG("+ id=%u type=\"%s\" name=\"%s\" text=\"%s\" shortcut=\"%s\" desc=\"%s\"",
00214                 (unsigned int)id.master_id,
00215                 TypeToString(id.type),
00216                 id.name.c_str(),
00217                 id.text.c_str(),
00218                 id.shortcut_text.c_str(),
00219                 id.description.c_str());
00220 
00221             if (!id.get_mapper_event().empty())
00222                 LOG_MSG("+ + mapper_event=\"%s\"",id.get_mapper_event().c_str());
00223         }
00224     }
00225     LOG_MSG("---- display list ----");
00226     dump_log_displaylist(display_list, 1);
00227 }
00228 
00229 void DOSBoxMenu::clear_all_menu_items(void) {
00230     for (auto &id : master_list) {
00231         if (id.is_allocated())
00232             id.deallocate();
00233     }
00234     master_list_alloc = 0;
00235     master_list.clear();
00236     name_map.clear();
00237 }
00238 
00239 DOSBoxMenu::item::item() {
00240 }
00241 
00242 DOSBoxMenu::item::~item() {
00243 }
00244 
00245 DOSBoxMenu::item &DOSBoxMenu::item::allocate(const item_handle_t id,const enum item_type_t new_type,const std::string &new_name) {
00246     if (master_id != unassigned_item_handle || status.allocated)
00247         E_Exit("DOSBoxMenu::item::allocate() called on item already allocated");
00248 
00249     status.allocated = 1;
00250     name = new_name;
00251     type = new_type;
00252     master_id = id;
00253     return *this;
00254 }
00255 
00256 void DOSBoxMenu::item::deallocate(void) {
00257     if (master_id == unassigned_item_handle || !status.allocated)
00258         E_Exit("DOSBoxMenu::item::deallocate() called on item already deallocated");
00259 
00260     master_id = unassigned_item_handle;
00261     status.allocated = 0;
00262     status.changed = 1;
00263     shortcut_text.clear();
00264     description.clear();
00265     text.clear();
00266     name.clear();
00267 }
00268 
00269 void DOSBoxMenu::displaylist_append(displaylist &ls,const DOSBoxMenu::item_handle_t item_id) {
00270     DOSBoxMenu::item &item = get_item(item_id);
00271 
00272     if (item.status.in_use)
00273         E_Exit("DOSBoxMenu::displaylist_append() item already in use");
00274 
00275     ls.disp_list.push_back(item.master_id);
00276     item.status.in_use = true;
00277     ls.order_changed = true;
00278 }
00279 
00280 void DOSBoxMenu::displaylist_clear(DOSBoxMenu::displaylist &ls) {
00281     for (auto &id : ls.disp_list) {
00282         if (id != DOSBoxMenu::unassigned_item_handle) {
00283             id = DOSBoxMenu::unassigned_item_handle;
00284         }
00285     }
00286 
00287     ls.disp_list.clear();
00288     ls.items_changed = true;
00289     ls.order_changed = true;
00290 }
00291 
00292 void DOSBoxMenu::rebuild(void) {
00293 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00294     if (winMenu == NULL) {
00295         if (!winMenuInit())
00296             return;
00297     }
00298 #endif
00299 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00300     if (nsMenu == NULL) {
00301         if (!nsMenuInit())
00302             return;
00303     }
00304 #endif
00305 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW /* SDL drawn menus */
00306     layoutMenu();
00307 #endif
00308 }
00309 
00310 void DOSBoxMenu::unbuild(void) {
00311 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00312     winMenuDestroy();
00313 #endif
00314 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00315     nsMenuDestroy();
00316 #endif
00317 }
00318 
00319 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00320 bool DOSBoxMenu::mainMenuAction(unsigned int id) {
00321     if (id < nsMenuMinimumID) return false;
00322     id -= nsMenuMinimumID;
00323 
00324     if (id >= master_list.size()) return false;
00325 
00326     item &item = master_list[id];
00327     if (!item.status.allocated || item.master_id == unassigned_item_handle) return false;
00328 
00329     dispatchItemCommand(item);
00330     return true;
00331 }
00332 
00333 void DOSBoxMenu::item::nsAppendMenu(void* parent_nsMenu) {
00334     if (type == separator_type_id) {
00335         sdl_hax_nsMenuAddItem(parent_nsMenu, sdl_hax_nsMenuAllocSeparator());
00336     }
00337     else if (type == vseparator_type_id) {
00338         sdl_hax_nsMenuAddItem(parent_nsMenu, sdl_hax_nsMenuAllocSeparator());
00339     }
00340     else if (type == submenu_type_id) {
00341         if (nsMenu != NULL) {
00342                 // NTS: You have to make a menu ITEM who's submenu is the submenu object
00343                 nsMenuItem = sdl_hax_nsMenuItemAlloc(text.c_str());
00344 
00345                 sdl_hax_nsMenuItemSetTag(nsMenuItem, master_id + nsMenuMinimumID);
00346                 sdl_hax_nsMenuItemSetSubmenu(nsMenuItem, nsMenu);
00347                 sdl_hax_nsMenuAddItem(parent_nsMenu, nsMenuItem);
00348                 sdl_hax_nsMenuItemUpdateFromItem(nsMenuItem, *this);
00349                 sdl_hax_nsMenuItemRelease(nsMenuItem);
00350         }
00351     }
00352     else if (type == item_type_id) {
00353         nsMenuItem = sdl_hax_nsMenuItemAlloc(text.c_str());
00354 
00355         sdl_hax_nsMenuItemSetTag(nsMenuItem, master_id + nsMenuMinimumID);
00356         sdl_hax_nsMenuAddItem(parent_nsMenu, nsMenuItem);
00357         sdl_hax_nsMenuItemUpdateFromItem(nsMenuItem, *this);
00358         sdl_hax_nsMenuItemRelease(nsMenuItem);
00359     }
00360 }
00361 
00362 bool DOSBoxMenu::nsMenuSubInit(DOSBoxMenu::item &p_item) {
00363     if (p_item.nsMenu == NULL) {
00364         p_item.nsMenu = sdl_hax_nsMenuAlloc(p_item.get_text().c_str());
00365         if (p_item.nsMenu != NULL) {
00366             for (auto id : p_item.display_list.disp_list) {
00367                 DOSBoxMenu::item &item = get_item(id);
00368 
00369                 /* if a submenu, make the submenu */
00370                 if (item.type == submenu_type_id) {
00371                     item.parent_id = p_item.master_id;
00372                     nsMenuSubInit(item);
00373                 }
00374 
00375                 item.nsAppendMenu(p_item.nsMenu);
00376             }
00377         }
00378     }
00379 
00380     return true;
00381 }
00382 
00383 bool DOSBoxMenu::nsMenuInit(void) {
00384     if (nsMenu == NULL) {
00385         if ((nsMenu = sdl_hax_nsMenuAlloc("")) == NULL)
00386             return false;
00387 
00388         /* For whatever reason, Mac OS X will always make the first top level menu
00389            the Application menu and will put the name of the app there NO MATTER WHAT */
00390         sdl_hax_nsMenuAddApplicationMenu(nsMenu);
00391 
00392         /* top level */
00393         for (auto id : display_list.disp_list) {
00394             DOSBoxMenu::item &item = get_item(id);
00395 
00396             /* if a submenu, make the submenu */
00397             if (item.type == submenu_type_id) {
00398                 item.parent_id = unassigned_item_handle;
00399                 nsMenuSubInit(item);
00400             }
00401 
00402             item.nsAppendMenu(nsMenu);
00403         }
00404 
00405         /* release our handle on the nsMenus. Mac OS X will keep them alive with it's
00406            reference until the menu is destroyed at which point all items and submenus
00407            will be automatically destroyed */
00408         for (auto &id : master_list) {
00409                 if (id.nsMenu != NULL) {
00410                     sdl_hax_nsMenuRelease(id.nsMenu);
00411                     id.nsMenu = NULL;
00412                 }
00413         }
00414     }
00415 
00416     return true;
00417 }
00418 
00419 void DOSBoxMenu::nsMenuDestroy(void) {
00420     if (nsMenu != NULL) {
00421         sdl_hax_nsMenuRelease(nsMenu);
00422         nsMenu = NULL;
00423     }
00424 }
00425 
00426 void* DOSBoxMenu::getNsMenu(void) const {
00427         return nsMenu;
00428 }
00429 #endif
00430 
00431 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00432 std::string DOSBoxMenu::item::winConstructMenuText(void) {
00433     std::string r;
00434 
00435     /* copy text, converting '&' to '&&' for Windows.
00436      * TODO: Use accelerator to place '&' for underline */
00437     for (auto i=text.begin();i!=text.end();i++) {
00438         char c = *i;
00439 
00440         if (c == '&') {
00441             r += "&&";
00442         }
00443         else {
00444             r += c;
00445         }
00446     }
00447 
00448     /* then the shortcut text */
00449     if (!shortcut_text.empty()) {
00450         r += "\t";
00451 
00452         for (auto i=shortcut_text.begin();i!=shortcut_text.end();i++) {
00453             char c = *i;
00454 
00455             if (c == '&') {
00456                 r += "&&";
00457             }
00458             else {
00459                 r += c;
00460             }
00461         }
00462     }
00463 
00464     return r;
00465 }
00466 
00467 void DOSBoxMenu::item::winAppendMenu(HMENU handle) {
00468     if (type == separator_type_id) {
00469         AppendMenu(handle, MF_SEPARATOR, 0, NULL);
00470     }
00471     else if (type == vseparator_type_id) {
00472         AppendMenu(handle, MF_MENUBREAK, 0, NULL);
00473     }
00474     else if (type == submenu_type_id) {
00475         if (winMenu != NULL)
00476             AppendMenu(handle, MF_POPUP | MF_STRING, (uintptr_t)winMenu, winConstructMenuText().c_str());
00477     }
00478     else if (type == item_type_id) {
00479         unsigned int attr = MF_STRING;
00480 
00481         attr |= (status.checked) ? MF_CHECKED : MF_UNCHECKED;
00482         attr |= (status.enabled) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
00483 
00484         AppendMenu(handle, attr, (uintptr_t)(master_id + winMenuMinimumID), winConstructMenuText().c_str());
00485     }
00486 }
00487 
00488 bool DOSBoxMenu::winMenuSubInit(DOSBoxMenu::item &p_item) {
00489     if (p_item.winMenu == NULL) {
00490         p_item.winMenu = CreatePopupMenu();
00491         if (p_item.winMenu != NULL) {
00492             for (auto id : p_item.display_list.disp_list) {
00493                 DOSBoxMenu::item &item = get_item(id);
00494 
00495                 /* if a submenu, make the submenu */
00496                 if (item.type == submenu_type_id) {
00497                     item.parent_id = p_item.master_id;
00498                     winMenuSubInit(item);
00499                 }
00500 
00501                 item.winAppendMenu(p_item.winMenu);
00502             }
00503         }
00504     }
00505 
00506     return true;
00507 }
00508 
00509 bool DOSBoxMenu::winMenuInit(void) {
00510     if (winMenu == NULL) {
00511         winMenu = CreateMenu();
00512         if (winMenu == NULL) return false;
00513 
00514         /* top level */
00515         for (auto id : display_list.disp_list) {
00516             DOSBoxMenu::item &item = get_item(id);
00517 
00518             /* if a submenu, make the submenu */
00519             if (item.type == submenu_type_id) {
00520                 item.parent_id = unassigned_item_handle;
00521                 winMenuSubInit(item);
00522             }
00523 
00524             item.winAppendMenu(winMenu);
00525         }
00526     }
00527 
00528     return true;
00529 }
00530 
00531 void DOSBoxMenu::winMenuDestroy(void) {
00532     if (winMenu != NULL) {
00533         /* go through all menu items, and clear the menu handle */
00534         for (auto &id : master_list)
00535             id.winMenu = NULL;
00536 
00537         /* destroy the menu.
00538          * By MSDN docs it destroys submenus automatically */
00539         DestroyMenu(winMenu);
00540         winMenu = NULL;
00541     }
00542 }
00543 
00544 HMENU DOSBoxMenu::getWinMenu(void) const {
00545     return winMenu;
00546 }
00547 
00548 /* call this from WM_COMMAND */
00549 bool DOSBoxMenu::mainMenuWM_COMMAND(unsigned int id) {
00550     if (id < winMenuMinimumID) return false;
00551     id -= winMenuMinimumID;
00552 
00553     if (id >= master_list.size()) return false;
00554 
00555     item &item = master_list[id];
00556     if (!item.status.allocated || item.master_id == unassigned_item_handle) return false;
00557 
00558     dispatchItemCommand(item);
00559     return true;
00560 }
00561 #endif
00562 
00563 void MAPPER_TriggerEventByName(const std::string name);
00564 
00565 void DOSBoxMenu::item::refresh_item(DOSBoxMenu &menu) {
00566     (void)menu;//POSSIBLY UNUSED
00567 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU /* Windows menu handle */
00568     if (menu.winMenu != NULL && status.in_use && status.changed) {
00569         HMENU phandle = NULL;
00570 
00571         if (parent_id != unassigned_item_handle)
00572             phandle = menu.get_item(parent_id).winMenu;
00573         else
00574             phandle = menu.winMenu;
00575 
00576                 if (phandle != NULL) {
00577                         if (type == separator_type_id) {
00578                                 /* none */
00579                         }
00580                         else if (type == vseparator_type_id) {
00581                                 /* none */
00582                         }
00583                         else if (type == submenu_type_id) {
00584                                 /* TODO: Can't change by ID, have to change by position */
00585                         }
00586                         else if (type == item_type_id) {
00587                                 unsigned int attr = MF_STRING;
00588 
00589                                 attr |= (status.checked) ? MF_CHECKED : MF_UNCHECKED;
00590                                 attr |= (status.enabled) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
00591 
00592                                 ModifyMenu(phandle, (uintptr_t)(master_id + winMenuMinimumID),
00593                                         attr | MF_BYCOMMAND, (uintptr_t)(master_id + winMenuMinimumID),
00594                                         winConstructMenuText().c_str());
00595                         }
00596                 }
00597     }
00598 
00599     status.changed = false;
00600 #endif
00601 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* Mac OS X menu handle */
00602     if (nsMenuItem != NULL)
00603             sdl_hax_nsMenuItemUpdateFromItem(nsMenuItem, *this);
00604 #endif
00605 }
00606 
00607 void DOSBoxMenu::dispatchItemCommand(item &item) {
00608     if (item.callback_func)
00609         item.callback_func(this,&item);
00610 
00611     if (item.mapper_event != unassigned_mapper_event)
00612         MAPPER_TriggerEventByName(item.mapper_event);
00613 }
00614 
00615 /* this is THE menu */
00616 DOSBoxMenu mainMenu;
00617 
00618 /* top level menu ("") */
00619 static const char *def_menu__toplevel[] = {
00620     "MainMenu",
00621     "CpuMenu",
00622     "VideoMenu",
00623     "SoundMenu",
00624     "DOSMenu",
00625     "CaptureMenu",
00626     NULL
00627 };
00628 
00629 /* main menu ("MainMenu") */
00630 static const char *def_menu_main[] = {
00631     "mapper_mapper",
00632     "mapper_gui",
00633         "--",
00634     "MainSendKey",
00635         "--",
00636         "wait_on_error",
00637     "showdetails",
00638 #if C_DEBUG
00639         "--",
00640         "mapper_debugger",
00641 #endif
00642 #if !defined(MACOSX) && !defined(LINUX) && !defined(HX_DOS)
00643     "show_console",
00644 #endif
00645     "--",
00646     "mapper_capmouse",
00647         "auto_lock_mouse",
00648         "--",
00649         "mapper_pause",
00650     "--",
00651     "mapper_reset",
00652         "--",
00653 #if !defined(HX_DOS)
00654         "mapper_restart",
00655     "--",
00656 #endif
00657     "mapper_shutdown",
00658     NULL
00659 };
00660 
00661 /* main -> send key menu ("MenuSendKey") */
00662 static const char *def_menu_main_sendkey[] = {
00663     "sendkey_ctrlesc",
00664     "sendkey_alttab",
00665     "sendkey_winlogo",
00666     "sendkey_winmenu",
00667     "--",
00668     "sendkey_cad",
00669     NULL
00670 };
00671 
00672 /* cpu -> core menu ("CpuCoreMenu") */
00673 static const char *def_menu_cpu_core[] = {
00674     "mapper_cycauto",
00675     "--",
00676     "mapper_normal",
00677     "mapper_full",
00678     "mapper_simple",
00679 #if (C_DYNAMIC_X86)
00680     "mapper_dynamic",
00681 #endif
00682     NULL
00683 };
00684 
00685 /* cpu -> type menu ("CpuTypeMenu") */
00686 static const char *def_menu_cpu_type[] = {
00687     "cputype_auto",
00688     "--",
00689     "cputype_8086",
00690     "cputype_8086_prefetch",
00691     "cputype_80186",
00692     "cputype_80186_prefetch",
00693     "cputype_286",
00694     "cputype_286_prefetch",
00695     "cputype_386",
00696     "cputype_386_prefetch",
00697     "cputype_486",
00698     "cputype_486_prefetch",
00699     "cputype_pentium",
00700     "cputype_pentium_mmx",
00701     "cputype_ppro_slow",
00702     NULL
00703 };
00704 
00705 /* cpu menu ("CpuMenu") */
00706 static const char *def_menu_cpu[] = {
00707     "mapper_speedlock2", /* NTS: "mapper_speedlock" doesn't work for a menu item because it requires holding the key */
00708     "--",
00709     "mapper_cycleup",
00710     "mapper_cycledown",
00711 #if !defined(C_SDL2)
00712         "mapper_editcycles",
00713 #endif
00714     "--",
00715     "CpuCoreMenu",
00716     "CpuTypeMenu",
00717     NULL
00718 };
00719 
00720 /* video frameskip menu ("VideoFrameskipMenu") */
00721 static const char *def_menu_video_frameskip[] = {
00722     "frameskip_0",
00723     "frameskip_1",
00724     "frameskip_2",
00725     "frameskip_3",
00726     "frameskip_4",
00727     "frameskip_5",
00728     "frameskip_6",
00729     "frameskip_7",
00730     "frameskip_8",
00731     "frameskip_9",
00732     "frameskip_10",
00733     NULL
00734 };
00735 
00736 /* video scaler menu ("VideoScalerMenu") */
00737 static const char *def_menu_video_scaler[] = {
00738     NULL
00739 };
00740 
00741 /* video output menu ("VideoOutputMenu") */
00742 static const char *def_menu_video_output[] = {
00743     "output_surface",
00744 #if !defined(C_SDL2) && !defined(HX_DOS)
00745 # if (HAVE_D3D9_H) && defined(WIN32)
00746     "output_direct3d",
00747 # endif
00748 # if (C_OPENGL)
00749     "output_opengl",
00750     "output_openglnb",
00751 # endif
00752 #endif
00753     NULL
00754 };
00755 
00756 /* video vsync menu ("VideoVsyncMenu") */
00757 static const char *def_menu_video_vsync[] = {
00758 #if !defined(C_SDL2)
00759     "vsync_on",
00760     "vsync_force",
00761     "vsync_host",
00762     "vsync_off",
00763     "--",
00764     "vsync_set_syncrate",
00765 #endif
00766     NULL
00767 };
00768 
00769 /* video overscan menu ("VideoOverscanMenu") */
00770 static const char *def_menu_video_overscan[] = {
00771     "overscan_0",
00772     "overscan_1",
00773     "overscan_2",
00774     "overscan_3",
00775     "overscan_4",
00776     "overscan_5",
00777     "overscan_6",
00778     "overscan_7",
00779     "overscan_8",
00780     "overscan_9",
00781     "overscan_10",
00782     NULL
00783 };
00784 
00785 /* video output menu ("VideoCompatMenu") */
00786 static const char *def_menu_video_compat[] = {
00787     "vga_9widetext",
00788     "doublescan",
00789     NULL
00790 };
00791 
00792 /* video output menu ("VideoPC98Menu") */
00793 static const char *def_menu_video_pc98[] = {
00794     "pc98_5mhz_gdc",
00795     "pc98_allow_200scanline",
00796     "pc98_allow_4partitions",
00797     "--",
00798     "pc98_enable_egc",
00799     "pc98_enable_grcg",
00800     "pc98_enable_analog",
00801     "--",
00802     "pc98_clear_text",
00803     "pc98_clear_graphics",
00804     NULL
00805 };
00806 
00807 /* video menu ("VideoMenu") */
00808 static const char *def_menu_video[] = {
00809 #if !defined(C_SDL2)
00810         "mapper_aspratio",
00811         "--",
00812 #endif
00813 #if !defined(C_SDL2) && !defined(HX_DOS)
00814         "mapper_fullscr",
00815         "--",
00816 #endif
00817 #if !defined(MACOSX) && !defined(C_SDL2) && !defined(HX_DOS)
00818     "alwaysontop",
00819 #endif
00820 #if !defined(C_SDL2)
00821     "doublebuf",
00822         "--",
00823 #endif
00824 #ifndef MACOSX
00825     "mapper_togmenu",
00826 # if !defined(C_SDL2) && !defined(HX_DOS)
00827         "--",
00828 # endif
00829 #endif
00830 #if !defined(C_SDL2) && !defined(HX_DOS)
00831         "mapper_resetsize",
00832 #endif
00833         "--",
00834         "VideoFrameskipMenu",
00835         "--",
00836     "scaler_forced",
00837     "VideoScalerMenu",
00838     "VideoOutputMenu",
00839 #if !defined(C_SDL2)
00840     "VideoVsyncMenu",
00841 #endif
00842     "VideoOverscanMenu",
00843     "VideoCompatMenu",
00844     "VideoPC98Menu",
00845     NULL
00846 };
00847 
00848 /* DOS menu ("DOSMenu") */
00849 static const char *def_menu_dos[] = {
00850     "DOSMouseMenu",
00851     "--",
00852     "DOSPC98Menu",
00853     NULL
00854 };
00855 
00856 /* DOS mouse menu ("DOSMouseMenu") */
00857 static const char *def_menu_dos_mouse[] = {
00858     "dos_mouse_enable_int33",
00859     "dos_mouse_y_axis_reverse",
00860 #if !defined(C_SDL2)
00861     "--",
00862     "dos_mouse_sensitivity",
00863 #endif
00864     NULL
00865 };
00866 
00867 /* DOS pc-98 menu ("DOSPC98Menu") */
00868 static const char *def_menu_dos_pc98[] = {
00869     "dos_pc98_pit_4mhz",
00870     "dos_pc98_pit_5mhz",
00871     NULL
00872 };
00873 
00874 /* sound menu ("SoundMenu") */
00875 static const char *def_menu_sound[] = {
00876     "mapper_volup",
00877     "mapper_voldown",
00878     "--",
00879     "mixer_mute",
00880     "mixer_swapstereo",
00881     NULL
00882 };
00883 
00884 
00885 /* capture menu ("CaptureMenu") */
00886 static const char *def_menu_capture[] = {
00887 #if defined(C_SSHOT)
00888     "mapper_scrshot",
00889     "--",
00890 #endif
00891     "mapper_video",
00892     "mapper_recwave",
00893     "mapper_recmtwave",
00894     "mapper_caprawopl",
00895     "mapper_caprawmidi",
00896     NULL
00897 };
00898 
00899 static DOSBoxMenu::item_handle_t separator_alloc = 0;
00900 static std::vector<DOSBoxMenu::item_handle_t> separators;
00901 
00902 static std::string separator_id(const DOSBoxMenu::item_handle_t r) {
00903     char tmp[32];
00904 
00905     sprintf(tmp,"%u",(unsigned int)r);
00906     return std::string("_separator_") + std::string(tmp);
00907 }
00908 
00909 static DOSBoxMenu::item_handle_t separator_get(const DOSBoxMenu::item_type_t t=DOSBoxMenu::separator_type_id) {
00910     assert(separator_alloc <= separators.size());
00911     if (separator_alloc == separators.size()) {
00912         DOSBoxMenu::item &nitem = mainMenu.alloc_item(t, separator_id(separator_alloc));
00913         separators.push_back(nitem.get_master_id());
00914     }
00915 
00916     assert(separator_alloc < separators.size());
00917     mainMenu.get_item(separators[separator_alloc]).set_type(t);
00918     return separators[separator_alloc++];
00919 }
00920 
00921 void ConstructSubMenu(DOSBoxMenu::item_handle_t item_id, const char * const * list) {
00922     for (size_t i=0;list[i] != NULL;i++) {
00923         const char *ref = list[i];
00924 
00925         /* NTS: This code calls mainMenu.get_item(item_id) every iteration.
00926          *      
00927          *      This seemingly inefficient method of populating the display
00928          *      list is REQUIRED because DOSBoxMenu::item& is a reference
00929          *      to a std::vector, and the reference becomes invalid when
00930          *      the vector reallocates to accomodate more entries.
00931          *
00932          *      Holding onto one reference for the entire loop risks a
00933          *      segfault (use after free) bug if the vector should reallocate
00934          *      in separator_get() -> alloc_item()
00935          *
00936          *      Since get_item(item_id) is literally just a constant time
00937          *      array lookup, this is not very inefficient at all. */
00938 
00939         if (!strcmp(ref,"--")) {
00940             mainMenu.displaylist_append(
00941                 mainMenu.get_item(item_id).display_list, separator_get(DOSBoxMenu::separator_type_id));
00942         }
00943         else if (mainMenu.item_exists(ref)) {
00944             mainMenu.displaylist_append(
00945                 mainMenu.get_item(item_id).display_list, mainMenu.get_item_id_by_name(ref));
00946         }
00947     }
00948 }
00949 
00950 extern const char *scaler_menu_opts[][2];
00951 
00952 void ConstructMenu(void) {
00953     mainMenu.displaylist_clear(mainMenu.display_list);
00954     separator_alloc = 0;
00955 
00956     /* top level */
00957     for (size_t i=0;def_menu__toplevel[i] != NULL;i++)
00958         mainMenu.displaylist_append(
00959             mainMenu.display_list,
00960             mainMenu.get_item_id_by_name(def_menu__toplevel[i]));
00961 
00962     /* main menu */
00963     ConstructSubMenu(mainMenu.get_item("MainMenu").get_master_id(), def_menu_main);
00964 
00965     /* main sendkey menu */
00966     ConstructSubMenu(mainMenu.get_item("MainSendKey").get_master_id(), def_menu_main_sendkey);
00967 
00968     /* cpu menu */
00969     ConstructSubMenu(mainMenu.get_item("CpuMenu").get_master_id(), def_menu_cpu);
00970 
00971     /* cpu core menu */
00972     ConstructSubMenu(mainMenu.get_item("CpuCoreMenu").get_master_id(), def_menu_cpu_core);
00973 
00974     /* cpu type menu */
00975     ConstructSubMenu(mainMenu.get_item("CpuTypeMenu").get_master_id(), def_menu_cpu_type);
00976 
00977     /* video menu */
00978     ConstructSubMenu(mainMenu.get_item("VideoMenu").get_master_id(), def_menu_video);
00979 
00980     /* video frameskip menu */
00981     ConstructSubMenu(mainMenu.get_item("VideoFrameskipMenu").get_master_id(), def_menu_video_frameskip);
00982 
00983     /* video scaler menu */
00984     ConstructSubMenu(mainMenu.get_item("VideoScalerMenu").get_master_id(), def_menu_video_scaler);
00985     {
00986         size_t count=0;
00987 
00988         for (size_t i=0;scaler_menu_opts[i][0] != NULL;i++) {
00989             const std::string name = std::string("scaler_set_") + scaler_menu_opts[i][0];
00990 
00991             if (mainMenu.item_exists(name)) {
00992                 mainMenu.displaylist_append(
00993                     mainMenu.get_item("VideoScalerMenu").display_list,
00994                     mainMenu.get_item_id_by_name(name));
00995 
00996 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
00997                 if ((count % 15) == 14) {
00998                     mainMenu.displaylist_append(
00999                         mainMenu.get_item("VideoScalerMenu").display_list,
01000                         separator_get(DOSBoxMenu::vseparator_type_id));
01001                 }
01002 #endif
01003 
01004                 count++;
01005             }
01006         }
01007     }
01008 
01009     /* video output menu */
01010     ConstructSubMenu(mainMenu.get_item("VideoOutputMenu").get_master_id(), def_menu_video_output);
01011 
01012     /* video vsync menu */
01013     ConstructSubMenu(mainMenu.get_item("VideoVsyncMenu").get_master_id(), def_menu_video_vsync);
01014 
01015     /* video overscan menu */
01016     ConstructSubMenu(mainMenu.get_item("VideoOverscanMenu").get_master_id(), def_menu_video_overscan);
01017 
01018     /* video compat menu */
01019     ConstructSubMenu(mainMenu.get_item("VideoCompatMenu").get_master_id(), def_menu_video_compat);
01020 
01021     /* video PC-98 menu */
01022     ConstructSubMenu(mainMenu.get_item("VideoPC98Menu").get_master_id(), def_menu_video_pc98);
01023 
01024     /* sound menu */
01025     ConstructSubMenu(mainMenu.get_item("SoundMenu").get_master_id(), def_menu_sound);
01026 
01027     /* DOS menu */
01028     ConstructSubMenu(mainMenu.get_item("DOSMenu").get_master_id(), def_menu_dos);
01029 
01030     /* DOS mouse menu */
01031     ConstructSubMenu(mainMenu.get_item("DOSMouseMenu").get_master_id(), def_menu_dos_mouse);
01032 
01033     /* DOS PC-98 menu */
01034     ConstructSubMenu(mainMenu.get_item("DOSPC98Menu").get_master_id(), def_menu_dos_pc98);
01035 
01036     /* capture menu */
01037     ConstructSubMenu(mainMenu.get_item("CaptureMenu").get_master_id(), def_menu_capture);
01038 }
01039 
01040 bool MENU_SetBool(std::string secname, std::string value) {
01041         Section_prop * sec = static_cast<Section_prop *>(control->GetSection(secname));
01042         if(sec) SetVal(secname, value, sec->Get_bool(value) ? "false" : "true");
01043         return sec->Get_bool(value);
01044 }
01045 
01046 void RENDER_CallBack( GFX_CallBackFunctions_t function );
01047 
01048 // Sets the scaler 'forced' flag.
01049 void SetScaleForced(bool forced)
01050 {
01051         render.scale.forced = forced;
01052         RENDER_CallBack(GFX_CallBackReset);
01053     mainMenu.get_item("scaler_forced").check(render.scale.forced).refresh_item(mainMenu);
01054 }
01055 
01056 // Sets the scaler to use.
01057 void SetScaler(scalerOperation_t op, Bitu size, std::string prefix)
01058 {
01059         auto value = prefix + (render.scale.forced ? " forced" : "");
01060         SetVal("render", "scaler", value);
01061         render.scale.size = size;
01062         render.scale.op = op;
01063         RENDER_CallBack(GFX_CallBackReset);
01064 }
01065 
01066 extern int NonUserResizeCounter;
01067 
01068 extern bool dos_kernel_disabled;
01069 extern bool dos_shell_running_program;
01070 
01071 bool GFX_GetPreventFullscreen(void);
01072 void DOSBox_ShowConsole();
01073 
01074 #if !defined(C_SDL2)
01075 void GUI_ResetResize(bool pressed);
01076 #endif
01077 
01078 std::string MSCDEX_Output(int num) {
01079         std::string MSCDEX_MSG = "GUI: MSCDEX ";
01080         std::string MSCDEX_MSG_Failure = "Failure: ";
01081         switch (num) {
01082         case 0: return MSCDEX_MSG + "installed";
01083         case 1: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Drive-letters of multiple CDRom-drives have to be continuous.";
01084         case 2: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Not yet supported.";
01085         case 3: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Path not valid.";
01086         case 4: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Too many CDRom-drives (max: 5). MSCDEX Installation failed";
01087         case 5: return MSCDEX_MSG + "Mounted subdirectory: limited support.";
01088         case 6: return MSCDEX_MSG + MSCDEX_MSG_Failure + "Unknown error";
01089         default: return 0;
01090         }
01091 }
01092 
01093 static std::string not_recommended = "Mounting C:\\ is NOT recommended.\nDo you want to continue?";
01094 
01095 void SetVal(const std::string secname, std::string preval, const std::string val) {
01096         if(preval=="keyboardlayout" && !dos_kernel_disabled) {
01097                 DOS_MCB mcb(dos.psp()-1);
01098                 static char name[9];
01099                 mcb.GetFileName(name);
01100                 if (strlen(name)) {
01101                         LOG_MSG("GUI: Exit %s running in DOSBox, and then try again.",name);
01102                         return;
01103                 }
01104         }
01105         Section* sec = control->GetSection(secname);
01106         if(sec) {
01107                 std::string real_val=preval+"="+val;
01108                 sec->HandleInputline(real_val);
01109         }
01110 }
01111 
01112 MENU_Block menu;
01113 
01114 unsigned int hdd_defsize=16000;
01115 char hdd_size[20]="";
01116 
01117 bool DOSBox_isMenuVisible(void) {
01118     return menu.toggle;
01119 }
01120 
01121 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01122 extern "C" void SDL1_hax_SetMenu(HMENU menu);
01123 extern HMENU MainMenu;
01124 #endif
01125 
01126 void DOSBox_SetMenu(void) {
01127 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01128     /* FIXME: SDL menu is NOT AVAILABLE if OpenGL surface is used */
01129     {
01130         menu.toggle=true;
01131         mainMenu.showMenu();
01132         mainMenu.setRedraw();
01133         GFX_ResetScreen();
01134     }
01135 #endif
01136 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU /* TODO: Move to menu.cpp DOSBox_SetMenu() and add setmenu(NULL) to DOSBox_NoMenu() @emendelson request showmenu=false */
01137     void sdl_hax_macosx_setmenu(void *nsMenu);
01138     sdl_hax_macosx_setmenu(mainMenu.getNsMenu());
01139 #endif
01140 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01141         if(!menu.gui) return;
01142 
01143     if (MainMenu == NULL) return;
01144 
01145         LOG(LOG_MISC,LOG_DEBUG)("Win32: loading and attaching menu resource to DOSBox's window");
01146 
01147         menu.toggle=true;
01148     NonUserResizeCounter=1;
01149         SDL1_hax_SetMenu(MainMenu);
01150         mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
01151 
01152         Reflect_Menu();
01153 
01154         if(menu.startup) {
01155                 RENDER_CallBack( GFX_CallBackReset );
01156         }
01157 
01158     void DOSBox_SetSysMenu(void);
01159     DOSBox_SetSysMenu();
01160 #endif
01161 }
01162 
01163 void DOSBox_NoMenu(void) {
01164 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01165     /* FIXME: SDL menu is NOT AVAILABLE if OpenGL surface is used */
01166     {
01167         menu.toggle=false;
01168         mainMenu.showMenu(false);
01169         mainMenu.setRedraw();
01170         GFX_ResetScreen();
01171     }
01172 #endif
01173 #if DOSBOXMENU_TYPE == DOSBOXMENU_NSMENU
01174     void sdl_hax_macosx_setmenu(void *nsMenu);
01175     sdl_hax_macosx_setmenu(NULL);
01176 #endif
01177 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01178         if(!menu.gui) return;
01179         menu.toggle=false;
01180     NonUserResizeCounter=1;
01181         SDL1_hax_SetMenu(NULL);
01182         mainMenu.get_item("mapper_togmenu").check(!menu.toggle).refresh_item(mainMenu);
01183         RENDER_CallBack( GFX_CallBackReset );
01184 
01185     void DOSBox_SetSysMenu(void);
01186     DOSBox_SetSysMenu();
01187 #endif
01188 }
01189 
01190 void ToggleMenu(bool pressed) {
01191     bool GFX_GetPreventFullscreen(void);
01192 
01193     /* prevent removing the menu in 3Dfx mode */
01194     if (GFX_GetPreventFullscreen())
01195         return;
01196 
01197     menu.resizeusing=true;
01198         int width, height; bool fullscreen;
01199         void GFX_GetSize(int &width, int &height, bool &fullscreen);
01200         GFX_GetSize(width, height, fullscreen);
01201     if(!menu.gui || !pressed || fullscreen) return;
01202         if(!menu.toggle) {
01203                 menu.toggle=true;
01204                 DOSBox_SetMenu();
01205         } else {
01206                 menu.toggle=false;
01207                 DOSBox_NoMenu();
01208         }
01209 
01210     void DOSBox_SetSysMenu(void);
01211         DOSBox_SetSysMenu();
01212 }
01213 
01214 #if !(defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS))
01215 bool OpenGL_using(void);
01216 
01217 int Reflect_Menu(void) {
01218     return 0;
01219 }
01220 
01221 void DOSBox_RefreshMenu(void) {
01222 }
01223 
01224 void DOSBox_CheckOS(int &id, int &major, int &minor) {
01225     id=major=minor=0;
01226 }
01227 #endif
01228 
01229 #if defined(WIN32)
01230 # if defined(HX_DOS) || !defined(C_SDL2)
01231 HWND GetHWND(void) {
01232         SDL_SysWMinfo wmi;
01233         SDL_VERSION(&wmi.version);
01234 
01235         if(!SDL_GetWMInfo(&wmi)) {
01236                 return NULL;
01237         }
01238         return wmi.window;
01239 }
01240 
01241 HWND GetSurfaceHWND(void) {
01242         SDL_SysWMinfo wmi;
01243         SDL_VERSION(&wmi.version);
01244 
01245         if (!SDL_GetWMInfo(&wmi)) {
01246                 return NULL;
01247         }
01248         return wmi.child_window;
01249 }
01250 # endif
01251 #endif
01252 
01253 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
01254 #include <shlobj.h>
01255 
01256 extern void RENDER_CallBack( GFX_CallBackFunctions_t function );
01257 
01258 void GetDefaultSize(void) {
01259         char sizetemp[20]="512,32,32765,";
01260         char sizetemp2[20]="";
01261         sprintf(sizetemp2,"%d",hdd_defsize);
01262         strcat(sizetemp,sizetemp2);
01263         sprintf(hdd_size,sizetemp);
01264 }
01265 
01266 void SearchFolder( char path[MAX_PATH], char drive, std::string drive_type ) {
01267         WIN32_FIND_DATA FindFileData;
01268         HANDLE hFind;
01269 
01270         hFind = FindFirstFile ( "*.*", &FindFileData );
01271 
01272         if ( hFind != INVALID_HANDLE_VALUE ) MountDrive_2(drive,path,drive_type);
01273         FindClose ( hFind );
01274 }
01275 
01276 void BrowseFolder( char drive , std::string drive_type ) {
01277 #if !defined(HX_DOS)
01278         if (Drives[drive-'A']) {
01279                 LOG_MSG("Unmount drive %c first, and then try again.",drive);
01280                 return;
01281         }
01282         std::string title = "Select a drive/directory to mount";
01283         char path[MAX_PATH];
01284         BROWSEINFO bi = { 0 };
01285         if(drive_type=="CDROM")
01286                 bi.lpszTitle = ( title + " CD-ROM\nMounting a directory as CD-ROM gives an limited support" ).c_str();
01287         else if(drive_type=="FLOPPY")
01288                 bi.lpszTitle = ( title + " as Floppy" ).c_str();
01289         else if(drive_type=="LOCAL")
01290                 bi.lpszTitle = ( title + " as Local").c_str();
01291         else
01292                 bi.lpszTitle = (title.c_str());
01293         LPITEMIDLIST pidl = SHBrowseForFolder ( &bi );
01294 
01295         if ( pidl != 0 ) {
01296                 SHGetPathFromIDList ( pidl, path );
01297 //              SetCurrentDirectory ( path );
01298                 SearchFolder( path , drive, drive_type );
01299                 IMalloc * imalloc = 0;
01300                 if ( SUCCEEDED( SHGetMalloc ( &imalloc )) ) {
01301                         imalloc->Free ( pidl );
01302                         imalloc->Release ( );
01303                 }
01304         }
01305 #endif
01306 }
01307 
01308 void mem_conf(std::string memtype, int option) {
01309         std::string tmp;
01310         Section* sec = control->GetSection("dos");
01311         Section_prop * section=static_cast<Section_prop *>(sec); 
01312         if (!option) {
01313                 tmp = section->Get_bool(memtype) ? "false" : "true";
01314         } else {
01315                 switch (option) {
01316                         case 1: tmp = "true"; break;
01317                         case 2: tmp = "false"; break;
01318                         case 3: tmp = "emsboard"; break;
01319                         case 4: tmp = "emm386"; break;
01320                         default: return;
01321                 }
01322         }
01323         if(sec) {
01324                 memtype += "=" + tmp;
01325                 sec->HandleInputline(memtype);
01326         }
01327 }
01328 
01329 void UnMount(int i_drive) {
01330         if (dos_kernel_disabled)
01331                 return;
01332 
01333         i_drive = toupper(i_drive);
01334         if(i_drive-'A' == DOS_GetDefaultDrive()) {
01335                 DOS_MCB mcb(dos.psp()-1);
01336                 static char name[9];
01337                 mcb.GetFileName(name);
01338                 if (!strlen(name)) goto umount;
01339                 LOG_MSG("GUI:Drive %c is being used. Aborted.",i_drive);
01340                 return;
01341         };
01342 umount:
01343         if (i_drive-'A' < DOS_DRIVES && i_drive-'A' >= 0 && Drives[i_drive-'A']) {
01344                 switch (DriveManager::UnmountDrive(i_drive-'A')) {
01345                 case 0:
01346                         Drives[i_drive-'A'] = 0;
01347                         if(i_drive-'A' == DOS_GetDefaultDrive()) 
01348                                 DOS_SetDrive(toupper('Z') - 'A');
01349                         LOG_MSG("GUI:Drive %c has succesfully been removed.",i_drive); break;
01350                 case 1:
01351                         LOG_MSG("GUI:Virtual Drives can not be unMOUNTed."); break;
01352                 case 2:
01353                         LOG_MSG(MSCDEX_Output(1).c_str()); break;
01354                 }
01355         }
01356 }
01357 
01358 void MountDrive_2(char drive, const char drive2[DOS_PATHLENGTH], std::string drive_type) {
01359         (void)drive_type;
01360         (void)drive2;
01361         (void)drive;
01362 }
01363 
01364 void MountDrive(char drive, const char drive2[DOS_PATHLENGTH]) {
01365         (void)drive2;
01366         (void)drive;
01367 }
01368 
01369 void Mount_Img_Floppy(char drive, std::string realpath) {
01370         (void)realpath;
01371         (void)drive;
01372 }
01373 
01374 void Mount_Img_HDD(char drive, std::string realpath) {
01375         (void)realpath;
01376         (void)drive;
01377 }
01378 
01379 void Mount_Img(char drive, std::string realpath) {
01380         (void)realpath;
01381         (void)drive;
01382 }
01383 
01384 void DOSBox_SetSysMenu(void) {
01385 #if !defined(HX_DOS)
01386         MENUITEMINFO mii;
01387         HMENU sysmenu;
01388 
01389         sysmenu = GetSystemMenu(GetHWND(), TRUE); // revert, so we can reapply menu items
01390         sysmenu = GetSystemMenu(GetHWND(), FALSE);
01391         if (sysmenu == NULL) return;
01392 
01393         AppendMenu(sysmenu, MF_SEPARATOR, -1, "");
01394 
01395         {
01396                 const char *msg = "Show menu &bar";
01397 
01398                 memset(&mii, 0, sizeof(mii));
01399                 mii.cbSize = sizeof(mii);
01400                 mii.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
01401                 mii.fState = (menu.toggle ? MFS_CHECKED : 0) | (GFX_GetPreventFullscreen() ? MFS_DISABLED : MFS_ENABLED);
01402                 mii.wID = ID_WIN_SYSMENU_TOGGLEMENU;
01403                 mii.dwTypeData = (LPTSTR)(msg);
01404                 mii.cch = (UINT)(strlen(msg)+1);
01405 
01406                 InsertMenuItem(sysmenu, GetMenuItemCount(sysmenu), TRUE, &mii);
01407         }
01408 #endif
01409 }
01410 
01411 void DOSBox_CheckOS(int &id, int &major, int &minor) {
01412         OSVERSIONINFO osi;
01413         ZeroMemory(&osi, sizeof(OSVERSIONINFO));
01414         osi.dwOSVersionInfoSize = sizeof(osi);
01415         GetVersionEx(&osi);
01416         id=osi.dwPlatformId;
01417         if(id==1) { major=0; minor=0; return; }
01418         major=osi.dwMajorVersion;
01419         minor=osi.dwMinorVersion;
01420 }
01421 
01422 bool DOSBox_Kor(void) {
01423     if(menu.compatible) return false;
01424     char Buffer[30];
01425     GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, Buffer, sizeof(Buffer));
01426     return (!strcmp(Buffer,"KOR") ? true : false);
01427 }
01428 
01429 void DOSBox_RefreshMenu(void) {
01430 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01431     int width, height; bool fullscreen;
01432     void GFX_GetSize(int &width, int &height, bool &fullscreen);
01433     GFX_GetSize(width,height,fullscreen);
01434     void SDL_Prepare(void);
01435     SDL_Prepare();
01436     if(!menu.gui) return;
01437 
01438     bool GFX_GetPreventFullscreen(void);
01439 
01440     /* prevent removing the menu in 3Dfx mode */
01441     if (GFX_GetPreventFullscreen())
01442         return;
01443 
01444     if(fullscreen) {
01445         NonUserResizeCounter=1;
01446         SetMenu(GetHWND(), NULL);
01447         DrawMenuBar(GetHWND());
01448         return;
01449     }
01450         DOSBox_SetSysMenu();
01451         if(menu.toggle)
01452                 DOSBox_SetMenu();
01453         else
01454                 DOSBox_NoMenu();
01455 #endif
01456 }
01457 
01458 void DOSBox_RefreshMenu2(void) {
01459 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01460         if(!menu.gui) return;
01461    int width, height; bool fullscreen;
01462    void GFX_GetSize(int &width, int &height, bool &fullscreen);
01463    GFX_GetSize(width,height,fullscreen);
01464     void SDL_Prepare(void);
01465     SDL_Prepare();
01466     if(!menu.gui) return;
01467 
01468     if(fullscreen) {
01469         NonUserResizeCounter=1;
01470         return;
01471     }
01472         if(menu.toggle) {
01473                 menu.toggle=true;
01474         NonUserResizeCounter=1;
01475         SDL1_hax_SetMenu(MainMenu);
01476         } else {
01477                 menu.toggle=false;
01478         NonUserResizeCounter=1;
01479                 SDL1_hax_SetMenu(NULL);
01480         }
01481 
01482     void DOSBox_SetSysMenu(void);
01483     DOSBox_SetSysMenu();
01484 #endif
01485 }
01486 
01487 void MENU_Check_Drive(HMENU handle, int cdrom, int floppy, int local, int image, int automount, int umount, char drive) {
01488 #if !defined(HX_DOS)
01489         std::string full_drive(1, drive);
01490         Section_prop * sec = static_cast<Section_prop *>(control->GetSection("dos"));
01491         full_drive += ":\\";
01492         EnableMenuItem(handle, cdrom, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01493         EnableMenuItem(handle, floppy, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01494         EnableMenuItem(handle, local, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01495         EnableMenuItem(handle, image, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01496         if(sec) EnableMenuItem(handle, automount, AUTOMOUNT(full_drive.c_str(), drive) && !menu.boot && sec->Get_bool("automount") ? MF_ENABLED : MF_GRAYED);
01497         EnableMenuItem(handle, umount, (!Drives[drive - 'A']) || menu.boot ? MF_GRAYED : MF_ENABLED);
01498 #endif
01499 }
01500 
01501 void MENU_KeyDelayRate(int delay, int rate) {
01502         IO_Write(0x60,0xf3); IO_Write(0x60,(Bit8u)(((delay-1)<<5)|(32-rate)));
01503         LOG_MSG("GUI: Keyboard rate %d, delay %d", rate, delay);
01504 }
01505 
01506 extern "C" void (*SDL1_hax_INITMENU_cb)();
01507 void reflectmenu_INITMENU_cb();
01508 
01509 bool GFX_GetPreventFullscreen(void);
01510 
01511 int Reflect_Menu(void) {
01512 #if !defined(HX_DOS)
01513     SDL1_hax_INITMENU_cb = reflectmenu_INITMENU_cb;
01514 #endif
01515     return 1;
01516 }
01517 
01518 void reflectmenu_INITMENU_cb() {
01519         /* WARNING: SDL calls this from Parent Window Thread!
01520                     This executes in the context of the Parent Window Thread, NOT the main thread!
01521                                 As stupid as that seems, this is the only way the Parent Window Thread can make
01522                                 sure to keep Windows waiting while we take our time to reset the checkmarks in
01523                                 the menus before the menu is displayed. */
01524         Reflect_Menu();
01525 }
01526 
01527 void MSG_WM_COMMAND_handle(SDL_SysWMmsg &Message) {
01528     bool GFX_GetPreventFullscreen(void);
01529 
01530     if (!menu.gui || GetSetSDLValue(1, "desktop.fullscreen", 0)) return;
01531     if (!GetMenu(GetHWND())) return;
01532     if (Message.msg != WM_COMMAND) return;
01533 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01534         if (mainMenu.mainMenuWM_COMMAND((unsigned int)Message.wParam)) return;
01535 #endif
01536 }
01537 #else
01538 void DOSBox_SetSysMenu(void) {
01539 }
01540 #endif
01541 
01542 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01543 void DOSBoxMenu::item::showItem(DOSBoxMenu &menu,bool show) {
01544     (void)menu;//UNUSED
01545     if (itemVisible != show) {
01546         itemVisible = show;
01547         needRedraw = true;
01548     }
01549     else {
01550     }
01551 }
01552 
01553 DOSBoxMenu::item &DOSBoxMenu::item::setHilight(DOSBoxMenu &menu,bool hi) {
01554     (void)menu;//UNUSED
01555     if (itemHilight != hi) {
01556         itemHilight = hi;
01557         needRedraw = true;
01558     }
01559 
01560     return *this;
01561 }
01562 
01563 DOSBoxMenu::item &DOSBoxMenu::item::setHover(DOSBoxMenu &menu,bool ho) {
01564     (void)menu;//UNUSED
01565     if (itemHover != ho) {
01566         itemHover = ho;
01567         needRedraw = true;
01568     }
01569 
01570     return *this;
01571 }
01572 
01573 void DOSBoxMenu::item::removeFocus(DOSBoxMenu &menu) {
01574     if (menu.menuUserAttentionAt == master_id) {
01575         menu.menuUserAttentionAt = unassigned_item_handle;
01576         setHilight(menu,false);
01577     }
01578 }
01579 
01580 void DOSBoxMenu::item::removeHover(DOSBoxMenu &menu) {
01581     if (menu.menuUserHoverAt == master_id) {
01582         menu.menuUserHoverAt = unassigned_item_handle;
01583         setHover(menu,false);
01584     }
01585 }
01586 
01587 void DOSBoxMenu::showMenu(bool show) {
01588     if (menuVisible != show) {
01589         menuVisible = show;
01590         needRedraw = true;
01591         removeFocus();
01592         updateRect();
01593     }
01594 }
01595 
01596 void DOSBoxMenu::removeFocus(void) {
01597     if (menuUserAttentionAt != unassigned_item_handle) {
01598         for (auto &id : master_list) {
01599             id.removeFocus(*this);
01600             id.showItem(*this,false);
01601         }
01602         menuUserAttentionAt = unassigned_item_handle;
01603         needRedraw = true;
01604     }
01605 }
01606 
01607 void DOSBoxMenu::setScale(size_t s) {
01608     if (s == 0) s = 1;
01609     if (s > 2) s = 2;
01610 
01611     if (fontCharScale != s) {
01612         fontCharScale = s;
01613         menuBarHeight = menuBarHeightBase * fontCharScale;
01614         fontCharWidth = fontCharWidthBase * fontCharScale;
01615         fontCharHeight = fontCharHeightBase * fontCharScale;
01616         updateRect();
01617         layoutMenu();
01618     }
01619 }
01620 
01621 void DOSBoxMenu::removeHover(void) {
01622     if (menuUserHoverAt != unassigned_item_handle) {
01623         get_item(menuUserHoverAt).removeHover(*this);
01624         menuUserHoverAt = unassigned_item_handle;
01625         needRedraw = true;
01626     }
01627 }
01628 
01629 void DOSBoxMenu::updateRect(void) {
01630     menuBox.x = 0;
01631     menuBox.y = 0;
01632     menuBox.w = menuVisible ? (unsigned int)screenWidth : 0;
01633     menuBox.h = menuVisible ? (unsigned int)menuBarHeight : 0;
01634     LOG_MSG("SDL menuBox w=%d h=%d",menuBox.w,menuBox.h);
01635 }
01636 
01637 void DOSBoxMenu::layoutMenu(void) {
01638     int x, y;
01639 
01640     x = menuBox.x;
01641     y = menuBox.y;
01642 
01643     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01644         DOSBoxMenu::item &item = get_item(*i);
01645 
01646         item.placeItem(*this, x, y, /*toplevel*/true);
01647         x += item.screenBox.w;
01648     }
01649 
01650     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01651         get_item(*i).placeItemFinal(*this, /*finalwidth*/x - menuBox.x, /*toplevel*/true);
01652 
01653     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01654         get_item(*i).layoutSubmenu(*this, /*toplevel*/true);
01655 
01656     LOG_MSG("Layout complete");
01657 }
01658 
01659 void DOSBoxMenu::item::layoutSubmenu(DOSBoxMenu &menu, bool isTopLevel) {
01660     int x, y, minx, maxx;
01661 
01662     x = screenBox.x;
01663     y = screenBox.y;
01664 
01665     if (isTopLevel) {
01666         y += textBox.h;
01667     }
01668     else {
01669         x += screenBox.w + 2/*popup border*/;
01670     }
01671 
01672     popupBox.x = x;
01673     popupBox.y = y;
01674 
01675     minx = x;
01676     maxx = x;
01677 
01678     auto arr_follow=display_list.disp_list.begin();
01679     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01680         DOSBoxMenu::item &item = menu.get_item(*i);
01681 
01682         if (item.get_type() == DOSBoxMenu::vseparator_type_id) {
01683             for (;arr_follow < i;arr_follow++)
01684                 menu.get_item(*arr_follow).placeItemFinal(menu, /*finalwidth*/maxx - minx, /*toplevel*/false);
01685 
01686             x = maxx;
01687 
01688             item.screenBox.x = x;
01689             item.screenBox.y = popupBox.y;
01690             item.screenBox.w = (unsigned int)((4 * menu.fontCharScale) + 1);
01691             item.screenBox.h = y - popupBox.y;
01692 
01693             minx = maxx = x = item.screenBox.x + item.screenBox.w;
01694             y = popupBox.y;
01695         }
01696         else {
01697             item.placeItem(menu, x, y, /*toplevel*/false);
01698             y += item.screenBox.h;
01699 
01700             if (maxx < (item.screenBox.x + item.screenBox.w))
01701                 maxx = (item.screenBox.x + item.screenBox.w);
01702         }
01703     }
01704 
01705     for (;arr_follow < display_list.disp_list.end();arr_follow++)
01706         menu.get_item(*arr_follow).placeItemFinal(menu, /*finalwidth*/maxx - minx, /*toplevel*/false);
01707 
01708     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01709         DOSBoxMenu::item &item = menu.get_item(*i);
01710         int my = item.screenBox.y + item.screenBox.h;
01711         if (y < my) y = my;
01712     }
01713 
01714     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01715         menu.get_item(*i).layoutSubmenu(menu, /*toplevel*/false);
01716 
01717     popupBox.w = maxx - popupBox.x;
01718     popupBox.h = y - popupBox.y;
01719 
01720     /* 1 pixel border, top */
01721     if (!isTopLevel) {
01722         borderTop = true;
01723         popupBox.y -= 1;
01724         popupBox.h += 1;
01725     }
01726     else {
01727         borderTop = false;
01728     }
01729     /* 1 pixel border, left */
01730     popupBox.x -= 1;
01731     popupBox.w += 1;
01732     /* 1 pixel border, right */
01733     popupBox.w += 1;
01734     /* 1 pixel border, bottom */
01735     popupBox.h += 1;
01736 }
01737 
01738 void DOSBoxMenu::item::placeItemFinal(DOSBoxMenu &menu,int finalwidth,bool isTopLevel) {
01739     if (type < separator_type_id) {
01740         int x = 0,rx = 0;
01741 
01742         if (!isTopLevel) {
01743             screenBox.w = finalwidth;
01744         }
01745 
01746         /* from the left */
01747         checkBox.x = x;
01748         x += checkBox.w;
01749 
01750         textBox.x = x;
01751         x += textBox.w;
01752 
01753         /* from the right */
01754         rx = screenBox.w;
01755 
01756         rx -= (int)menu.fontCharWidth;
01757 
01758         rx -= shortBox.w;
01759         shortBox.x = rx;
01760 
01761         if (!isTopLevel) {
01762             screenBox.w = finalwidth;
01763         }
01764 
01765         /* check */
01766         if (x > rx) LOG_MSG("placeItemFinal warning: text and shorttext overlap by %d pixels",x-rx);
01767     }
01768     else if (type == separator_type_id) {
01769         if (!isTopLevel) {
01770             screenBox.w = finalwidth;
01771         }
01772     }
01773 
01774     LOG_MSG("Item id=%u name=\"%s\" placed at x,y,w,h=%d,%d,%d,%d. text:x,y,w,h=%d,%d,%d,%d",
01775         master_id,name.c_str(),
01776         screenBox.x,screenBox.y,
01777         screenBox.w,screenBox.h,
01778         textBox.x,textBox.y,
01779         textBox.w,textBox.h);
01780     boxInit = true;
01781 }
01782 
01783 void DOSBoxMenu::item::placeItem(DOSBoxMenu &menu,int x,int y,bool isTopLevel) {
01784     if (type < separator_type_id) {
01785         screenBox.x = x;
01786         screenBox.y = y;
01787         screenBox.w = 0;
01788         screenBox.h = (unsigned int)menu.fontCharHeight;
01789 
01790         checkBox.x = 0;
01791         checkBox.y = 0;
01792         checkBox.w = (unsigned int)menu.fontCharWidth;
01793         checkBox.h = (unsigned int)menu.fontCharHeight;
01794         screenBox.w += (unsigned int)checkBox.w;
01795 
01796         textBox.x = 0;
01797         textBox.y = 0;
01798         textBox.w = (unsigned int)menu.fontCharWidth * (unsigned int)text.length();
01799         textBox.h = (unsigned int)menu.fontCharHeight;
01800         screenBox.w += (unsigned int)textBox.w;
01801 
01802         shortBox.x = 0;
01803         shortBox.y = 0;
01804         shortBox.w = 0;
01805         shortBox.h = 0;
01806         if (!isTopLevel && !shortcut_text.empty()) {
01807             screenBox.w += (unsigned int)menu.fontCharWidth;
01808             shortBox.w += (unsigned int)menu.fontCharWidth * (unsigned int)shortcut_text.length();
01809             shortBox.h = (unsigned int)menu.fontCharHeight;
01810             screenBox.w += (unsigned int)shortBox.w;
01811         }
01812 
01813         if (!isTopLevel && type == submenu_type_id)
01814             screenBox.w += (unsigned int)menu.fontCharWidth;
01815 
01816         screenBox.w += (unsigned int)menu.fontCharWidth;
01817     }
01818     else {
01819         screenBox.x = x;
01820         screenBox.y = y;
01821         screenBox.w = (unsigned int)menu.fontCharWidth * 2;
01822         screenBox.h = (unsigned int)((4 * menu.fontCharScale) + 1);
01823 
01824         checkBox.x = 0;
01825         checkBox.y = 0;
01826         checkBox.w = 0;
01827         checkBox.h = 0;
01828 
01829         textBox.x = 0;
01830         textBox.y = 0;
01831         textBox.w = 0;
01832         textBox.h = 0;
01833 
01834         shortBox.x = 0;
01835         shortBox.y = 0;
01836         shortBox.w = 0;
01837         shortBox.h = 0;
01838     }
01839 }
01840 #endif
01841