/*++ Copyright (c) 1992-2002 Microsoft Corporation Module Name: Menu.c Abstract: This module contains the support for Windbg's menu. --*/ #include "precomp.hxx" #pragma hdrstop MRU_ENTRY* g_MruFiles[MAX_MRU_FILES]; HMENU g_MruMenu; // // EnableMenuItemTable contains the menu IDs for all menu items whose // enabled state needs to be determined dynamically i.e. based on the state // of Windbg. // UINT g_EnableMenuItemTable[ ] = { IDM_FILE_CLOSE, IDM_FILE_OPEN_EXECUTABLE, IDM_FILE_ATTACH, IDM_FILE_OPEN_CRASH_DUMP, IDM_FILE_CONNECT_TO_REMOTE, IDM_FILE_KERNEL_DEBUG, IDM_FILE_SAVE_WORKSPACE, IDM_FILE_SAVE_WORKSPACE_AS, IDM_FILE_CLEAR_WORKSPACE, IDM_FILE_SAVE_WORKSPACE_FILE, IDM_EDIT_CUT, IDM_EDIT_COPY, IDM_EDIT_PASTE, IDM_EDIT_SELECT_ALL, IDM_EDIT_WRITE_TEXT_TO_FILE, IDM_EDIT_ADD_TO_COMMAND_HISTORY, IDM_EDIT_CLEAR_COMMAND_HISTORY, IDM_EDIT_FIND, IDM_EDIT_FIND_NEXT, IDM_EDIT_GOTO_ADDRESS, IDM_EDIT_GOTO_LINE, IDM_EDIT_GOTO_CURRENT_IP, IDM_EDIT_BREAKPOINTS, IDM_EDIT_PROPERTIES, IDM_VIEW_TOGGLE_VERBOSE, IDM_VIEW_SHOW_VERSION, IDM_DEBUG_GO, IDM_DEBUG_GO_UNHANDLED, IDM_DEBUG_GO_HANDLED, IDM_DEBUG_RESTART, IDM_DEBUG_STOPDEBUGGING, IDM_DEBUG_BREAK, IDM_DEBUG_STEPINTO, IDM_DEBUG_STEPOVER, IDM_DEBUG_STEPOUT, IDM_DEBUG_RUNTOCURSOR, IDM_DEBUG_SOURCE_MODE, IDM_DEBUG_SOURCE_MODE_ON, IDM_DEBUG_SOURCE_MODE_OFF, IDM_DEBUG_EVENT_FILTERS, IDM_DEBUG_MODULES, IDM_KDEBUG_TOGGLE_BAUDRATE, IDM_KDEBUG_TOGGLE_INITBREAK, IDM_KDEBUG_RECONNECT, IDM_WINDOW_CASCADE, IDM_WINDOW_TILE_HORZ, IDM_WINDOW_TILE_VERT, IDM_WINDOW_ARRANGE, IDM_WINDOW_ARRANGE_ICONS, IDM_WINDOW_CLOSE_ALL_DOCWIN, IDM_WINDOW_AUTO_ARRANGE, IDM_WINDOW_ARRANGE_ALL, IDM_WINDOW_OVERLAY_SOURCE, IDM_WINDOW_AUTO_DISASM, }; #define ELEMENTS_IN_ENABLE_MENU_ITEM_TABLE \ ( sizeof( g_EnableMenuItemTable ) / sizeof( g_EnableMenuItemTable[ 0 ] )) UINT CommandIdEnabled( IN UINT uMenuID ) /*++ Routine Description: Determines if a menu item is enabled/disabled based on the current state of the debugger. Arguments: uMenuID - Supplies a menu id whose state is to be determined. Return Value: UINT - Returns ( MF_ENABLED | MF_BYCOMMAND ) if the supplied menu ID is enabled, ( MF_GRAYED | MF_BYCOMMAND) otherwise. --*/ { BOOL fEnabled; HWND hwndChild = MDIGetActive(g_hwndMDIClient, NULL); PCOMMONWIN_DATA pCommonWinData; WIN_TYPES nDocType; nDocType = MINVAL_WINDOW; pCommonWinData = NULL; if (hwndChild != NULL) { pCommonWinData = GetCommonWinData(hwndChild); if (pCommonWinData != NULL) { nDocType = pCommonWinData->m_enumType; } } // // Assume menu item is not enabled. // fEnabled = FALSE; switch( uMenuID ) { case IDM_FILE_SAVE_WORKSPACE: case IDM_FILE_SAVE_WORKSPACE_AS: case IDM_FILE_CLEAR_WORKSPACE: case IDM_FILE_SAVE_WORKSPACE_FILE: fEnabled = g_Workspace != NULL; break; case IDM_DEBUG_SOURCE_MODE: case IDM_DEBUG_SOURCE_MODE_ON: case IDM_DEBUG_SOURCE_MODE_OFF: fEnabled = TRUE; CheckMenuItem(g_hmenuMain, IDM_DEBUG_SOURCE_MODE, GetSrcMode_StatusBar() ? MF_CHECKED : MF_UNCHECKED ); break; case IDM_FILE_CLOSE: fEnabled = (NULL != hwndChild); break; case IDM_FILE_OPEN_EXECUTABLE: if (g_ProcessServer) { // We can't provide a remote file browsing dialog // so just disable this option when a process // server is active. In the future we could // potentially provide a simple dialog where // you could type a command line. fEnabled = FALSE; break; } // Fall through. case IDM_FILE_ATTACH: case IDM_FILE_OPEN_CRASH_DUMP: case IDM_FILE_CONNECT_TO_REMOTE: case IDM_FILE_KERNEL_DEBUG: fEnabled = g_TargetClass == DEBUG_CLASS_UNINITIALIZED && !g_RemoteClient; break; case IDM_EDIT_CUT: if ( pCommonWinData ) { fEnabled = pCommonWinData->CanCut(); } else { fEnabled = FALSE; } break; case IDM_EDIT_COPY: if ( pCommonWinData ) { fEnabled = pCommonWinData->CanCopy(); } else { fEnabled = FALSE; } break; case IDM_EDIT_PASTE: // // If the window is normal, is not read only and is a document // or cmdwin, determine if the clipboard contains pastable data // (i.e. clipboard format CF_TEXT). // if ( !(pCommonWinData && pCommonWinData->CanPaste()) ) { fEnabled = FALSE; } else { fEnabled = FALSE; if (OpenClipboard(g_hwndFrame)) { UINT uFormat = 0; while ( uFormat = EnumClipboardFormats( uFormat )) { if ( uFormat == CF_TEXT ) { fEnabled = TRUE; break; } } CloseClipboard(); } } break; case IDM_EDIT_SELECT_ALL: if (pCommonWinData) { fEnabled = pCommonWinData->CanSelectAll(); } else { fEnabled = FALSE; } break; case IDM_EDIT_WRITE_TEXT_TO_FILE: if (pCommonWinData) { fEnabled = pCommonWinData->CanWriteTextToFile(); } else { fEnabled = FALSE; } break; case IDM_EDIT_ADD_TO_COMMAND_HISTORY: case IDM_EDIT_CLEAR_COMMAND_HISTORY: fEnabled = GetCmdHwnd() != NULL; break; case IDM_EDIT_GOTO_LINE: fEnabled = pCommonWinData != NULL && pCommonWinData->CanGotoLine(); break; case IDM_EDIT_FIND: case IDM_EDIT_FIND_NEXT: fEnabled = hwndChild != NULL; break; case IDM_EDIT_GOTO_ADDRESS: fEnabled = g_TargetClass != DEBUG_CLASS_UNINITIALIZED; break; case IDM_EDIT_GOTO_CURRENT_IP: case IDM_EDIT_BREAKPOINTS: fEnabled = IS_TARGET_HALTED(); break; case IDM_EDIT_PROPERTIES: if (pCommonWinData) { fEnabled = pCommonWinData->HasEditableProperties(); } else { fEnabled = FALSE; } break; case IDM_VIEW_TOGGLE_VERBOSE: case IDM_VIEW_SHOW_VERSION: fEnabled = g_TargetClass != DEBUG_CLASS_UNINITIALIZED; break; case IDM_DEBUG_GO: case IDM_DEBUG_GO_HANDLED: case IDM_DEBUG_GO_UNHANDLED: fEnabled = IS_TARGET_HALTED(); break; case IDM_DEBUG_RESTART: // If no debuggee is running we can only restart if // enough information was given on the command line. fEnabled = (g_TargetClass == DEBUG_CLASS_UNINITIALIZED && g_CommandLineStart == 1) || (g_TargetClass != DEBUG_CLASS_UNINITIALIZED && !g_RemoteClient && IS_TARGET_HALTED()); break; case IDM_DEBUG_STOPDEBUGGING: // Technically we can support stopping while the // debuggee is running, but that will generally // require terminating the engine thread as it // will most likely be busy and not able to // quickly exit to stop. If we terminate the // engine thread at a random point it may // leave the engine in an unstable or locked state, // so restrict restarts to situations where // the engine thread should be available. fEnabled = g_RemoteClient || IS_TARGET_HALTED(); break; case IDM_DEBUG_BREAK: fEnabled = g_TargetClass != DEBUG_CLASS_UNINITIALIZED; break; case IDM_DEBUG_STEPINTO: case IDM_DEBUG_STEPOVER: case IDM_DEBUG_STEPOUT: fEnabled = IS_TARGET_HALTED(); break; case IDM_DEBUG_RUNTOCURSOR: // // If the document can return a code address for // its cursor it is a candidate for run-to-cursor. // fEnabled = FALSE; if (IS_TARGET_HALTED() && pCommonWinData) { fEnabled = pCommonWinData->CodeExprAtCaret(NULL, 0, NULL) == S_OK; } break; case IDM_DEBUG_EVENT_FILTERS: case IDM_DEBUG_MODULES: fEnabled = IS_TARGET_HALTED(); break; case IDM_KDEBUG_TOGGLE_BAUDRATE: case IDM_KDEBUG_TOGGLE_INITBREAK: case IDM_KDEBUG_RECONNECT: fEnabled = g_TargetClass == DEBUG_CLASS_KERNEL && g_TargetClassQual == DEBUG_KERNEL_CONNECTION; break; case IDM_WINDOW_CASCADE: case IDM_WINDOW_TILE_HORZ: case IDM_WINDOW_TILE_VERT: case IDM_WINDOW_ARRANGE: case IDM_WINDOW_ARRANGE_ICONS: case IDM_WINDOW_CLOSE_ALL_DOCWIN: fEnabled = hwndChild != NULL; break; case IDM_WINDOW_AUTO_ARRANGE: CheckMenuItem(g_hmenuMain, IDM_WINDOW_AUTO_ARRANGE, g_WinOptions & WOPT_AUTO_ARRANGE ? MF_CHECKED : MF_UNCHECKED ); fEnabled = TRUE; break; case IDM_WINDOW_ARRANGE_ALL: CheckMenuItem(g_hmenuMain, IDM_WINDOW_ARRANGE_ALL, g_WinOptions & WOPT_ARRANGE_ALL ? MF_CHECKED : MF_UNCHECKED ); fEnabled = TRUE; break; case IDM_WINDOW_OVERLAY_SOURCE: CheckMenuItem(g_hmenuMain, IDM_WINDOW_OVERLAY_SOURCE, g_WinOptions & WOPT_OVERLAY_SOURCE ? MF_CHECKED : MF_UNCHECKED ); fEnabled = TRUE; break; case IDM_WINDOW_AUTO_DISASM: CheckMenuItem(g_hmenuMain, IDM_WINDOW_AUTO_DISASM, g_WinOptions & WOPT_AUTO_DISASM ? MF_CHECKED : MF_UNCHECKED ); fEnabled = TRUE; break; case IDM_FILE_OPEN: case IDM_VIEW_COMMAND: case IDM_VIEW_WATCH: case IDM_VIEW_CALLSTACK: case IDM_VIEW_MEMORY: case IDM_VIEW_LOCALS: case IDM_VIEW_REGISTERS: case IDM_VIEW_DISASM: case IDM_VIEW_SCRATCH: case IDM_VIEW_TOOLBAR: case IDM_VIEW_STATUS: case IDM_VIEW_FONT: case IDM_VIEW_OPTIONS: case IDM_EDIT_TOGGLEBREAKPOINT: case IDM_EDIT_LOG_FILE: // These items are not dynamically enabled // but are present in the toolbar. The toolbar // code requests enable state for every item on it // so these entries need to be present to return TRUE. fEnabled = TRUE; break; default: DebugPrint("CommandIdEnabled: Unhandled %d (%X)\n", uMenuID, uMenuID - MENU_SIGNATURE); // We should have handled everything. Assert(0); break; } ToolbarIdEnabled(uMenuID, fEnabled); return (( fEnabled ) ? MF_ENABLED : MF_GRAYED ) | MF_BYCOMMAND; } VOID InitializeMenu( IN HMENU hMenu ) /*++ Routine Description: InitializeMenu sets the enabled/disabled state of all menu items whose state musr be determined dynamically. Arguments: hMenu - Supplies a handle to the menu bar. Return Value: None. --*/ { INT i; Dbg(hMenu); // // Iterate thrrough the table, enabling/disabling menu items // as appropriate. // for ( i = 0; i < ELEMENTS_IN_ENABLE_MENU_ITEM_TABLE; i++ ) { EnableMenuItem(hMenu, g_EnableMenuItemTable[ i ], CommandIdEnabled( g_EnableMenuItemTable[ i ]) ); } } ULONG MruEntrySize(PTSTR File) { ULONG Len = strlen(File) + 1; return sizeof(MRU_ENTRY) + (Len & ~3); } void ClearMruMenu(void) { while (GetMenuItemCount(g_MruMenu) > 0) { if (!DeleteMenu(g_MruMenu, 0, MF_BYPOSITION)) { break; } } } VOID AddFileToMru(ULONG FileUse, PTSTR File) { ULONG Len = MruEntrySize(File); MRU_ENTRY* Entry = (MRU_ENTRY*)malloc(Len); if (Entry == NULL) { return; } if (g_MruFiles[0] == NULL) { // MRU list is empty. Delete placeholder menu entry. ClearMruMenu(); } else if (g_MruFiles[MAX_MRU_FILES - 1] != NULL) { // MRU list is full, free up the oldest entry. free(g_MruFiles[MAX_MRU_FILES - 1]); } // Push entries down. memmove(g_MruFiles + 1, g_MruFiles, (MAX_MRU_FILES - 1) * sizeof(*g_MruFiles)); g_MruFiles[0] = Entry; Entry->FileUse = FileUse; strcpy(Entry->FileName, File); // // Insert file in MRU menu. // MENUITEMINFO Item; ULONG i; ZeroMemory(&Item, sizeof(Item)); Item.cbSize = sizeof(Item); // Renumber existing items and remove any excess. i = GetMenuItemCount(g_MruMenu); while (i-- > 0) { if (i >= MAX_MRU_FILES) { DeleteMenu(g_MruMenu, i, MF_BYPOSITION); } else { Item.fMask = MIIM_ID; GetMenuItemInfo(g_MruMenu, i, TRUE, &Item); Item.wID++; SetMenuItemInfo(g_MruMenu, i, TRUE, &Item); } } Item.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING; Item.fType = MFT_STRING; Item.wID = IDM_FILE_MRU_FILE1; Item.dwTypeData = g_MruFiles[0]->FileName; InsertMenuItem(g_MruMenu, 0, TRUE, &Item); DrawMenuBar(g_hwndFrame); if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_MRU_LIST); } } void ClearMru(void) { ULONG i; for (i = 0; i < MAX_MRU_FILES; i++) { if (g_MruFiles[i] != NULL) { free(g_MruFiles[i]); g_MruFiles[i] = NULL; } else { break; } } ClearMruMenu(); DrawMenuBar(g_hwndFrame); } ULONG GetMruSize(void) { ULONG i; ULONG Size = 0; for (i = 0; i < MAX_MRU_FILES; i++) { if (g_MruFiles[i] != NULL) { Size += MruEntrySize(g_MruFiles[i]->FileName); } else { break; } } return Size; } PUCHAR ReadMru(PUCHAR Data, PUCHAR End) { ClearMru(); ULONG i; i = 0; while (Data < End) { MRU_ENTRY* DataEntry = (MRU_ENTRY*)Data; ULONG Len = MruEntrySize(DataEntry->FileName); g_MruFiles[i] = (MRU_ENTRY*)malloc(Len); if (g_MruFiles[i] == NULL) { Data = End; break; } g_MruFiles[i]->FileUse = DataEntry->FileUse; strcpy(g_MruFiles[i]->FileName, DataEntry->FileName); Data += Len; i++; } MENUITEMINFO Item; ZeroMemory(&Item, sizeof(Item)); Item.cbSize = sizeof(Item); Item.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING; Item.fType = MFT_STRING; for (i = 0; i < MAX_MRU_FILES; i++) { if (g_MruFiles[i] == NULL) { break; } Item.wID = IDM_FILE_MRU_FILE1 + i; Item.dwTypeData = g_MruFiles[i]->FileName; InsertMenuItem(g_MruMenu, i, TRUE, &Item); } DrawMenuBar(g_hwndFrame); return Data; } PUCHAR WriteMru(PUCHAR Data) { ULONG i; for (i = 0; i < MAX_MRU_FILES; i++) { if (g_MruFiles[i] != NULL) { MRU_ENTRY* DataEntry = (MRU_ENTRY*)Data; ULONG Len = MruEntrySize(g_MruFiles[i]->FileName); DataEntry->FileUse = g_MruFiles[i]->FileUse; strcpy(DataEntry->FileName, g_MruFiles[i]->FileName); Data += Len; } else { break; } } return Data; }