|
|
/*++
Copyright (c) 1997-2002 Microsoft Corporation
Module Name:
cmdwin.cpp
Abstract:
New command window UI.
--*/
#include "precomp.hxx"
#pragma hdrstop
#define MAX_CMDWIN_LINES 30000
// Minimum window pane size.
#define MIN_PANE_SIZE 20
BOOL g_AutoCmdScroll = TRUE;
HMENU CMDWIN_DATA::s_ContextMenu;
#define CMDWIN_CONTEXT_ID_BASE 0x100
#define ID_WORD_WRAP 0
TBBUTTON g_CmdWinTbButtons[] = { TEXT_TB_BTN(IDM_EDIT_ADD_TO_COMMAND_HISTORY, "Add to Command Output", 0), TEXT_TB_BTN(IDM_EDIT_CLEAR_COMMAND_HISTORY, "Clear Command Output", 0), SEP_TB_BTN(), TEXT_TB_BTN(ID_WORD_WRAP, "Word wrap", 0), };
#define NUM_CMDWIN_MENU_BUTTONS \
(sizeof(g_CmdWinTbButtons) / sizeof(g_CmdWinTbButtons[0]))
//
//
//
CMDWIN_DATA::CMDWIN_DATA() // State buffer isn't currently used.
: COMMONWIN_DATA(256) { m_enumType = CMD_WINDOW; m_bTrackingMouse = FALSE; m_nDividerPosition = 0; m_EditHeight = 0; m_hwndHistory = NULL; m_hwndEdit = NULL; m_bHistoryActive = FALSE; m_Prompt = NULL; m_PromptWidth = 0; m_OutputIndex = 0; m_OutputIndexAtEnd = TRUE; m_FindSel.cpMin = 1; m_FindSel.cpMax = 0; m_FindFlags = 0; m_TabDown = FALSE; }
void CMDWIN_DATA::Validate() { COMMONWIN_DATA::Validate();
Assert(CMD_WINDOW == m_enumType);
Assert(m_hwndHistory); Assert(m_hwndEdit); }
BOOL CMDWIN_DATA::CanWriteTextToFile() { return TRUE; }
HRESULT CMDWIN_DATA::WriteTextToFile(HANDLE File) { return RicheditWriteToFile(m_hwndHistory, File); }
HMENU CMDWIN_DATA::GetContextMenu(void) { // Disable the context menu for now because it interferes
// with right-click copy and paste.
#ifdef CMD_CONTEXT_MENU
CheckMenuItem(s_ContextMenu, ID_WORD_WRAP + CMDWIN_CONTEXT_ID_BASE, MF_BYCOMMAND | (m_Wrap ? MF_CHECKED : 0)); return s_ContextMenu; #else
return NULL; #endif
}
void CMDWIN_DATA::OnContextMenuSelection(UINT Item) { // Disable the context menu for now because it interferes
// with right-click copy and paste.
#ifdef CMD_CONTEXT_MENU
Item -= CMDWIN_CONTEXT_ID_BASE; switch(Item) { case ID_WORD_WRAP: SetWordWrap(!m_Wrap); break; default: SendMessage(g_hwndFrame, WM_COMMAND, MAKELONG(Item, 1), 0); break; } #endif
} void CMDWIN_DATA::Find(PTSTR Text, ULONG Flags, BOOL FromDlg) { RicheditFind(m_hwndHistory, Text, Flags, &m_FindSel, &m_FindFlags, FALSE); }
BOOL CMDWIN_DATA::OnCreate(void) { if (s_ContextMenu == NULL) { s_ContextMenu = CreateContextMenuFromToolbarButtons (NUM_CMDWIN_MENU_BUTTONS, g_CmdWinTbButtons, CMDWIN_CONTEXT_ID_BASE); if (s_ContextMenu == NULL) { return FALSE; } } m_EditHeight = 3 * m_Font->Metrics.tmHeight / 2; m_nDividerPosition = m_Size.cy - m_EditHeight;
m_hwndHistory = CreateWindowEx( WS_EX_CLIENTEDGE, // Extended style
RICHEDIT_CLASS, // class name
NULL, // title
WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL | ES_MULTILINE | ES_READONLY, // style
0, // x
0, // y
100, // width
100, // height
m_Win, // parent
(HMENU) IDC_RICHEDIT_CMD_HISTORY, // control id
g_hInst, // hInstance
NULL); // user defined data
if ( !m_hwndHistory ) { return FALSE; }
m_PromptWidth = 4 * m_Font->Metrics.tmAveCharWidth; m_Prompt = CreateWindowEx( WS_EX_CLIENTEDGE, // Extended style
"STATIC", // class name
"", // title
WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE, // style
0, // x
100, // y
m_PromptWidth, // width
100, // height
m_Win, // parent
(HMENU) IDC_STATIC, // control id
g_hInst, // hInstance
NULL); // user defined data
if ( m_Prompt == NULL ) { return FALSE; }
m_hwndEdit = CreateWindowEx( WS_EX_CLIENTEDGE, // Extended style
RICHEDIT_CLASS, // class name
NULL, // title
WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL | ES_MULTILINE, // style
m_PromptWidth, // x
100, // y
100, // width
100, // height
m_Win, // parent
(HMENU) IDC_RICHEDIT_CMD_EDIT, // control id
g_hInst, // hInstance
NULL); // user defined data
if ( !m_hwndEdit ) { return FALSE; }
SetFont( FONT_FIXED );
// Tell the edit control we want notification of keyboard input
// so that we can automatically set focus to the edit window.
SendMessage(m_hwndHistory, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_MOUSEEVENTS);
// Tell the edit controls, that we want notification of keyboard input
// This is so we can process the enter key, and then send that text into
// the History window.
SendMessage(m_hwndEdit, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | ENM_MOUSEEVENTS);
m_Wrap = FALSE; return TRUE; }
void CMDWIN_DATA::SetFont(ULONG FontIndex) { COMMONWIN_DATA::SetFont(FontIndex);
SendMessage(m_hwndHistory, WM_SETFONT, (WPARAM)m_Font->Font, TRUE); SendMessage(m_hwndEdit, WM_SETFONT, (WPARAM)m_Font->Font, TRUE); SendMessage(m_Prompt, WM_SETFONT, (WPARAM)m_Font->Font, TRUE); }
BOOL CMDWIN_DATA::CanCopy() { HWND hwnd = m_bHistoryActive ? m_hwndHistory : m_hwndEdit; CHARRANGE chrg;
SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&chrg); return chrg.cpMin != chrg.cpMax; }
BOOL CMDWIN_DATA::CanCut() { return !m_bHistoryActive && CanCopy() && (GetWindowLong(m_hwndEdit, GWL_STYLE) & ES_READONLY) == 0; }
BOOL CMDWIN_DATA::CanPaste() { return !m_bHistoryActive && SendMessage(m_hwndEdit, EM_CANPASTE, CF_TEXT, 0); }
void CMDWIN_DATA::Copy() { HWND hwnd = m_bHistoryActive ? m_hwndHistory : m_hwndEdit;
SendMessage(hwnd, WM_COPY, 0, 0); }
void CMDWIN_DATA::Cut() { SendMessage(m_hwndEdit, WM_CUT, 0, 0); }
void CMDWIN_DATA::Paste() { SendMessage(m_hwndEdit, EM_PASTESPECIAL, CF_TEXT, 0); }
BOOL CMDWIN_DATA::CanSelectAll() { return m_bHistoryActive; }
void CMDWIN_DATA::SelectAll() { CHARRANGE Sel;
Sel.cpMin = 0; Sel.cpMax = INT_MAX; SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&Sel); }
LRESULT CMDWIN_DATA::OnCommand(WPARAM wParam, LPARAM lParam) { int idEditCtrl = (int) LOWORD(wParam); // identifier of edit control
WORD wNotifyCode = HIWORD(wParam); // notification code
HWND hwndEditCtrl = (HWND) lParam; // handle of edit control
switch (wNotifyCode) { case EN_SETFOCUS: m_bHistoryActive = IDC_RICHEDIT_CMD_HISTORY == idEditCtrl; return 0;
}
return 1; } void CMDWIN_DATA::OnSetFocus() { if (m_bHistoryActive) { ::SetFocus(m_hwndHistory); } else { ::SetFocus(m_hwndEdit); } }
void CMDWIN_DATA::OnSize(void) { ResizeChildren(FALSE); }
void CMDWIN_DATA::OnButtonDown(ULONG Button) { if (Button & MK_LBUTTON) { m_bTrackingMouse = TRUE; SetCapture(m_Win); } }
void CMDWIN_DATA::OnButtonUp(ULONG Button) { if (Button & MK_LBUTTON) { if (m_bTrackingMouse) { m_bTrackingMouse = FALSE; ReleaseCapture(); } } }
void CMDWIN_DATA::OnMouseMove(ULONG Modifiers, ULONG X, ULONG Y) { if (MK_LBUTTON & Modifiers && m_bTrackingMouse) { // We are resizing the History & Edit Windows
// Y position centered vertically around the cursor
ULONG EdgeHeight = GetSystemMetrics(SM_CYEDGE); MoveDivider(Y - EdgeHeight / 2); } }
LRESULT CMDWIN_DATA::OnNotify(WPARAM wParam, LPARAM lParam) { MSGFILTER * lpMsgFilter = (MSGFILTER *) lParam; if (EN_MSGFILTER != lpMsgFilter->nmhdr.code) { return 0; }
if (WM_RBUTTONDOWN == lpMsgFilter->msg || WM_RBUTTONDBLCLK == lpMsgFilter->msg) { // If there's a selection copy it to the clipboard
// and clear it. Otherwise try to paste.
if (CanCopy()) { Copy(); CHARRANGE Sel; HWND hwnd = m_bHistoryActive ? m_hwndHistory : m_hwndEdit; SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&Sel); Sel.cpMax = Sel.cpMin; SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&Sel); } else if (SendMessage(m_hwndEdit, EM_CANPASTE, CF_TEXT, 0)) { SetFocus(m_hwndEdit); Paste(); } // Ignore right-button events.
return 1; } else if (lpMsgFilter->msg < WM_KEYFIRST || lpMsgFilter->msg > WM_KEYLAST) { // Process all non-key events.
return 0; } else if (WM_SYSKEYDOWN == lpMsgFilter->msg || WM_SYSKEYUP == lpMsgFilter->msg || WM_SYSCHAR == lpMsgFilter->msg) { // Process menu operations though the default so
// that the Alt-minus menu works.
return 1; }
// Allow tab to toggle between the windows.
// Make sure that it isn't a Ctrl-Tab or Alt-Tab or
// a keyup when we didn't see a keydown.
if (WM_KEYUP == lpMsgFilter->msg && VK_TAB == lpMsgFilter->wParam && GetKeyState(VK_CONTROL) >= 0 && GetKeyState(VK_MENU) >= 0 && m_TabDown) { m_TabDown = FALSE; HWND hwnd = m_bHistoryActive ? m_hwndEdit : m_hwndHistory; SetFocus(hwnd); return 1; } else if (((WM_KEYDOWN == lpMsgFilter->msg || WM_KEYUP == lpMsgFilter->msg) && VK_TAB == lpMsgFilter->wParam) || (WM_CHAR == lpMsgFilter->msg && '\t' == lpMsgFilter->wParam)) { if (WM_KEYDOWN == lpMsgFilter->msg) { // Remember that we saw a tab keydown so
// that we can match it with keyup.
m_TabDown = TRUE; } return 1; }
switch (wParam) { case IDC_RICHEDIT_CMD_HISTORY: // Ignore key-ups to ignore the tail end of
// menu operations. The switch below will occur on
// key-down anyway so key-up doesn't need to do it.
if (WM_KEYUP == lpMsgFilter->msg) { return 0; } // Allow keyboard navigation in the history text.
if (WM_KEYDOWN == lpMsgFilter->msg) { switch(lpMsgFilter->wParam) { case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: case VK_PRIOR: case VK_NEXT: case VK_HOME: case VK_END: case VK_SHIFT: case VK_CONTROL: return 0; } }
// Forward key events to the edit window.
SetFocus(m_hwndEdit); SendMessage(m_hwndEdit, lpMsgFilter->msg, lpMsgFilter->wParam, lpMsgFilter->lParam); return 1; // ignore
case IDC_RICHEDIT_CMD_EDIT: // If the window isn't accepting input don't do history.
if (GetWindowLong(m_hwndEdit, GWL_STYLE) & ES_READONLY) { return 1; } static HISTORY_LIST *pHistoryList = NULL;
switch (lpMsgFilter->msg) { case WM_KEYDOWN: switch (lpMsgFilter->wParam) { default: return 0;
case VK_RETURN:
// Reset the history list
pHistoryList = NULL; int nLen; TEXTRANGE TextRange; // Get length.
// +1 we have to take into account the null terminator.
nLen = GetWindowTextLength(lpMsgFilter->nmhdr.hwndFrom) +1;
// Get everything
TextRange.chrg.cpMin = 0; TextRange.chrg.cpMax = -1; TextRange.lpstrText = (PTSTR) calloc(nLen, sizeof(TCHAR));
if (TextRange.lpstrText) { // Okay got the text
GetWindowText(m_hwndEdit, TextRange.lpstrText, nLen ); SetWindowText(m_hwndEdit, _T("") );
CmdExecuteCmd(TextRange.lpstrText, UIC_CMD_INPUT);
free(TextRange.lpstrText); }
// ignore the event
return 1;
case VK_UP: case VK_DOWN: CHARRANGE End; LRESULT LineCount;
End.cpMin = INT_MAX; End.cpMax = INT_MAX;
if (IsListEmpty( (PLIST_ENTRY) &m_listHistory )) { return 0; // process
} LineCount = SendMessage(m_hwndEdit, EM_GETLINECOUNT, 0, 0); if (LineCount != 1) { // If more than 1 line, then scroll through the text
// unless at the top or bottom line.
if (VK_UP == lpMsgFilter->wParam) { if (SendMessage(m_hwndEdit, EM_LINEINDEX, -1, 0) != 0) { return 0; } } else { if (SendMessage(m_hwndEdit, EM_LINEFROMCHAR, -1, 0) < LineCount - 1) { return 0; } } }
if (NULL == pHistoryList) { // first time scrolling thru the list,
// start at the beginning
pHistoryList = (HISTORY_LIST *) m_listHistory.Flink; SetWindowText(m_hwndEdit, pHistoryList->m_psz); // Put the cursor at the end.
SendMessage(m_hwndEdit, EM_EXSETSEL, 0, (LPARAM)&End); SendMessage(m_hwndEdit, EM_SCROLLCARET, 0, 0); return 1; // ignore
} if (VK_UP == lpMsgFilter->wParam) { // up
if (pHistoryList->Flink != (PLIST_ENTRY) &m_listHistory) { pHistoryList = (HISTORY_LIST *) pHistoryList->Flink; } else { return 0; // process
} SetWindowText(m_hwndEdit, pHistoryList->m_psz); // Put the cursor at the end.
SendMessage(m_hwndEdit, EM_EXSETSEL, 0, (LPARAM)&End); SendMessage(m_hwndEdit, EM_SCROLLCARET, 0, 0); return 1; // ignore
} else { // down
if (pHistoryList->Blink != (PLIST_ENTRY) &m_listHistory) { pHistoryList = (HISTORY_LIST *) pHistoryList->Blink; } else { return 0; // process
} SetWindowText(m_hwndEdit, pHistoryList->m_psz); // Put the cursor at the end.
SendMessage(m_hwndEdit, EM_EXSETSEL, 0, (LPARAM)&End); SendMessage(m_hwndEdit, EM_SCROLLCARET, 0, 0); return 1; // ignore
} case VK_ESCAPE: // Clear the current command.
SetWindowText(m_hwndEdit, ""); // Reset the history list
pHistoryList = NULL; return 1; } }
// process the event
return 0; }
return 0; }
void CMDWIN_DATA::OnUpdate(UpdateType Type) { PSTR Prompt = NULL; if (Type == UPDATE_EXEC || Type == UPDATE_INPUT_REQUIRED) { if (Type == UPDATE_INPUT_REQUIRED || g_ExecStatus == DEBUG_STATUS_BREAK) { SendMessage(m_hwndEdit, EM_SETBKGNDCOLOR, TRUE, 0); SendMessage(m_hwndEdit, EM_SETREADONLY, FALSE, 0); SetWindowText(m_hwndEdit, _T(""));
//
// If WinDBG is minized and we breakin, flash the window
//
if (IsIconic(g_hwndFrame) && g_FlashWindowEx != NULL) { FLASHWINFO FlashInfo = {sizeof(FLASHWINFO), g_hwndFrame, FLASHW_ALL | FLASHW_TIMERNOFG, 0, 0};
g_FlashWindowEx(&FlashInfo); } } else { PSTR Message;
if (!g_SessionActive || g_ExecStatus == DEBUG_STATUS_NO_DEBUGGEE) { Message = "Debuggee not connected"; } else { Message = "Debuggee is running..."; } SetWindowText(m_hwndEdit, Message); SendMessage(m_hwndEdit, EM_SETBKGNDCOLOR, 0, g_Colors[COL_DISABLED_WINDOW].Color); SendMessage(m_hwndEdit, EM_SETREADONLY, TRUE, 0); }
if (Type == UPDATE_INPUT_REQUIRED) { // Indicate this is an input string and not debugger
// commands.
Prompt = "Input>"; } else { // Put the existing prompt back.
Prompt = g_PromptText; } } else if (Type == UPDATE_PROMPT_TEXT) { Prompt = g_PromptText; }
if (Prompt != NULL) { if (Prompt[0] != 0) { ULONG Width = (strlen(Prompt) + 1) * m_Font->Metrics.tmAveCharWidth; if (Width != m_PromptWidth) { m_PromptWidth = Width; ResizeChildren(TRUE); } } SetWindowText(m_Prompt, Prompt); } }
ULONG CMDWIN_DATA::GetWorkspaceSize(void) { return COMMONWIN_DATA::GetWorkspaceSize() + sizeof(int) + sizeof(BOOL); }
PUCHAR CMDWIN_DATA::SetWorkspace(PUCHAR Data) { Data = COMMONWIN_DATA::SetWorkspace(Data); // The divider position is relative to the top of
// the window. This means that if the window
// grows suddenly the edit area will grow to
// fill the space rather than keeping it the same
// size. To avoid this we save the position
// relative to the bottom of the window so that
// the edit window stays the same size even when
// the overall window changes size.
// Previous versions didn't do this inversion, so
// we mark the new style with a negative value.
*(int*)Data = -(int)(m_Size.cy - m_nDividerPosition); Data += sizeof(int); *(PBOOL)Data = m_Wrap; Data += sizeof(BOOL); return Data; }
PUCHAR CMDWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End) { Data = COMMONWIN_DATA::ApplyWorkspace1(Data, End);
if (End - Data >= sizeof(int)) { int Pos = *(int*)Data; // Handle old-style top-relative positive values and
// new-style bottom-relative negative values.
MoveDivider(Pos >= 0 ? Pos : m_Size.cy + Pos); Data += sizeof(int); }
if (End - Data >= sizeof(BOOL)) { SetWordWrap(*(PBOOL)Data); Data += sizeof(BOOL); }
return Data; } void CMDWIN_DATA::UpdateColors(void) { COLORREF Fg, Bg;
// Don't update the text in the history window
// as it is already colored due to masks and
// we don't want to interfere with that.
RicheditUpdateColors(m_hwndHistory, 0, FALSE, g_Colors[COL_PLAIN].Color, TRUE); GetOutMaskColors(DEBUG_OUTPUT_PROMPT, &Fg, &Bg); RicheditUpdateColors(m_hwndEdit, Fg, TRUE, Bg, TRUE); } void CMDWIN_DATA::MoveDivider(int Pos) { if (Pos == m_nDividerPosition) { return; } m_nDividerPosition = Pos; m_EditHeight = m_Size.cy - m_nDividerPosition;
if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } SendMessage(m_Win, WM_SIZE, SIZE_RESTORED, MAKELPARAM(m_Size.cx, m_Size.cy)); }
void CMDWIN_DATA::AddCmdToHistory(PCSTR pszCmd) /*++
Description: Add a command to the command history.
If the command already exists, just move it to the beginning of the list. This way we don't repeat commands. --*/ { Assert(pszCmd);
HISTORY_LIST *p = NULL; BOOL fWhiteSpace; BOOL fFoundDuplicate;
//
// Does the command contain whitespace? If it does, it may be a command
// that requires arguments. If it does have arguments, then string
// comparisons must be case sensitive.
//
fWhiteSpace = _tcscspn(pszCmd, _T(" \t") ) != _tcslen(pszCmd);
p = (HISTORY_LIST *) m_listHistory.Flink; while (p != &m_listHistory) { fFoundDuplicate = FALSE;
if (fWhiteSpace) { if ( !_tcscmp(p->m_psz, pszCmd) ) { fFoundDuplicate = TRUE; } } else { if ( !_tcsicmp(p->m_psz, pszCmd) ) { fFoundDuplicate = TRUE; } }
if (fFoundDuplicate) { RemoveEntryList( (PLIST_ENTRY) p ); InsertHeadList( (PLIST_ENTRY) &m_listHistory, (PLIST_ENTRY) p); return; }
p = (HISTORY_LIST *) p->Flink; }
// This cmd is new to the list, add it
p = new HISTORY_LIST; if (p != NULL) { p->m_psz = _tcsdup(pszCmd); if (p->m_psz != NULL) { InsertHeadList( (PLIST_ENTRY) &m_listHistory, (PLIST_ENTRY) p); } else { delete p; } } }
void CMDWIN_DATA::AddText(PTSTR Text, COLORREF Fg, COLORREF Bg) { CHARRANGE OrigSel; POINT OrigScroll; CHARRANGE TextRange;
#if 0
DebugPrint("Add %d chars, %p - %p\n", strlen(Text), Text, Text + strlen(Text)); #endif
SendMessage(m_hwndHistory, WM_SETREDRAW, FALSE, 0); SendMessage(m_hwndHistory, EM_EXGETSEL, 0, (LPARAM)&OrigSel); SendMessage(m_hwndHistory, EM_GETSCROLLPOS, 0, (LPARAM)&OrigScroll); // The selection is lost when adding text so discard
// any previous find results.
if (g_AutoCmdScroll && m_FindSel.cpMax >= m_FindSel.cpMin) { m_FindSel.cpMin = 1; m_FindSel.cpMax = 0; }
//
// are there too many lines in the buffer?
//
INT Overflow; Overflow = (INT)SendMessage(m_hwndHistory, EM_GETLINECOUNT, 0, 0) - MAX_CMDWIN_LINES; if (Overflow > 0) { //
// delete more than we need to so it doesn't happen
// every time a line is printed.
//
TextRange.cpMin = 0; // Get the character index of the 50th line past the overflow.
TextRange.cpMax = (LONG) SendMessage(m_hwndHistory, EM_LINEINDEX, Overflow + 50, 0 );
SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM) &TextRange ); SendMessage(m_hwndHistory, EM_REPLACESEL, FALSE, (LPARAM) _T("") );
m_OutputIndex -= TextRange.cpMax; if (!g_AutoCmdScroll) { if (m_FindSel.cpMax >= m_FindSel.cpMin) { m_FindSel.cpMin -= TextRange.cpMax; m_FindSel.cpMax -= TextRange.cpMax; if (m_FindSel.cpMin < 0) { // Find is at least partially gone so
// throw it away.
m_FindSel.cpMin = 1; m_FindSel.cpMax = 0; } } OrigSel.cpMin -= TextRange.cpMax; OrigSel.cpMax -= TextRange.cpMax; if (OrigSel.cpMin < 0) { OrigSel.cpMin = 0; } if (OrigSel.cpMax < 0) { OrigSel.cpMax = 0; } } }
//
// Output the text to the cmd window. The command
// window is emulating a console window so we need
// to emulate the effects of backspaces and carriage returns.
//
for (;;) { PSTR Stop, Scan; char Save;
// Find the first occurrence of an emulated char.
// If the output index is at the end of the text
// there's no need to specially emulate newline.
// This is a very common case and not splitting
// up output for it greatly enhances append performance.
Stop = strchr(Text, '\r'); Scan = strchr(Text, '\b'); if (Stop == NULL || (Scan != NULL && Scan < Stop)) { Stop = Scan; } if (!m_OutputIndexAtEnd) { Scan = strchr(Text, '\n'); if (Stop == NULL || (Scan != NULL && Scan < Stop)) { Stop = Scan; } }
// Add all text up to the emulated char.
if (Stop != NULL) { Save = *Stop; *Stop = 0; }
if (*Text) { LONG Len = strlen(Text);
// Replace any text that might already be there.
TextRange.cpMin = m_OutputIndex; TextRange.cpMax = m_OutputIndex + Len; SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&TextRange); SendMessage(m_hwndHistory, EM_REPLACESEL, FALSE, (LPARAM)Text);
m_OutputIndex = TextRange.cpMax; CHARFORMAT2 Fmt;
ZeroMemory(&Fmt, sizeof(Fmt)); Fmt.cbSize = sizeof(Fmt); Fmt.dwMask = CFM_COLOR | CFM_BACKCOLOR; Fmt.crTextColor = Fg; Fmt.crBackColor = Bg; SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&TextRange); SendMessage(m_hwndHistory, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&Fmt); TextRange.cpMin = TextRange.cpMax; SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&TextRange); }
// If there weren't any emulated chars all the remaining text
// was just added so we're done.
if (Stop == NULL) { break; }
Text = Stop; *Stop = Save;
// Emulate the character.
if (Save == '\b') { TextRange.cpMax = m_OutputIndex; do { if (m_OutputIndex > 0) { m_OutputIndex--; } } while (*(++Text) == '\b'); TextRange.cpMin = m_OutputIndex;
SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&TextRange); SendMessage(m_hwndHistory, EM_REPLACESEL, FALSE, (LPARAM)""); } else if (Save == '\n') { // Move the output position to the next line.
// This routine always appends text to the very
// end of the control so that's always the last
// position in the control.
TextRange.cpMin = INT_MAX; TextRange.cpMax = INT_MAX; SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&TextRange); do { SendMessage(m_hwndHistory, EM_REPLACESEL, FALSE, (LPARAM)"\n"); } while (*(++Text) == '\n'); SendMessage(m_hwndHistory, EM_EXGETSEL, 0, (LPARAM)&TextRange); m_OutputIndex = TextRange.cpMax; m_OutputIndexAtEnd = TRUE; } else if (Save == '\r') { // Return the output position to the beginning of
// the current line.
TextRange.cpMin = m_OutputIndex; TextRange.cpMax = m_OutputIndex; SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&TextRange); m_OutputIndex = (LONG) SendMessage(m_hwndHistory, EM_LINEINDEX, -1, 0); m_OutputIndexAtEnd = FALSE; while (*(++Text) == '\r') { // Advance.
} } else { Assert(FALSE); } }
if (g_AutoCmdScroll) { // Force the window to scroll to the bottom of the text.
SendMessage(m_hwndHistory, EM_SCROLLCARET, 0, 0); } else { // Restore original selection.
SendMessage(m_hwndHistory, EM_EXSETSEL, 0, (LPARAM)&OrigSel); SendMessage(m_hwndHistory, EM_SETSCROLLPOS, 0, (LPARAM)&OrigScroll); }
SendMessage(m_hwndHistory, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndHistory, NULL, TRUE); }
void CMDWIN_DATA::Clear(void) { SetWindowText(m_hwndHistory, ""); m_OutputIndex = 0; m_OutputIndexAtEnd = TRUE; }
void CMDWIN_DATA::SetWordWrap(BOOL Wrap) { if (Wrap == m_Wrap) { return; } m_Wrap = Wrap; if (m_Wrap) { SendMessage(m_hwndHistory, EM_SETTARGETDEVICE, 0, 0); } else { SendMessage(m_hwndHistory, EM_SETTARGETDEVICE, 0, 1); SendMessage(m_hwndHistory, EM_SHOWSCROLLBAR, SB_HORZ, TRUE); }
if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } }
void CMDWIN_DATA::ResizeChildren(BOOL PromptChange) { const int DividerHeight = GetSystemMetrics(SM_CYEDGE); int HistoryHeight;
// Attempt to keep the input area the same size as it was
// and modify the output area. If the input area would
// take up more than half the window shrink it also.
if (m_EditHeight > (int)m_Size.cy / 2) { m_EditHeight = m_Size.cy / 2; } else if (m_EditHeight < MIN_PANE_SIZE) { m_EditHeight = MIN_PANE_SIZE; } HistoryHeight = m_Size.cy - m_EditHeight - DividerHeight / 2; m_nDividerPosition = m_Size.cy - m_EditHeight;
if ((int)m_PromptWidth > m_Size.cx / 2) { m_PromptWidth = m_Size.cx / 2; }
if (!PromptChange) { MoveWindow(m_hwndHistory, 0, 0, m_Size.cx, HistoryHeight, TRUE); }
MoveWindow(m_Prompt, 0, HistoryHeight + DividerHeight, m_PromptWidth, m_Size.cy - HistoryHeight - DividerHeight, TRUE); MoveWindow(m_hwndEdit, m_PromptWidth, HistoryHeight + DividerHeight, m_Size.cx - m_PromptWidth, m_Size.cy - HistoryHeight - DividerHeight, TRUE);
if (g_AutoCmdScroll) { if (!PromptChange) { // Keep the caret visible in both windows. The richedit
// control likes to leave the history window scrolled
// up off the top of the window so force a scroll down
// so that the history comes to the bottom of the history
// window.
SendMessage(m_hwndHistory, EM_LINESCROLL, 0, -(LONG)m_LineHeight); SendMessage(m_hwndHistory, EM_SCROLLCARET, 0, 0); }
// The edit window usually has the caret and doesn't
// need any extra scrolling.
SendMessage(m_hwndEdit, EM_SCROLLCARET, 0, 0); } }
//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------
void ClearCmdWindow(void) { HWND CmdWin = GetCmdHwnd(); if (CmdWin == NULL) { return; } PCMDWIN_DATA CmdWinData = GetCmdWinData(CmdWin); if (CmdWinData == NULL) { return; }
CmdWinData->Clear(); }
BOOL CmdOutput(PTSTR pszStr, COLORREF Fg, COLORREF Bg) { PCMDWIN_DATA pCmdWinData; BOOL fRet = TRUE;
//
// Ensure that the command window exists
//
if ( !GetCmdHwnd() ) { if ( !NewCmd_CreateWindow(g_hwndMDIClient) ) { return FALSE; } }
pCmdWinData = GetCmdWinData(GetCmdHwnd()); if (!pCmdWinData) { return FALSE; }
pCmdWinData->AddText(pszStr, Fg, Bg); return TRUE; }
void CmdLogFmt( PCTSTR lpFmt, ... ) { TCHAR szText[MAX_VAR_MSG_TXT]; va_list vargs;
va_start(vargs, lpFmt); _vsntprintf(szText, MAX_VAR_MSG_TXT - 1, lpFmt, vargs); szText[MAX_VAR_MSG_TXT - 1] = 0; va_end(vargs);
COLORREF Fg, Bg;
GetOutMaskColors(DEBUG_OUTPUT_NORMAL, &Fg, &Bg); CmdOutput(szText, Fg, Bg); }
void CmdOpenSourceFile(PSTR File) { char Found[MAX_SOURCE_PATH];
if (File == NULL) { CmdLogFmt("Usage: .open filename\n"); return; } // Look up the reported file along the source path.
// XXX drewb - Use first-match and then element walk to
// determine ambiguities and display resolution UI.
if (g_pUiLocSymbols-> FindSourceFile(0, File, DEBUG_FIND_SOURCE_BEST_MATCH | DEBUG_FIND_SOURCE_FULL_PATH, NULL, Found, sizeof(Found), NULL) != S_OK) { CmdLogFmt("Unable to find '%s'\n", File); } else { OpenOrActivateFile(Found, NULL, NULL, -1, TRUE, TRUE); } }
BOOL DirectCommand(PSTR Command) { char Term, TermText; PSTR Scan, Arg, ArgText;
//
// Check and see if this is a UI command
// vs. a command that should go to the engine.
//
while (isspace(*Command)) { Command++; } Scan = Command; while (*Scan && !isspace(*Scan)) { Scan++; } Term = *Scan; *Scan = 0;
// Advance to next nonspace char for arguments.
if (Term != 0) { Arg = Scan + 1; while (isspace(*Arg)) { Arg++; } if (*Arg == 0) { Arg = NULL; ArgText = ""; } else { ArgText = Arg; } TermText = Term; } else { Arg = NULL; ArgText = ""; TermText = ' '; }
if (!_strcmpi(Command, ".cls")) { CmdLogFmt("windbg> %s%c%s\n", Command, TermText, ArgText); ClearCmdWindow(); } else if (!_strcmpi(Command, ".hh")) { CmdLogFmt("windbg> %s%c%s\n", Command, TermText, ArgText); if (Arg == NULL) { OpenHelpTopic(HELP_TOPIC_TABLE_OF_CONTENTS); } else if (!_strnicmp(Arg, "dbgerr", 6)) { OpenHelpKeyword(Arg, TRUE); } else { OpenHelpKeyword(Arg, FALSE); } } else if (!_strcmpi(Command, ".hold_output")) { CmdLogFmt("windbg> %s%c%s\n", Command, TermText, ArgText);
if (Arg) { g_HoldWaitOutput = _strcmpi(Arg, "on") == 0; }
CmdLogFmt("Hold output until event: %s\n", g_HoldWaitOutput ? "on" : "off"); } else if (!_strcmpi(Command, ".lsrcpath") || !_strcmpi(Command, ".lsrcpath+")) { CmdLogFmt("windbg> %s%c%s\n", Command, TermText, ArgText);
*Scan = Term; // Apply source path changes to the local symbol
// object to update its source path.
if (g_RemoteClient) { char Path[MAX_ENGINE_PATH]; // Convert .lsrcpath to .srcpath.
Command[1] = '.'; g_pUiLocControl->Execute(DEBUG_OUTCTL_IGNORE, Command + 1, DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT); if (g_pUiLocSymbols->GetSourcePath(Path, sizeof(Path), NULL) == S_OK) { CmdLogFmt("Local source search path is: %s\n", Path); if (g_Workspace != NULL) { g_Workspace->SetString(WSP_GLOBAL_LOCAL_SOURCE_PATH, Path); } } // Refresh windows affected by the source path.
InvalidateStateBuffers(1 << EVENT_BIT); UpdateEngine(); } else { CmdLogFmt("lsrcpath is only enabled for remote clients\n"); } } else if (!_strcmpi(Command, ".open")) { CmdLogFmt("windbg> %s%c%s\n", Command, TermText, ArgText); CmdOpenSourceFile(Arg); } else if (!_strcmpi(Command, ".restart")) { CmdLogFmt("windbg> %s%c%s\n", Command, TermText, ArgText); AddEnumCommand(UIC_RESTART); } else if (!_strcmpi(Command, ".server")) { // We don't interpret this but we need to update
// the title.
if (Arg) { SetTitleServerText("Server '%s'", Arg); } *Scan = Term; return FALSE; } else if (!_strcmpi(Command, ".wtitle")) { CmdLogFmt("windbg> %s%c%s\n", Command, TermText, ArgText); if (!Arg) { CmdLogFmt("Usage: .wtitle title_string\n"); } else { SetTitleExplicitText(Arg); } } else { *Scan = Term; return FALSE; }
// Handled so no need to patch up the command.
return TRUE; }
int CmdExecuteCmd( PCTSTR pszCmd, UiCommand UiCmd ) { PCMDWIN_DATA pCmdWinData = NULL; PTSTR pszDupe = NULL; PTSTR pszToken = NULL;
if ( !GetCmdHwnd() ) { NewCmd_CreateWindow(g_hwndMDIClient); } pCmdWinData = GetCmdWinData(GetCmdHwnd());
pszDupe = _tcsdup(pszCmd); pszToken = _tcstok(pszDupe, _T("\r\n") );
if (pszToken == NULL) { // Blank command, important for repeats in
// the engine but not for the history window.
AddStringCommand(UiCmd, pszCmd); } else { for (; pszToken; pszToken = _tcstok(NULL, _T("\r\n") ) ) { if (pCmdWinData) { pCmdWinData->AddCmdToHistory(pszToken); }
if (!DirectCommand(pszToken)) { AddStringCommand(UiCmd, pszToken); } } }
free(pszDupe);
return 0; }
|