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