DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
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 
01228 # if defined(HX_DOS)
01229 HWND GetHWND(void) {
01230         SDL_SysWMinfo wmi;
01231         SDL_VERSION(&wmi.version);
01232 
01233         if(!SDL_GetWMInfo(&wmi)) {
01234                 return NULL;
01235         }
01236         return wmi.window;
01237 }
01238 
01239 HWND GetSurfaceHWND(void) {
01240         SDL_SysWMinfo wmi;
01241         SDL_VERSION(&wmi.version);
01242 
01243         if (!SDL_GetWMInfo(&wmi)) {
01244                 return NULL;
01245         }
01246         return wmi.child_window;
01247 }
01248 # endif
01249 #endif
01250 
01251 #if defined(WIN32) && !defined(C_SDL2) && !defined(HX_DOS)
01252 #include <shlobj.h>
01253 
01254 extern void RENDER_CallBack( GFX_CallBackFunctions_t function );
01255 
01256 HWND GetHWND(void) {
01257         SDL_SysWMinfo wmi;
01258         SDL_VERSION(&wmi.version);
01259 
01260         if(!SDL_GetWMInfo(&wmi)) {
01261                 return NULL;
01262         }
01263         return wmi.window;
01264 }
01265 
01266 HWND GetSurfaceHWND(void) {
01267         SDL_SysWMinfo wmi;
01268         SDL_VERSION(&wmi.version);
01269 
01270         if (!SDL_GetWMInfo(&wmi)) {
01271                 return NULL;
01272         }
01273         return wmi.child_window;
01274 }
01275 
01276 void GetDefaultSize(void) {
01277         char sizetemp[20]="512,32,32765,";
01278         char sizetemp2[20]="";
01279         sprintf(sizetemp2,"%d",hdd_defsize);
01280         strcat(sizetemp,sizetemp2);
01281         sprintf(hdd_size,sizetemp);
01282 }
01283 
01284 void SearchFolder( char path[MAX_PATH], char drive, std::string drive_type ) {
01285         WIN32_FIND_DATA FindFileData;
01286         HANDLE hFind;
01287 
01288         hFind = FindFirstFile ( "*.*", &FindFileData );
01289 
01290         if ( hFind != INVALID_HANDLE_VALUE ) MountDrive_2(drive,path,drive_type);
01291         FindClose ( hFind );
01292 }
01293 
01294 void BrowseFolder( char drive , std::string drive_type ) {
01295 #if !defined(HX_DOS)
01296         if (Drives[drive-'A']) {
01297                 LOG_MSG("Unmount drive %c first, and then try again.",drive);
01298                 return;
01299         }
01300         std::string title = "Select a drive/directory to mount";
01301         char path[MAX_PATH];
01302         BROWSEINFO bi = { 0 };
01303         if(drive_type=="CDROM")
01304                 bi.lpszTitle = ( title + " CD-ROM\nMounting a directory as CD-ROM gives an limited support" ).c_str();
01305         else if(drive_type=="FLOPPY")
01306                 bi.lpszTitle = ( title + " as Floppy" ).c_str();
01307         else if(drive_type=="LOCAL")
01308                 bi.lpszTitle = ( title + " as Local").c_str();
01309         else
01310                 bi.lpszTitle = (title.c_str());
01311         LPITEMIDLIST pidl = SHBrowseForFolder ( &bi );
01312 
01313         if ( pidl != 0 ) {
01314                 SHGetPathFromIDList ( pidl, path );
01315 //              SetCurrentDirectory ( path );
01316                 SearchFolder( path , drive, drive_type );
01317                 IMalloc * imalloc = 0;
01318                 if ( SUCCEEDED( SHGetMalloc ( &imalloc )) ) {
01319                         imalloc->Free ( pidl );
01320                         imalloc->Release ( );
01321                 }
01322         }
01323 #endif
01324 }
01325 
01326 void mem_conf(std::string memtype, int option) {
01327         std::string tmp;
01328         Section* sec = control->GetSection("dos");
01329         Section_prop * section=static_cast<Section_prop *>(sec); 
01330         if (!option) {
01331                 tmp = section->Get_bool(memtype) ? "false" : "true";
01332         } else {
01333                 switch (option) {
01334                         case 1: tmp = "true"; break;
01335                         case 2: tmp = "false"; break;
01336                         case 3: tmp = "emsboard"; break;
01337                         case 4: tmp = "emm386"; break;
01338                         default: return;
01339                 }
01340         }
01341         if(sec) {
01342                 memtype += "=" + tmp;
01343                 sec->HandleInputline(memtype);
01344         }
01345 }
01346 
01347 void UnMount(int i_drive) {
01348         if (dos_kernel_disabled)
01349                 return;
01350 
01351         i_drive = toupper(i_drive);
01352         if(i_drive-'A' == DOS_GetDefaultDrive()) {
01353                 DOS_MCB mcb(dos.psp()-1);
01354                 static char name[9];
01355                 mcb.GetFileName(name);
01356                 if (!strlen(name)) goto umount;
01357                 LOG_MSG("GUI:Drive %c is being used. Aborted.",i_drive);
01358                 return;
01359         };
01360 umount:
01361         if (i_drive-'A' < DOS_DRIVES && i_drive-'A' >= 0 && Drives[i_drive-'A']) {
01362                 switch (DriveManager::UnmountDrive(i_drive-'A')) {
01363                 case 0:
01364                         Drives[i_drive-'A'] = 0;
01365                         if(i_drive-'A' == DOS_GetDefaultDrive()) 
01366                                 DOS_SetDrive(toupper('Z') - 'A');
01367                         LOG_MSG("GUI:Drive %c has succesfully been removed.",i_drive); break;
01368                 case 1:
01369                         LOG_MSG("GUI:Virtual Drives can not be unMOUNTed."); break;
01370                 case 2:
01371                         LOG_MSG(MSCDEX_Output(1).c_str()); break;
01372                 }
01373         }
01374 }
01375 
01376 void MountDrive_2(char drive, const char drive2[DOS_PATHLENGTH], std::string drive_type) {
01377         (void)drive_type;
01378         (void)drive2;
01379         (void)drive;
01380 }
01381 
01382 void MountDrive(char drive, const char drive2[DOS_PATHLENGTH]) {
01383         (void)drive2;
01384         (void)drive;
01385 }
01386 
01387 void Mount_Img_Floppy(char drive, std::string realpath) {
01388         (void)realpath;
01389         (void)drive;
01390 }
01391 
01392 void Mount_Img_HDD(char drive, std::string realpath) {
01393         (void)realpath;
01394         (void)drive;
01395 }
01396 
01397 void Mount_Img(char drive, std::string realpath) {
01398         (void)realpath;
01399         (void)drive;
01400 }
01401 
01402 void DOSBox_SetSysMenu(void) {
01403 #if !defined(HX_DOS)
01404         MENUITEMINFO mii;
01405         HMENU sysmenu;
01406 
01407         sysmenu = GetSystemMenu(GetHWND(), TRUE); // revert, so we can reapply menu items
01408         sysmenu = GetSystemMenu(GetHWND(), FALSE);
01409         if (sysmenu == NULL) return;
01410 
01411         AppendMenu(sysmenu, MF_SEPARATOR, -1, "");
01412 
01413         {
01414                 const char *msg = "Show menu &bar";
01415 
01416                 memset(&mii, 0, sizeof(mii));
01417                 mii.cbSize = sizeof(mii);
01418                 mii.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
01419                 mii.fState = (menu.toggle ? MFS_CHECKED : 0) | (GFX_GetPreventFullscreen() ? MFS_DISABLED : MFS_ENABLED);
01420                 mii.wID = ID_WIN_SYSMENU_TOGGLEMENU;
01421                 mii.dwTypeData = (LPTSTR)(msg);
01422                 mii.cch = (UINT)(strlen(msg)+1);
01423 
01424                 InsertMenuItem(sysmenu, GetMenuItemCount(sysmenu), TRUE, &mii);
01425         }
01426 #endif
01427 }
01428 
01429 void DOSBox_CheckOS(int &id, int &major, int &minor) {
01430         OSVERSIONINFO osi;
01431         ZeroMemory(&osi, sizeof(OSVERSIONINFO));
01432         osi.dwOSVersionInfoSize = sizeof(osi);
01433         GetVersionEx(&osi);
01434         id=osi.dwPlatformId;
01435         if(id==1) { major=0; minor=0; return; }
01436         major=osi.dwMajorVersion;
01437         minor=osi.dwMinorVersion;
01438 }
01439 
01440 bool DOSBox_Kor(void) {
01441     if(menu.compatible) return false;
01442     char Buffer[30];
01443     GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, Buffer, sizeof(Buffer));
01444     return (!strcmp(Buffer,"KOR") ? true : false);
01445 }
01446 
01447 void DOSBox_RefreshMenu(void) {
01448 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01449     int width, height; bool fullscreen;
01450     void GFX_GetSize(int &width, int &height, bool &fullscreen);
01451     GFX_GetSize(width,height,fullscreen);
01452     void SDL_Prepare(void);
01453     SDL_Prepare();
01454     if(!menu.gui) return;
01455 
01456     bool GFX_GetPreventFullscreen(void);
01457 
01458     /* prevent removing the menu in 3Dfx mode */
01459     if (GFX_GetPreventFullscreen())
01460         return;
01461 
01462     if(fullscreen) {
01463         NonUserResizeCounter=1;
01464         SetMenu(GetHWND(), NULL);
01465         DrawMenuBar(GetHWND());
01466         return;
01467     }
01468         DOSBox_SetSysMenu();
01469         if(menu.toggle)
01470                 DOSBox_SetMenu();
01471         else
01472                 DOSBox_NoMenu();
01473 #endif
01474 }
01475 
01476 void DOSBox_RefreshMenu2(void) {
01477 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01478         if(!menu.gui) return;
01479    int width, height; bool fullscreen;
01480    void GFX_GetSize(int &width, int &height, bool &fullscreen);
01481    GFX_GetSize(width,height,fullscreen);
01482     void SDL_Prepare(void);
01483     SDL_Prepare();
01484     if(!menu.gui) return;
01485 
01486     if(fullscreen) {
01487         NonUserResizeCounter=1;
01488         return;
01489     }
01490         if(menu.toggle) {
01491                 menu.toggle=true;
01492         NonUserResizeCounter=1;
01493         SDL1_hax_SetMenu(MainMenu);
01494         } else {
01495                 menu.toggle=false;
01496         NonUserResizeCounter=1;
01497                 SDL1_hax_SetMenu(NULL);
01498         }
01499 
01500     void DOSBox_SetSysMenu(void);
01501     DOSBox_SetSysMenu();
01502 #endif
01503 }
01504 
01505 void MENU_Check_Drive(HMENU handle, int cdrom, int floppy, int local, int image, int automount, int umount, char drive) {
01506 #if !defined(HX_DOS)
01507         std::string full_drive(1, drive);
01508         Section_prop * sec = static_cast<Section_prop *>(control->GetSection("dos"));
01509         full_drive += ":\\";
01510         EnableMenuItem(handle, cdrom, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01511         EnableMenuItem(handle, floppy, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01512         EnableMenuItem(handle, local, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01513         EnableMenuItem(handle, image, (Drives[drive - 'A'] || menu.boot) ? MF_GRAYED : MF_ENABLED);
01514         if(sec) EnableMenuItem(handle, automount, AUTOMOUNT(full_drive.c_str(), drive) && !menu.boot && sec->Get_bool("automount") ? MF_ENABLED : MF_GRAYED);
01515         EnableMenuItem(handle, umount, (!Drives[drive - 'A']) || menu.boot ? MF_GRAYED : MF_ENABLED);
01516 #endif
01517 }
01518 
01519 void MENU_KeyDelayRate(int delay, int rate) {
01520         IO_Write(0x60,0xf3); IO_Write(0x60,(Bit8u)(((delay-1)<<5)|(32-rate)));
01521         LOG_MSG("GUI: Keyboard rate %d, delay %d", rate, delay);
01522 }
01523 
01524 extern "C" void (*SDL1_hax_INITMENU_cb)();
01525 void reflectmenu_INITMENU_cb();
01526 
01527 bool GFX_GetPreventFullscreen(void);
01528 
01529 int Reflect_Menu(void) {
01530 #if !defined(HX_DOS)
01531     SDL1_hax_INITMENU_cb = reflectmenu_INITMENU_cb;
01532 #endif
01533     return 1;
01534 }
01535 
01536 void reflectmenu_INITMENU_cb() {
01537         /* WARNING: SDL calls this from Parent Window Thread!
01538                     This executes in the context of the Parent Window Thread, NOT the main thread!
01539                                 As stupid as that seems, this is the only way the Parent Window Thread can make
01540                                 sure to keep Windows waiting while we take our time to reset the checkmarks in
01541                                 the menus before the menu is displayed. */
01542         Reflect_Menu();
01543 }
01544 
01545 void MSG_WM_COMMAND_handle(SDL_SysWMmsg &Message) {
01546     bool GFX_GetPreventFullscreen(void);
01547 
01548     if (!menu.gui || GetSetSDLValue(1, "desktop.fullscreen", 0)) return;
01549     if (!GetMenu(GetHWND())) return;
01550     if (Message.msg != WM_COMMAND) return;
01551 #if DOSBOXMENU_TYPE == DOSBOXMENU_HMENU
01552         if (mainMenu.mainMenuWM_COMMAND((unsigned int)Message.wParam)) return;
01553 #endif
01554 }
01555 #else
01556 void DOSBox_SetSysMenu(void) {
01557 }
01558 #endif
01559 
01560 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
01561 void DOSBoxMenu::item::showItem(DOSBoxMenu &menu,bool show) {
01562     (void)menu;//UNUSED
01563     if (itemVisible != show) {
01564         itemVisible = show;
01565         needRedraw = true;
01566     }
01567     else {
01568     }
01569 }
01570 
01571 DOSBoxMenu::item &DOSBoxMenu::item::setHilight(DOSBoxMenu &menu,bool hi) {
01572     (void)menu;//UNUSED
01573     if (itemHilight != hi) {
01574         itemHilight = hi;
01575         needRedraw = true;
01576     }
01577 
01578     return *this;
01579 }
01580 
01581 DOSBoxMenu::item &DOSBoxMenu::item::setHover(DOSBoxMenu &menu,bool ho) {
01582     (void)menu;//UNUSED
01583     if (itemHover != ho) {
01584         itemHover = ho;
01585         needRedraw = true;
01586     }
01587 
01588     return *this;
01589 }
01590 
01591 void DOSBoxMenu::item::removeFocus(DOSBoxMenu &menu) {
01592     if (menu.menuUserAttentionAt == master_id) {
01593         menu.menuUserAttentionAt = unassigned_item_handle;
01594         setHilight(menu,false);
01595     }
01596 }
01597 
01598 void DOSBoxMenu::item::removeHover(DOSBoxMenu &menu) {
01599     if (menu.menuUserHoverAt == master_id) {
01600         menu.menuUserHoverAt = unassigned_item_handle;
01601         setHover(menu,false);
01602     }
01603 }
01604 
01605 void DOSBoxMenu::showMenu(bool show) {
01606     if (menuVisible != show) {
01607         menuVisible = show;
01608         needRedraw = true;
01609         removeFocus();
01610         updateRect();
01611     }
01612 }
01613 
01614 void DOSBoxMenu::removeFocus(void) {
01615     if (menuUserAttentionAt != unassigned_item_handle) {
01616         for (auto &id : master_list) {
01617             id.removeFocus(*this);
01618             id.showItem(*this,false);
01619         }
01620         menuUserAttentionAt = unassigned_item_handle;
01621         needRedraw = true;
01622     }
01623 }
01624 
01625 void DOSBoxMenu::setScale(size_t s) {
01626     if (s == 0) s = 1;
01627     if (s > 2) s = 2;
01628 
01629     if (fontCharScale != s) {
01630         fontCharScale = s;
01631         menuBarHeight = menuBarHeightBase * fontCharScale;
01632         fontCharWidth = fontCharWidthBase * fontCharScale;
01633         fontCharHeight = fontCharHeightBase * fontCharScale;
01634         updateRect();
01635         layoutMenu();
01636     }
01637 }
01638 
01639 void DOSBoxMenu::removeHover(void) {
01640     if (menuUserHoverAt != unassigned_item_handle) {
01641         get_item(menuUserHoverAt).removeHover(*this);
01642         menuUserHoverAt = unassigned_item_handle;
01643         needRedraw = true;
01644     }
01645 }
01646 
01647 void DOSBoxMenu::updateRect(void) {
01648     menuBox.x = 0;
01649     menuBox.y = 0;
01650     menuBox.w = menuVisible ? (unsigned int)screenWidth : 0;
01651     menuBox.h = menuVisible ? (unsigned int)menuBarHeight : 0;
01652     LOG_MSG("SDL menuBox w=%d h=%d",menuBox.w,menuBox.h);
01653 }
01654 
01655 void DOSBoxMenu::layoutMenu(void) {
01656     int x, y;
01657 
01658     x = menuBox.x;
01659     y = menuBox.y;
01660 
01661     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01662         DOSBoxMenu::item &item = get_item(*i);
01663 
01664         item.placeItem(*this, x, y, /*toplevel*/true);
01665         x += item.screenBox.w;
01666     }
01667 
01668     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01669         get_item(*i).placeItemFinal(*this, /*finalwidth*/x - menuBox.x, /*toplevel*/true);
01670 
01671     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01672         get_item(*i).layoutSubmenu(*this, /*toplevel*/true);
01673 
01674     LOG_MSG("Layout complete");
01675 }
01676 
01677 void DOSBoxMenu::item::layoutSubmenu(DOSBoxMenu &menu, bool isTopLevel) {
01678     int x, y, minx, maxx;
01679 
01680     x = screenBox.x;
01681     y = screenBox.y;
01682 
01683     if (isTopLevel) {
01684         y += textBox.h;
01685     }
01686     else {
01687         x += screenBox.w + 2/*popup border*/;
01688     }
01689 
01690     popupBox.x = x;
01691     popupBox.y = y;
01692 
01693     minx = x;
01694     maxx = x;
01695 
01696     auto arr_follow=display_list.disp_list.begin();
01697     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01698         DOSBoxMenu::item &item = menu.get_item(*i);
01699 
01700         if (item.get_type() == DOSBoxMenu::vseparator_type_id) {
01701             for (;arr_follow < i;arr_follow++)
01702                 menu.get_item(*arr_follow).placeItemFinal(menu, /*finalwidth*/maxx - minx, /*toplevel*/false);
01703 
01704             x = maxx;
01705 
01706             item.screenBox.x = x;
01707             item.screenBox.y = popupBox.y;
01708             item.screenBox.w = (unsigned int)((4 * menu.fontCharScale) + 1);
01709             item.screenBox.h = y - popupBox.y;
01710 
01711             minx = maxx = x = item.screenBox.x + item.screenBox.w;
01712             y = popupBox.y;
01713         }
01714         else {
01715             item.placeItem(menu, x, y, /*toplevel*/false);
01716             y += item.screenBox.h;
01717 
01718             if (maxx < (item.screenBox.x + item.screenBox.w))
01719                 maxx = (item.screenBox.x + item.screenBox.w);
01720         }
01721     }
01722 
01723     for (;arr_follow < display_list.disp_list.end();arr_follow++)
01724         menu.get_item(*arr_follow).placeItemFinal(menu, /*finalwidth*/maxx - minx, /*toplevel*/false);
01725 
01726     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++) {
01727         DOSBoxMenu::item &item = menu.get_item(*i);
01728         int my = item.screenBox.y + item.screenBox.h;
01729         if (y < my) y = my;
01730     }
01731 
01732     for (auto i=display_list.disp_list.begin();i!=display_list.disp_list.end();i++)
01733         menu.get_item(*i).layoutSubmenu(menu, /*toplevel*/false);
01734 
01735     popupBox.w = maxx - popupBox.x;
01736     popupBox.h = y - popupBox.y;
01737 
01738     /* 1 pixel border, top */
01739     if (!isTopLevel) {
01740         borderTop = true;
01741         popupBox.y -= 1;
01742         popupBox.h += 1;
01743     }
01744     else {
01745         borderTop = false;
01746     }
01747     /* 1 pixel border, left */
01748     popupBox.x -= 1;
01749     popupBox.w += 1;
01750     /* 1 pixel border, right */
01751     popupBox.w += 1;
01752     /* 1 pixel border, bottom */
01753     popupBox.h += 1;
01754 }
01755 
01756 void DOSBoxMenu::item::placeItemFinal(DOSBoxMenu &menu,int finalwidth,bool isTopLevel) {
01757     if (type < separator_type_id) {
01758         int x = 0,rx = 0;
01759 
01760         if (!isTopLevel) {
01761             screenBox.w = finalwidth;
01762         }
01763 
01764         /* from the left */
01765         checkBox.x = x;
01766         x += checkBox.w;
01767 
01768         textBox.x = x;
01769         x += textBox.w;
01770 
01771         /* from the right */
01772         rx = screenBox.w;
01773 
01774         rx -= (int)menu.fontCharWidth;
01775 
01776         rx -= shortBox.w;
01777         shortBox.x = rx;
01778 
01779         if (!isTopLevel) {
01780             screenBox.w = finalwidth;
01781         }
01782 
01783         /* check */
01784         if (x > rx) LOG_MSG("placeItemFinal warning: text and shorttext overlap by %d pixels",x-rx);
01785     }
01786     else if (type == separator_type_id) {
01787         if (!isTopLevel) {
01788             screenBox.w = finalwidth;
01789         }
01790     }
01791 
01792     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",
01793         master_id,name.c_str(),
01794         screenBox.x,screenBox.y,
01795         screenBox.w,screenBox.h,
01796         textBox.x,textBox.y,
01797         textBox.w,textBox.h);
01798     boxInit = true;
01799 }
01800 
01801 void DOSBoxMenu::item::placeItem(DOSBoxMenu &menu,int x,int y,bool isTopLevel) {
01802     if (type < separator_type_id) {
01803         screenBox.x = x;
01804         screenBox.y = y;
01805         screenBox.w = 0;
01806         screenBox.h = (unsigned int)menu.fontCharHeight;
01807 
01808         checkBox.x = 0;
01809         checkBox.y = 0;
01810         checkBox.w = (unsigned int)menu.fontCharWidth;
01811         checkBox.h = (unsigned int)menu.fontCharHeight;
01812         screenBox.w += (unsigned int)checkBox.w;
01813 
01814         textBox.x = 0;
01815         textBox.y = 0;
01816         textBox.w = (unsigned int)menu.fontCharWidth * (unsigned int)text.length();
01817         textBox.h = (unsigned int)menu.fontCharHeight;
01818         screenBox.w += (unsigned int)textBox.w;
01819 
01820         shortBox.x = 0;
01821         shortBox.y = 0;
01822         shortBox.w = 0;
01823         shortBox.h = 0;
01824         if (!isTopLevel && !shortcut_text.empty()) {
01825             screenBox.w += (unsigned int)menu.fontCharWidth;
01826             shortBox.w += (unsigned int)menu.fontCharWidth * (unsigned int)shortcut_text.length();
01827             shortBox.h = (unsigned int)menu.fontCharHeight;
01828             screenBox.w += (unsigned int)shortBox.w;
01829         }
01830 
01831         if (!isTopLevel && type == submenu_type_id)
01832             screenBox.w += (unsigned int)menu.fontCharWidth;
01833 
01834         screenBox.w += (unsigned int)menu.fontCharWidth;
01835     }
01836     else {
01837         screenBox.x = x;
01838         screenBox.y = y;
01839         screenBox.w = (unsigned int)menu.fontCharWidth * 2;
01840         screenBox.h = (unsigned int)((4 * menu.fontCharScale) + 1);
01841 
01842         checkBox.x = 0;
01843         checkBox.y = 0;
01844         checkBox.w = 0;
01845         checkBox.h = 0;
01846 
01847         textBox.x = 0;
01848         textBox.y = 0;
01849         textBox.w = 0;
01850         textBox.h = 0;
01851 
01852         shortBox.x = 0;
01853         shortBox.y = 0;
01854         shortBox.w = 0;
01855         shortBox.h = 0;
01856     }
01857 }
01858 #endif
01859