windows-nt/Source/XPSP1/NT/sdktools/debuggers/windbg/menu.cpp
2020-09-26 16:20:57 +08:00

661 lines
15 KiB
C++

/*++
Copyright (c) 1992-2000 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_EDIT_CUT,
IDM_EDIT_COPY,
IDM_EDIT_PASTE,
IDM_EDIT_SELECT_ALL,
IDM_EDIT_ADD_TO_COMMAND_HISTORY,
IDM_EDIT_CLEAR_COMMAND_HISTORY,
IDM_EDIT_FIND,
IDM_EDIT_GOTO_ADDRESS,
IDM_EDIT_GOTO_LINE,
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_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:
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:
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_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:
fEnabled = hwndChild != NULL;
break;
case IDM_EDIT_GOTO_ADDRESS:
fEnabled = g_TargetClass != DEBUG_CLASS_UNINITIALIZED;
break;
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.
// If a debuggee is running we can only restart
// created user-mode processes.
fEnabled =
(g_TargetClass == DEBUG_CLASS_UNINITIALIZED &&
g_CommandLineStart == 1) ||
(g_DebugCommandLine != NULL &&
g_TargetClass == DEBUG_CLASS_USER_WINDOWS &&
!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, NULL);
}
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:
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;
}