Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2939 lines
70 KiB

/*++
Copyright (c) 1999-2002 Microsoft Corporation
Module Name:
cmnwin.cpp
Abstract:
This module contains the code for the common window architecture.
--*/
#include "precomp.hxx"
#pragma hdrstop
ULONG g_WinOptions = WOPT_AUTO_ARRANGE | WOPT_AUTO_DISASM;
LIST_ENTRY g_ActiveWin;
PCOMMONWIN_DATA g_IndexedWin[MAXVAL_WINDOW];
HWND g_IndexedHwnd[MAXVAL_WINDOW];
INDEXED_FONT g_Fonts[FONT_COUNT];
BOOL g_LineMarkers = FALSE;
#define CW_WSP_SIG3 '3WCW'
//
//
//
COMMONWIN_DATA::COMMONWIN_DATA(ULONG ChangeBy)
: StateBuffer(ChangeBy)
{
m_Size.cx = 0;
m_Size.cy = 0;
m_CausedArrange = FALSE;
// Creation is an automatic operation so
// InAutoOp is initialized to a non-zero value.
// After CreateWindow returns it is decremented.
m_InAutoOp = 1;
m_enumType = MINVAL_WINDOW;
m_Font = &g_Fonts[FONT_FIXED];
m_FontHeight = 0;
m_LineHeight = 0;
m_Toolbar = NULL;
m_ShowToolbar = FALSE;
m_ToolbarHeight = 0;
m_MinToolbarWidth = 0;
m_ToolbarEdit = NULL;
}
void
COMMONWIN_DATA::Validate()
{
Assert(MINVAL_WINDOW < m_enumType);
Assert(m_enumType < MAXVAL_WINDOW);
}
void
COMMONWIN_DATA::SetFont(ULONG FontIndex)
{
m_Font = &g_Fonts[FontIndex];
m_FontHeight = m_Font->Metrics.tmHeight;
m_LineHeight = m_Size.cy / m_FontHeight;
}
BOOL
COMMONWIN_DATA::CanCopy()
{
if (GetFocus() == m_ToolbarEdit)
{
DWORD Start, End;
SendMessage(m_ToolbarEdit, EM_GETSEL,
(WPARAM)&Start, (WPARAM)&End);
return Start != End;
}
else
{
return FALSE;
}
}
BOOL
COMMONWIN_DATA::CanCut()
{
if (GetFocus() == m_ToolbarEdit)
{
DWORD Start, End;
SendMessage(m_ToolbarEdit, EM_GETSEL,
(WPARAM)&Start, (WPARAM)&End);
return Start != End;
}
else
{
return FALSE;
}
}
BOOL
COMMONWIN_DATA::CanPaste()
{
if (GetFocus() == m_ToolbarEdit)
{
return TRUE;
}
else
{
return FALSE;
}
}
void
COMMONWIN_DATA::Copy()
{
if (GetFocus() == m_ToolbarEdit)
{
SendMessage(m_ToolbarEdit, WM_COPY, 0, 0);
}
}
void
COMMONWIN_DATA::Cut()
{
if (GetFocus() == m_ToolbarEdit)
{
SendMessage(m_ToolbarEdit, WM_CUT, 0, 0);
}
}
void
COMMONWIN_DATA::Paste()
{
if (GetFocus() == m_ToolbarEdit)
{
SendMessage(m_ToolbarEdit, WM_PASTE, 0, 0);
}
}
BOOL
COMMONWIN_DATA::CanSelectAll()
{
return FALSE;
}
void
COMMONWIN_DATA::SelectAll()
{
}
BOOL
COMMONWIN_DATA::SelectedText(PTSTR Buffer, ULONG BufferChars)
{
return FALSE;
}
BOOL
COMMONWIN_DATA::CanWriteTextToFile()
{
return FALSE;
}
HRESULT
COMMONWIN_DATA::WriteTextToFile(HANDLE File)
{
return E_NOTIMPL;
}
BOOL
COMMONWIN_DATA::HasEditableProperties()
{
return FALSE;
}
BOOL
COMMONWIN_DATA::EditProperties()
/*++
Returns
TRUE - If properties were edited
FALSE - If nothing was changed
--*/
{
return FALSE;
}
HMENU
COMMONWIN_DATA::GetContextMenu(void)
{
return NULL;
}
void
COMMONWIN_DATA::OnContextMenuSelection(UINT Item)
{
// Nothing to do.
}
BOOL
COMMONWIN_DATA::CanGotoLine(void)
{
return FALSE;
}
void
COMMONWIN_DATA::GotoLine(ULONG Line)
{
// Do nothing.
}
void
COMMONWIN_DATA::Find(PTSTR Text, ULONG Flags, BOOL FromDlg)
{
// Do nothing.
}
HRESULT
COMMONWIN_DATA::CodeExprAtCaret(PSTR Expr, ULONG ExprSize, PULONG64 Offset)
{
return E_NOINTERFACE;
}
void
COMMONWIN_DATA::ToggleBpAtCaret(void)
{
char CodeExpr[MAX_OFFSET_EXPR];
ULONG64 Offset;
if (CodeExprAtCaret(CodeExpr, DIMA(CodeExpr), &Offset) != S_OK)
{
MessageBeep(0);
ErrorBox(NULL, 0, ERR_No_Code_For_File_Line);
return;
}
ULONG CurBpId = DEBUG_ANY_ID;
// This doesn't work too well with duplicate
// breakpoints, but that should be a minor problem.
if (IsBpAtOffset(NULL, Offset, &CurBpId) != BP_NONE)
{
PrintStringCommand(UIC_SILENT_EXECUTE, "bc %d", CurBpId);
}
else
{
PrintStringCommand(UIC_SILENT_EXECUTE, "bp %s", CodeExpr);
}
}
BOOL
COMMONWIN_DATA::OnCreate(void)
{
return TRUE;
}
LRESULT
COMMONWIN_DATA::OnCommand(WPARAM wParam, LPARAM lParam)
{
return 1;
}
void
COMMONWIN_DATA::OnSetFocus(void)
{
}
void
COMMONWIN_DATA::OnSize(void)
{
RECT Rect;
// Resize the toolbar.
if (m_Toolbar != NULL && m_ShowToolbar)
{
// If the toolbar gets too small sometimes it's better
// to just let it get clipped rather than have it
// try to fit into a narrow column.
if (m_Size.cx >= m_MinToolbarWidth)
{
MoveWindow(m_Toolbar, 0, 0, m_Size.cx, m_ToolbarHeight, TRUE);
}
// Record what size it ended up.
GetClientRect(m_Toolbar, &Rect);
m_ToolbarHeight = Rect.bottom - Rect.top;
if (m_FontHeight != 0)
{
if (m_ToolbarHeight >= m_Size.cy)
{
m_LineHeight = 0;
}
else
{
m_LineHeight = (m_Size.cy - m_ToolbarHeight) / m_FontHeight;
}
}
}
else
{
Assert(m_ToolbarHeight == 0);
}
}
void
COMMONWIN_DATA::OnButtonDown(ULONG Button)
{
}
void
COMMONWIN_DATA::OnButtonUp(ULONG Button)
{
}
void
COMMONWIN_DATA::OnMouseMove(ULONG Modifiers, ULONG X, ULONG Y)
{
}
void
COMMONWIN_DATA::OnTimer(WPARAM TimerId)
{
}
LRESULT
COMMONWIN_DATA::OnGetMinMaxInfo(LPMINMAXINFO Info)
{
return 1;
}
LRESULT
COMMONWIN_DATA::OnVKeyToItem(WPARAM wParam, LPARAM lParam)
{
return -1;
}
LRESULT
COMMONWIN_DATA::OnNotify(WPARAM wParam, LPARAM lParam)
{
return 0;
}
void
COMMONWIN_DATA::OnUpdate(UpdateType Type)
{
}
void
COMMONWIN_DATA::OnDestroy(void)
{
}
LRESULT
COMMONWIN_DATA::OnOwnerDraw(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return 0;
}
ULONG
COMMONWIN_DATA::GetWorkspaceSize(void)
{
return 3 * sizeof(ULONG) + sizeof(WINDOWPLACEMENT);
}
PUCHAR
COMMONWIN_DATA::SetWorkspace(PUCHAR Data)
{
// First store the special signature that marks
// this version of the workspace data.
*(PULONG)Data = CW_WSP_SIG3;
Data += sizeof(ULONG);
// Store the size saved by this layer.
*(PULONG)Data = COMMONWIN_DATA::GetWorkspaceSize();
Data += sizeof(ULONG);
//
// Store the actual data.
//
*(PULONG)Data = m_ShowToolbar;
Data += sizeof(ULONG);
LPWINDOWPLACEMENT Place = (LPWINDOWPLACEMENT)Data;
Place->length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(m_Win, Place);
Data += sizeof(WINDOWPLACEMENT);
return Data;
}
PUCHAR
COMMONWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End)
{
ULONG_PTR Size = End - Data;
// There are three versions of the base COMMONWIN data.
// 1. RECT.
// 2. WINDOWPLACEMENT.
// 3. CW_WSP_SIG3 sized block.
// All three cases can be easily distinguished.
if (Size > 2 * sizeof(ULONG) &&
*(PULONG)Data == CW_WSP_SIG3 &&
Size >= *(PULONG)(Data + sizeof(ULONG)))
{
Size = *(PULONG)(Data + sizeof(ULONG)) - 2 * sizeof(ULONG);
Data += 2 * sizeof(ULONG);
if (Size >= sizeof(ULONG))
{
SetShowToolbar(*(PULONG)Data);
Size -= sizeof(ULONG);
Data += sizeof(ULONG);
}
}
if (Size >= sizeof(WINDOWPLACEMENT) &&
((LPWINDOWPLACEMENT)Data)->length == sizeof(WINDOWPLACEMENT))
{
LPWINDOWPLACEMENT Place = (LPWINDOWPLACEMENT)Data;
if (!IsAutoArranged(m_enumType))
{
SetWindowPlacement(m_Win, Place);
}
return (PUCHAR)(Place + 1);
}
else
{
LPRECT Rect = (LPRECT)Data;
Assert((PUCHAR)(Rect + 1) <= End);
if (!IsAutoArranged(m_enumType))
{
MoveWindow(m_Win, Rect->left, Rect->top,
(Rect->right - Rect->left), (Rect->bottom - Rect->top),
TRUE);
}
return (PUCHAR)(Rect + 1);
}
}
void
COMMONWIN_DATA::UpdateColors(void)
{
// Nothing to do.
}
void
COMMONWIN_DATA::UpdateSize(ULONG Width, ULONG Height)
{
m_Size.cx = Width;
m_Size.cy = Height;
if (m_FontHeight != 0)
{
m_LineHeight = m_Size.cy / m_FontHeight;
}
}
void
COMMONWIN_DATA::SetShowToolbar(BOOL Show)
{
if (!m_Toolbar)
{
return;
}
m_ShowToolbar = Show;
if (m_ShowToolbar)
{
ShowWindow(m_Toolbar, SW_SHOW);
}
else
{
ShowWindow(m_Toolbar, SW_HIDE);
m_ToolbarHeight = 0;
}
OnSize();
if (g_Workspace != NULL)
{
g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS);
}
}
PCOMMONWIN_DATA
NewWinData(WIN_TYPES Type)
{
switch(Type)
{
case DOC_WINDOW:
return new DOCWIN_DATA;
case WATCH_WINDOW:
return new WATCHWIN_DATA;
case LOCALS_WINDOW:
return new LOCALSWIN_DATA;
case CPU_WINDOW:
return new CPUWIN_DATA;
case DISASM_WINDOW:
return new DISASMWIN_DATA;
case CMD_WINDOW:
return new CMDWIN_DATA;
case SCRATCH_PAD_WINDOW:
return new SCRATCH_PAD_DATA;
case MEM_WINDOW:
return new MEMWIN_DATA;
#if 0
case QUICKW_WINDOW:
// XXX drewb - Unimplemented.
return new QUICKWWIN_DATA;
#endif
case CALLS_WINDOW:
return new CALLSWIN_DATA;
case PROCESS_THREAD_WINDOW:
return new PROCESS_THREAD_DATA;
default:
Assert(FALSE);
return NULL;
}
}
LRESULT
CALLBACK
COMMONWIN_DATA::WindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
PCOMMONWIN_DATA pWinData = GetCommonWinData(hwnd);
#if 0
{
DebugPrint("CommonWin msg %X for %p, args %X %X\n",
uMsg, pWinData, wParam, lParam);
}
#endif
if (uMsg != WM_CREATE && pWinData == NULL)
{
return DefMDIChildProc(hwnd, uMsg, wParam, lParam);
}
switch (uMsg)
{
case WM_CREATE:
RECT rc;
COMMONWIN_CREATE_DATA* Data;
Assert(NULL == pWinData);
Data = (COMMONWIN_CREATE_DATA*)
((LPMDICREATESTRUCT)
(((CREATESTRUCT *)lParam)->lpCreateParams))->lParam;
pWinData = NewWinData(Data->Type);
if (!pWinData)
{
return -1; // Fail window creation
}
Assert(pWinData->m_enumType == Data->Type);
pWinData->m_Win = hwnd;
GetClientRect(hwnd, &rc);
pWinData->m_Size.cx = rc.right;
pWinData->m_Size.cy = rc.bottom;
if ( !pWinData->OnCreate() )
{
delete pWinData;
return -1; // Fail window creation
}
// store this in the window
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pWinData);
#if DBG
pWinData->Validate();
#endif
g_IndexedWin[Data->Type] = pWinData;
g_IndexedHwnd[Data->Type] = hwnd;
InsertHeadList(&g_ActiveWin, &pWinData->m_ActiveWin);
if (g_Workspace != NULL)
{
g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS);
}
SendMessage(hwnd, WM_SETICON, 0, (LPARAM)
LoadIcon(g_hInst,
MAKEINTRESOURCE(pWinData->m_enumType +
MINVAL_WINDOW_ICON)));
// A new buffer has been created so put it in the list
// then wake up the engine to fill it.
Dbg_EnterCriticalSection(&g_QuickLock);
InsertHeadList(&g_StateList, pWinData);
Dbg_LeaveCriticalSection(&g_QuickLock);
UpdateEngine();
// Force initial updates so that the window starts
// out with a state which matches the current debug
// session's state.
PostMessage(hwnd, WU_UPDATE, UPDATE_BUFFER, 0);
PostMessage(hwnd, WU_UPDATE, UPDATE_EXEC, 0);
if (g_WinOptions & WOPT_AUTO_ARRANGE)
{
Arrange();
}
return 0;
case WM_COMMAND:
if (pWinData->OnCommand(wParam, lParam) == 0)
{
return 0;
}
break;
case WM_SETFOCUS:
pWinData->OnSetFocus();
break;
case WM_MOVE:
// When the frame window is minimized or restored
// a move to 0,0 comes through. Ignore this so
// as to not trigger the warning.
if (!IsIconic(g_hwndFrame) && lParam != 0 &&
!IsIconic(hwnd) && !pWinData->m_CausedArrange)
{
DisplayAutoArrangeWarning(pWinData);
}
if (g_Workspace != NULL)
{
g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
case WM_SIZE:
if (wParam == SIZE_MAXHIDE || wParam == SIZE_MAXSHOW)
{
// We don't care about cover/uncover events.
break;
}
// We don't care about size events while the frame is
// minimized as the children can't be seen. When
// the frame is restored a new size event will come through
// and things will get updated when they're actually visible.
if (IsIconic(g_hwndFrame))
{
break;
}
if (wParam == SIZE_RESTORED && !pWinData->m_CausedArrange)
{
DisplayAutoArrangeWarning(pWinData);
}
if (g_Workspace != NULL)
{
g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS);
}
pWinData->UpdateSize(LOWORD(lParam), HIWORD(lParam));
// No need to run sizing code for minimize.
if (wParam == SIZE_MINIMIZED)
{
// The minimized window will leave a hole so
// arrange to fill it and leave space for the
// minimized window.
if (g_WinOptions & WOPT_AUTO_ARRANGE)
{
pWinData->m_CausedArrange = TRUE;
Arrange();
}
break;
}
if (wParam == SIZE_RESTORED && pWinData->m_CausedArrange)
{
// If we're restoring a window that caused
// a rearrange when it was minimized we
// need to update things to account for it.
pWinData->m_CausedArrange = FALSE;
if (g_WinOptions & WOPT_AUTO_ARRANGE)
{
Arrange();
}
}
else if (wParam == SIZE_MAXIMIZED)
{
// Ask for a rearrange on restore just
// for consistency with minimize.
pWinData->m_CausedArrange = TRUE;
}
pWinData->OnSize();
break;
case WM_LBUTTONDOWN:
pWinData->OnButtonDown(MK_LBUTTON);
return 0;
case WM_LBUTTONUP:
pWinData->OnButtonUp(MK_LBUTTON);
return 0;
case WM_MBUTTONDOWN:
pWinData->OnButtonDown(MK_MBUTTON);
return 0;
case WM_MBUTTONUP:
pWinData->OnButtonUp(MK_MBUTTON);
return 0;
case WM_RBUTTONDOWN:
pWinData->OnButtonDown(MK_RBUTTON);
return 0;
case WM_RBUTTONUP:
pWinData->OnButtonUp(MK_RBUTTON);
return 0;
case WM_MOUSEMOVE:
pWinData->OnMouseMove((ULONG)wParam, LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_TIMER:
pWinData->OnTimer(wParam);
return 0;
case WM_GETMINMAXINFO:
if (pWinData->OnGetMinMaxInfo((LPMINMAXINFO)lParam) == 0)
{
return 0;
}
break;
case WM_VKEYTOITEM:
return pWinData->OnVKeyToItem(wParam, lParam);
case WM_NOTIFY:
return pWinData->OnNotify(wParam, lParam);
case WU_UPDATE:
pWinData->OnUpdate((UpdateType)wParam);
return 0;
case WU_RECONFIGURE:
pWinData->OnSize();
break;
case WM_DESTROY:
pWinData->OnDestroy();
SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
g_IndexedWin[pWinData->m_enumType] = NULL;
g_IndexedHwnd[pWinData->m_enumType] = NULL;
RemoveEntryList(&pWinData->m_ActiveWin);
if (g_Workspace != NULL)
{
g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS);
}
// Mark this buffer as ready for cleanup by the
// engine when it gets around to it.
pWinData->m_Win = NULL;
if (pWinData == g_FindLast)
{
g_FindLast = NULL;
}
UpdateEngine();
if (g_WinOptions & WOPT_AUTO_ARRANGE)
{
Arrange();
}
break;
case WM_MEASUREITEM:
case WM_DRAWITEM:
//
// Both these messages must be handled by owner drawn windows
//
return pWinData->OnOwnerDraw(uMsg, wParam, lParam);
case WM_CTLCOLORLISTBOX:
// Substitute windbg's default window colors.
SetTextColor((HDC)wParam, g_Colors[COL_PLAIN_TEXT].Color);
SetBkColor((HDC)wParam, g_Colors[COL_PLAIN].Color);
return (LRESULT)g_Colors[COL_PLAIN].Brush;
}
return DefMDIChildProc(hwnd, uMsg, wParam, lParam);
}
//
//
//
SINGLE_CHILDWIN_DATA::SINGLE_CHILDWIN_DATA(ULONG ChangeBy)
: COMMONWIN_DATA(ChangeBy)
{
m_hwndChild = NULL;
}
void
SINGLE_CHILDWIN_DATA::Validate()
{
COMMONWIN_DATA::Validate();
Assert(m_hwndChild);
}
void
SINGLE_CHILDWIN_DATA::SetFont(ULONG FontIndex)
{
COMMONWIN_DATA::SetFont(FontIndex);
SendMessage(m_hwndChild,
WM_SETFONT,
(WPARAM) m_Font->Font,
(LPARAM) TRUE
);
}
BOOL
SINGLE_CHILDWIN_DATA::CanCopy()
{
if (GetFocus() != m_hwndChild)
{
return COMMONWIN_DATA::CanCopy();
}
switch (m_enumType)
{
default:
Assert(!"Unknown type");
return FALSE;
case CMD_WINDOW:
Assert(!"Should not be handled here since this is only for windows"
" with only one child window.");
return FALSE;
case WATCH_WINDOW:
case LOCALS_WINDOW:
case CPU_WINDOW:
case QUICKW_WINDOW:
return -1 != ListView_GetNextItem(m_hwndChild,
-1, // Find the first match
LVNI_FOCUSED
);
case CALLS_WINDOW:
return LB_ERR != SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0);
case DOC_WINDOW:
case DISASM_WINDOW:
case MEM_WINDOW:
case SCRATCH_PAD_WINDOW:
CHARRANGE chrg;
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&chrg);
return chrg.cpMin != chrg.cpMax;
case PROCESS_THREAD_WINDOW:
return NULL != TreeView_GetSelection(m_hwndChild);
}
}
BOOL
SINGLE_CHILDWIN_DATA::CanCut()
{
if (GetFocus() != m_hwndChild)
{
return COMMONWIN_DATA::CanCut();
}
switch (m_enumType)
{
default:
Assert(!"Unknown type");
return FALSE;
case CMD_WINDOW:
Assert(!"Should not be handled here since this is only for windows"
" with only one child window.");
return FALSE;
case WATCH_WINDOW:
case LOCALS_WINDOW:
case CPU_WINDOW:
case QUICKW_WINDOW:
case CALLS_WINDOW:
case DOC_WINDOW:
case DISASM_WINDOW:
case MEM_WINDOW:
case PROCESS_THREAD_WINDOW:
return FALSE;
case SCRATCH_PAD_WINDOW:
CHARRANGE chrg;
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&chrg);
return chrg.cpMin != chrg.cpMax;
}
}
BOOL
SINGLE_CHILDWIN_DATA::CanPaste()
{
if (GetFocus() != m_hwndChild)
{
return COMMONWIN_DATA::CanPaste();
}
switch (m_enumType)
{
default:
Assert(!"Unknown type");
return FALSE;
case CMD_WINDOW:
Assert(!"Should not be handled here since this is only for windows"
" with only one child window.");
return FALSE;
case WATCH_WINDOW:
case LOCALS_WINDOW:
case CPU_WINDOW:
case QUICKW_WINDOW:
case CALLS_WINDOW:
case DOC_WINDOW:
case DISASM_WINDOW:
case MEM_WINDOW:
case PROCESS_THREAD_WINDOW:
return FALSE;
case SCRATCH_PAD_WINDOW:
return TRUE;
}
}
void
SINGLE_CHILDWIN_DATA::Copy()
{
if (GetFocus() != m_hwndChild)
{
COMMONWIN_DATA::Copy();
}
else
{
SendMessage(m_hwndChild, WM_COPY, 0, 0);
}
}
void
SINGLE_CHILDWIN_DATA::Cut()
{
if (GetFocus() != m_hwndChild)
{
COMMONWIN_DATA::Paste();
}
}
void
SINGLE_CHILDWIN_DATA::Paste()
{
if (GetFocus() != m_hwndChild)
{
COMMONWIN_DATA::Paste();
}
}
void
SINGLE_CHILDWIN_DATA::OnSetFocus()
{
::SetFocus(m_hwndChild);
}
void
SINGLE_CHILDWIN_DATA::OnSize(void)
{
COMMONWIN_DATA::OnSize();
MoveWindow(m_hwndChild, 0, m_ToolbarHeight,
m_Size.cx, m_Size.cy - m_ToolbarHeight, TRUE);
}
void
SINGLE_CHILDWIN_DATA::UpdateColors(void)
{
// Force a repaint of the child.
InvalidateRect(m_hwndChild, NULL, TRUE);
}
//
//
//
PROCESS_THREAD_DATA::PROCESS_THREAD_DATA()
: SINGLE_CHILDWIN_DATA(512)
{
m_enumType = PROCESS_THREAD_WINDOW;
m_TotalSystems = 0;
m_NamesOffset = 0;
}
void
PROCESS_THREAD_DATA::Validate()
{
SINGLE_CHILDWIN_DATA::Validate();
Assert(PROCESS_THREAD_WINDOW == m_enumType);
}
HRESULT
PROCESS_THREAD_DATA::ReadProcess(ULONG ProcId, PULONG Offset)
{
HRESULT Status;
PULONG ThreadIds, ThreadSysIds;
ULONG NumThread;
char Name[MAX_PATH];
ULONG NameLen;
PULONG Data;
if ((Status = g_pDbgSystem->
SetCurrentProcessId(ProcId)) != S_OK ||
(Status = g_pDbgSystem->GetNumberThreads(&NumThread)) != S_OK)
{
return Status;
}
if (FAILED(Status = g_pDbgSystem->
GetCurrentProcessExecutableName(Name, sizeof(Name),
NULL)))
{
PrintString(Name, DIMA(Name), "<%s>", FormatStatusCode(Status));
}
NameLen = strlen(Name) + 1;
if (NameLen > 1)
{
PSTR NameStore = (PSTR)AddData(NameLen);
if (NameStore == NULL)
{
return E_OUTOFMEMORY;
}
strcpy(NameStore, Name);
}
// Refresh pointers in case a resize
// caused buffer movement.
Data = (PULONG)GetDataBuffer() + *Offset;
*Data++ = NumThread;
*Data++ = NameLen;
*Offset += 2;
ThreadIds = Data;
ThreadSysIds = ThreadIds + NumThread;
if ((Status = g_pDbgSystem->
GetThreadIdsByIndex(0, NumThread,
ThreadIds, ThreadSysIds)) != S_OK)
{
return Status;
}
*Offset += 2 * NumThread;
return S_OK;
}
HRESULT
PROCESS_THREAD_DATA::ReadSystem(ULONG SysId,
PULONG Offset)
{
HRESULT Status;
ULONG ProcIdsOffset;
PULONG ProcIds, ProcSysIds;
ULONG NumProc;
ULONG i;
char Name[MAX_PATH + 32];
ULONG NameLen;
PULONG Data;
if (g_pDbgSystem3)
{
if ((Status = g_pDbgSystem3->
SetCurrentSystemId(SysId)) != S_OK ||
FAILED(Status = g_pDbgSystem3->
GetCurrentSystemServerName(Name, sizeof(Name), NULL)))
{
return Status;
}
}
else
{
Name[0] = 0;
}
NameLen = strlen(Name) + 1;
if (NameLen > 1)
{
PSTR NameStore = (PSTR)AddData(NameLen);
if (NameStore == NULL)
{
return E_OUTOFMEMORY;
}
strcpy(NameStore, Name);
}
if ((Status = g_pDbgSystem->
GetNumberProcesses(&NumProc)) != S_OK)
{
return Status;
}
// Refresh pointers in case a resize
// caused buffer movement.
Data = (PULONG)GetDataBuffer() + *Offset;
*Data++ = NumProc;
*Data++ = NameLen;
*Offset += 2;
if (NumProc == 0)
{
return S_OK;
}
ProcIds = Data;
ProcIdsOffset = *Offset;
ProcSysIds = ProcIds + NumProc;
if ((Status = g_pDbgSystem->
GetProcessIdsByIndex(0, NumProc, ProcIds, ProcSysIds)) != S_OK)
{
return Status;
}
*Offset += 2 * NumProc;
for (i = 0; i < NumProc; i++)
{
ProcIds = (PULONG)GetDataBuffer() + ProcIdsOffset;
if ((Status = ReadProcess(ProcIds[i], Offset)) != S_OK)
{
return Status;
}
}
return S_OK;
}
HRESULT
PROCESS_THREAD_DATA::ReadState(void)
{
HRESULT Status;
ULONG CurProc;
ULONG TotalSys, TotalThread, TotalProc;
ULONG MaxProcThread, MaxSysThread, MaxSysProc;
PULONG SysIds;
ULONG i;
ULONG Offset;
ULONG NamesOffset;
if ((Status = g_pDbgSystem->GetCurrentProcessId(&CurProc)) != S_OK)
{
return Status;
}
if (g_pDbgSystem3)
{
if ((Status = g_pDbgSystem3->GetNumberSystems(&TotalSys)) != S_OK ||
(Status = g_pDbgSystem3->
GetTotalNumberThreadsAndProcesses(&TotalThread, &TotalProc,
&MaxProcThread, &MaxSysThread,
&MaxSysProc)) != S_OK)
{
return Status;
}
}
else
{
if ((Status = g_pDbgSystem->GetNumberProcesses(&TotalProc)) != S_OK ||
(Status = g_pDbgSystem->
GetTotalNumberThreads(&TotalThread, &MaxProcThread)) != S_OK)
{
return Status;
}
TotalSys = 1;
MaxSysThread = MaxProcThread;
MaxSysProc = TotalProc;
}
Empty();
NamesOffset = (TotalSys * 3 + TotalProc * 4 + TotalThread * 2) *
sizeof(ULONG);
SysIds = (PULONG)AddData(NamesOffset);
if (SysIds == NULL)
{
return E_OUTOFMEMORY;
}
if (g_pDbgSystem3)
{
if ((Status = g_pDbgSystem3->
GetSystemIdsByIndex(0, TotalSys, SysIds)) != S_OK)
{
return Status;
}
}
else
{
*SysIds = 0;
}
ULONG OutMask, LogMask;
// Ignore thread notifications as we're changing the thread.
g_IgnoreThreadChange = TRUE;
// Switching threads causes output which we don't want so
// ignore all output.
g_pDbgClient->GetOutputMask(&OutMask);
g_pDbgControl->GetLogMask(&LogMask);
g_pDbgClient->SetOutputMask(0);
g_pDbgControl->SetLogMask(0);
Offset = TotalSys;
for (i = 0; i < TotalSys; i++)
{
SysIds = (PULONG)GetDataBuffer();
if ((Status = ReadSystem(SysIds[i], &Offset)) != S_OK)
{
break;
}
}
// This will also set the current system and thread
// from the process information.
g_pDbgSystem->SetCurrentProcessId(CurProc);
g_IgnoreThreadChange = FALSE;
g_pDbgClient->SetOutputMask(OutMask);
g_pDbgControl->SetLogMask(LogMask);
if (Status == S_OK)
{
m_TotalSystems = TotalSys;
m_NamesOffset = NamesOffset;
}
return Status;
}
BOOL
PROCESS_THREAD_DATA::OnCreate(void)
{
if (!SINGLE_CHILDWIN_DATA::OnCreate())
{
return FALSE;
}
m_hwndChild = CreateWindow(
WC_TREEVIEW, // class name
NULL, // title
WS_CLIPSIBLINGS |
WS_CHILD | WS_VISIBLE |
WS_HSCROLL | WS_VSCROLL |
TVS_HASBUTTONS | TVS_LINESATROOT |
TVS_HASLINES, // style
0, // x
0, // y
m_Size.cx, // width
m_Size.cy, // height
m_Win, // parent
(HMENU) IDC_PROCESS_TREE, // control id
g_hInst, // hInstance
NULL); // user defined data
if (!m_hwndChild)
{
return FALSE;
}
SetFont(FONT_FIXED);
SendMessage(m_hwndChild, TVM_SETTEXTCOLOR,
0, g_Colors[COL_PLAIN_TEXT].Color);
SendMessage(m_hwndChild, TVM_SETBKCOLOR,
0, g_Colors[COL_PLAIN].Color);
return TRUE;
}
LRESULT
PROCESS_THREAD_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm)
{
LPNMTREEVIEW Tvn;
HTREEITEM Sel;
Tvn = (LPNMTREEVIEW)Lpm;
if (Tvn->hdr.idFrom != IDC_PROCESS_TREE)
{
return FALSE;
}
switch(Tvn->hdr.code)
{
case TVN_SELCHANGED:
if (Tvn->action == TVC_BYMOUSE)
{
SetCurThreadFromProcessTreeItem(m_hwndChild, Tvn->itemNew.hItem);
}
break;
case NM_DBLCLK:
case NM_RETURN:
Sel = TreeView_GetSelection(m_hwndChild);
if (Sel)
{
SetCurThreadFromProcessTreeItem(m_hwndChild, Sel);
}
return TRUE;
}
return FALSE;
}
void
PROCESS_THREAD_DATA::OnUpdate(UpdateType Type)
{
if (Type != UPDATE_BUFFER &&
Type != UPDATE_EXEC)
{
return;
}
HRESULT Status;
Status = UiLockForRead();
if (Status != S_OK)
{
return;
}
ULONG Sys;
ULONG NameLen;
PULONG SysIds, Data;
char Text[MAX_PATH + 64];
PSTR Names;
TVINSERTSTRUCT Insert;
HTREEITEM CurThreadItem = NULL;
SysIds = (PULONG)GetDataBuffer();
Data = SysIds + m_TotalSystems;
Names = (PSTR)GetDataBuffer() + m_NamesOffset;
TreeView_DeleteAllItems(m_hwndChild);
for (Sys = 0; Sys < m_TotalSystems; Sys++)
{
HTREEITEM SysItem;
ULONG NumProc, Proc;
PULONG ProcIds, ProcSysIds;
NumProc = *Data++;
NameLen = *Data++;
ProcIds = Data;
ProcSysIds = ProcIds + NumProc;
Data = ProcSysIds + NumProc;
sprintf(Text, "%d ", SysIds[Sys]);
if (NameLen > 1)
{
CatString(Text, Names, DIMA(Text));
Names += strlen(Names) + 1;
}
if (m_TotalSystems > 1)
{
Insert.hParent = TVI_ROOT;
Insert.hInsertAfter = TVI_LAST;
Insert.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
Insert.item.pszText = Text;
Insert.item.state =
SysIds[Sys] == g_CurSystemId ? TVIS_EXPANDED | TVIS_BOLD: 0;
Insert.item.stateMask = TVIS_EXPANDED | TVIS_BOLD;
// Parameter is the thread ID to set to select the given system.
Insert.item.lParam = NumProc > 0 ? (LPARAM)Data[2] : (LPARAM)-1;
SysItem = TreeView_InsertItem(m_hwndChild, &Insert);
}
else
{
SysItem = TVI_ROOT;
}
for (Proc = 0; Proc < NumProc; Proc++)
{
HTREEITEM ProcItem;
ULONG NumThread, Thread;
PULONG ThreadIds, ThreadSysIds;
NumThread = *Data++;
NameLen = *Data++;
ThreadIds = Data;
ThreadSysIds = Data + NumThread;
Data = ThreadSysIds + NumThread;
sprintf(Text, "%03d:%x ", ProcIds[Proc], ProcSysIds[Proc]);
if (NameLen > 1)
{
CatString(Text, Names, DIMA(Text));
Names += strlen(Names) + 1;
}
Insert.hParent = SysItem;
Insert.hInsertAfter = TVI_LAST;
Insert.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
Insert.item.pszText = Text;
Insert.item.state =
SysIds[Sys] == g_CurSystemId &&
ProcIds[Proc] == g_CurProcessId ?
TVIS_EXPANDED | TVIS_BOLD: 0;
Insert.item.stateMask = TVIS_EXPANDED | TVIS_BOLD;
// Parameter is the thread ID to set to select the given thread.
Insert.item.lParam = (LPARAM)ThreadIds[0];
ProcItem = TreeView_InsertItem(m_hwndChild, &Insert);
for (Thread = 0; Thread < NumThread; Thread++)
{
HTREEITEM ThreadItem;
sprintf(Text, "%03d:%x",
ThreadIds[Thread], ThreadSysIds[Thread]);
Insert.hParent = ProcItem;
Insert.hInsertAfter = TVI_LAST;
Insert.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
Insert.item.pszText = Text;
Insert.item.state =
SysIds[Sys] == g_CurSystemId &&
ProcIds[Proc] == g_CurProcessId &&
ThreadIds[Thread] == g_CurThreadId ?
TVIS_BOLD : 0;
Insert.item.stateMask = TVIS_BOLD;
Insert.item.lParam = (LPARAM)ThreadIds[Thread];
ThreadItem = TreeView_InsertItem(m_hwndChild, &Insert);
if (Insert.item.state & TVIS_BOLD)
{
CurThreadItem = ThreadItem;
}
}
}
}
if (CurThreadItem)
{
TreeView_Select(m_hwndChild, CurThreadItem, TVGN_CARET);
}
UnlockStateBuffer(this);
}
void
PROCESS_THREAD_DATA::UpdateColors(void)
{
SendMessage(m_hwndChild, TVM_SETTEXTCOLOR,
0, g_Colors[COL_PLAIN_TEXT].Color);
SendMessage(m_hwndChild, TVM_SETBKCOLOR,
0, g_Colors[COL_PLAIN].Color);
InvalidateRect(m_hwndChild, NULL, TRUE);
}
void
PROCESS_THREAD_DATA::SetCurThreadFromProcessTreeItem(HWND Tree, HTREEITEM Sel)
{
TVITEM Item;
Item.hItem = Sel;
Item.mask = TVIF_CHILDREN | TVIF_PARAM;
TreeView_GetItem(Tree, &Item);
if (Item.lParam != (LPARAM)-1)
{
g_pUiSystem->SetCurrentThreadId((ULONG)Item.lParam);
}
}
//
//
//
EDITWIN_DATA::EDITWIN_DATA(ULONG ChangeBy)
: SINGLE_CHILDWIN_DATA(ChangeBy)
{
m_TextLines = 0;
m_Highlights = NULL;
}
void
EDITWIN_DATA::Validate()
{
SINGLE_CHILDWIN_DATA::Validate();
}
void
EDITWIN_DATA::SetFont(ULONG FontIndex)
{
SINGLE_CHILDWIN_DATA::SetFont(FontIndex);
// Force the tabstop size to be recomputed
// with the new font.
SendMessage(m_hwndChild, EM_SETTABSTOPS, 1, (LPARAM)&g_TabWidth);
}
BOOL
EDITWIN_DATA::CanSelectAll()
{
return TRUE;
}
void
EDITWIN_DATA::SelectAll()
{
CHARRANGE Sel;
Sel.cpMin = 0;
Sel.cpMax = INT_MAX;
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel);
}
BOOL
EDITWIN_DATA::OnCreate(void)
{
m_hwndChild = CreateWindowEx(
WS_EX_CLIENTEDGE, // Extended style
RICHEDIT_CLASS, // class name
NULL, // title
WS_CLIPSIBLINGS
| WS_CHILD | WS_VISIBLE
| WS_VSCROLL | ES_AUTOVSCROLL
| WS_HSCROLL | ES_AUTOHSCROLL
| ES_READONLY
| ES_MULTILINE, // style
0, // x
m_ToolbarHeight, // y
m_Size.cx, // width
m_Size.cy - m_ToolbarHeight, // height
m_Win, // parent
(HMENU) 0, // control id
g_hInst, // hInstance
NULL); // user defined data
if (m_hwndChild)
{
CHARFORMAT2 Fmt;
SetFont(FONT_FIXED);
SendMessage(m_hwndChild, EM_SETBKGNDCOLOR, FALSE,
g_Colors[COL_PLAIN].Color);
ZeroMemory(&Fmt, sizeof(Fmt));
Fmt.cbSize = sizeof(Fmt);
Fmt.dwMask = CFM_COLOR;
Fmt.crTextColor = g_Colors[COL_PLAIN_TEXT].Color;
SendMessage(m_hwndChild, EM_SETCHARFORMAT,
SCF_SELECTION, (LPARAM)&Fmt);
}
return m_hwndChild != NULL;
}
LRESULT
EDITWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm)
{
NMHDR* Hdr = (NMHDR*)Lpm;
if (Hdr->code == EN_SAVECLIPBOARD)
{
// Indicate that the clipboard contents should
// be kept alive.
return 0;
}
else if (Hdr->code == EN_MSGFILTER)
{
MSGFILTER* Filter = (MSGFILTER*)Lpm;
if (WM_SYSKEYDOWN == Filter->msg ||
WM_SYSKEYUP == Filter->msg ||
WM_SYSCHAR == Filter->msg)
{
// Force default processing for menu operations
// so that the Alt-minus menu comes up.
return 1;
}
}
return 0;
}
void
EDITWIN_DATA::OnDestroy(void)
{
EDIT_HIGHLIGHT* Next;
while (m_Highlights != NULL)
{
Next = m_Highlights->Next;
delete m_Highlights;
m_Highlights = Next;
}
SINGLE_CHILDWIN_DATA::OnDestroy();
}
void
EDITWIN_DATA::UpdateColors(void)
{
RicheditUpdateColors(m_hwndChild,
g_Colors[COL_PLAIN_TEXT].Color, TRUE,
g_Colors[COL_PLAIN].Color, TRUE);
UpdateCurrentLineHighlight();
UpdateBpMarks();
}
void
EDITWIN_DATA::SetCurrentLineHighlight(ULONG Line)
{
//
// Clear any other current line highlight in this window.
// Also, only one doc window can have a current IP highlight so if
// this is a doc window getting a current IP highlight make
// sure no other doc windows have a current IP highlight.
//
if (m_enumType == DOC_WINDOW && ULONG_MAX != Line)
{
RemoveActiveWinHighlights(1 << DOC_WINDOW, EHL_CURRENT_LINE);
}
else
{
RemoveAllHighlights(EHL_CURRENT_LINE);
}
if (ULONG_MAX != Line)
{
AddHighlight(Line, EHL_CURRENT_LINE);
RicheditScrollToLine(m_hwndChild, Line, m_LineHeight);
}
}
void
EDITWIN_DATA::UpdateCurrentLineHighlight(void)
{
EDIT_HIGHLIGHT* Hl;
for (Hl = m_Highlights; Hl != NULL; Hl = Hl->Next)
{
if (Hl->Flags & EHL_CURRENT_LINE)
{
break;
}
}
if (Hl)
{
ApplyHighlight(Hl);
}
}
EDIT_HIGHLIGHT*
EDITWIN_DATA::GetLineHighlighting(ULONG Line)
{
EDIT_HIGHLIGHT* Hl;
for (Hl = m_Highlights; Hl != NULL; Hl = Hl->Next)
{
if (Hl->Line == Line)
{
return Hl;
}
}
return NULL;
}
void
EDITWIN_DATA::ApplyHighlight(EDIT_HIGHLIGHT* Hl)
{
CHARRANGE OldSel;
BOOL HasFocus = ::GetFocus() == m_hwndChild;
// Get the old selection and scroll position.
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&OldSel);
// Disable the window to prevent auto-scrolling
// when the selection is set.
EnableWindow(m_hwndChild, FALSE);
//
// Compute the highlight information.
//
char Markers[LINE_MARKERS + 1];
CHARFORMAT2 Fmt;
ULONG TextCol, BgCol;
Markers[2] = 0;
ZeroMemory(&Fmt, sizeof(Fmt));
Fmt.cbSize = sizeof(Fmt);
Fmt.dwMask = CFM_COLOR | CFM_BACKCOLOR;
if (Hl->Flags & EHL_CURRENT_LINE)
{
Markers[1] = '>';
switch(Hl->Flags & EHL_ANY_BP)
{
case EHL_ENABLED_BP:
Markers[0] = 'B';
TextCol = COL_BP_CURRENT_LINE_TEXT;
BgCol = COL_BP_CURRENT_LINE;
break;
case EHL_DISABLED_BP:
Markers[0] = 'D';
TextCol = COL_BP_CURRENT_LINE_TEXT;
BgCol = COL_BP_CURRENT_LINE;
break;
default:
Markers[0] = ' ';
TextCol = COL_CURRENT_LINE_TEXT;
BgCol = COL_CURRENT_LINE;
break;
}
}
else
{
Markers[1] = ' ';
switch(Hl->Flags & EHL_ANY_BP)
{
case EHL_ENABLED_BP:
Markers[0] = 'B';
TextCol = COL_ENABLED_BP_TEXT;
BgCol = COL_ENABLED_BP;
break;
case EHL_DISABLED_BP:
Markers[0] = 'D';
TextCol = COL_DISABLED_BP_TEXT;
BgCol = COL_DISABLED_BP;
break;
default:
Markers[0] = ' ';
TextCol = COL_PLAIN_TEXT;
BgCol = COL_PLAIN;
break;
}
}
Fmt.crTextColor = g_Colors[TextCol].Color;
Fmt.crBackColor = g_Colors[BgCol].Color;
//
// Select the line to be highlighted
//
CHARRANGE FmtSel;
FmtSel.cpMin = (LONG)SendMessage(m_hwndChild, EM_LINEINDEX, Hl->Line, 0);
if (g_LineMarkers)
{
// Replace the markers at the beginning of the line.
FmtSel.cpMax = FmtSel.cpMin + 2;
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&FmtSel);
SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)Markers);
}
// Color the line.
FmtSel.cpMax = FmtSel.cpMin + (LONG)
SendMessage(m_hwndChild, EM_LINELENGTH, FmtSel.cpMin, 0) + 1;
if (g_LineMarkers)
{
FmtSel.cpMin += 2;
}
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&FmtSel);
SendMessage(m_hwndChild, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&Fmt);
// Restore the old selection
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&OldSel);
EnableWindow(m_hwndChild, TRUE);
// The disabling of the window caused the richedit
// to forget its focus status so force the focus
// back if it had it.
if (HasFocus)
{
::SetFocus(m_hwndChild);
}
}
EDIT_HIGHLIGHT*
EDITWIN_DATA::AddHighlight(ULONG Line, ULONG Flags)
{
EDIT_HIGHLIGHT* Hl;
// Search for an existing highlight record for the line.
Hl = GetLineHighlighting(Line);
if (Hl == NULL)
{
Hl = new EDIT_HIGHLIGHT;
if (Hl == NULL)
{
return NULL;
}
Hl->Data = 0;
Hl->Line = Line;
Hl->Flags = 0;
Hl->Next = m_Highlights;
m_Highlights = Hl;
}
Hl->Flags |= Flags;
ApplyHighlight(Hl);
return Hl;
}
void
EDITWIN_DATA::RemoveHighlight(ULONG Line, ULONG Flags)
{
EDIT_HIGHLIGHT* Hl;
EDIT_HIGHLIGHT* Prev;
// Search for an existing highlight record for the line.
Prev = NULL;
for (Hl = m_Highlights; Hl != NULL; Hl = Hl->Next)
{
if (Hl->Line == Line)
{
break;
}
Prev = Hl;
}
if (Hl == NULL)
{
return;
}
Hl->Flags &= ~Flags;
ApplyHighlight(Hl);
if (Hl->Flags == 0)
{
if (Prev == NULL)
{
m_Highlights = Hl->Next;
}
else
{
Prev->Next = Hl->Next;
}
delete Hl;
}
}
void
EDITWIN_DATA::RemoveAllHighlights(ULONG Flags)
{
EDIT_HIGHLIGHT* Hl;
EDIT_HIGHLIGHT* Next;
EDIT_HIGHLIGHT* Prev;
Prev = NULL;
for (Hl = m_Highlights; Hl != NULL; Hl = Next)
{
Next = Hl->Next;
if (Hl->Flags & Flags)
{
Hl->Flags &= ~Flags;
ApplyHighlight(Hl);
if (Hl->Flags == 0)
{
if (Prev == NULL)
{
m_Highlights = Hl->Next;
}
else
{
Prev->Next = Hl->Next;
}
delete Hl;
}
else
{
Prev = Hl;
}
}
else
{
Prev = Hl;
}
}
}
void
EDITWIN_DATA::RemoveActiveWinHighlights(ULONG Types, ULONG Flags)
{
PLIST_ENTRY Entry = g_ActiveWin.Flink;
while (Entry != &g_ActiveWin)
{
PEDITWIN_DATA WinData = (PEDITWIN_DATA)
ACTIVE_WIN_ENTRY(Entry);
if (Types & (1 << WinData->m_enumType))
{
WinData->RemoveAllHighlights(Flags);
}
Entry = Entry->Flink;
}
}
void
EDITWIN_DATA::UpdateBpMarks(void)
{
// Empty implementation for derived classes
// that do not show BP marks.
}
int
EDITWIN_DATA::CheckForFileChanges(PCSTR File, FILETIME* LastWrite)
{
HANDLE Handle;
Handle = CreateFile(File, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
NULL);
if (Handle == INVALID_HANDLE_VALUE)
{
goto Changed;
}
FILETIME NewWrite;
if (!GetFileTime(Handle, NULL, NULL, &NewWrite))
{
if (!GetFileTime(Handle, &NewWrite, NULL, NULL))
{
ZeroMemory(&NewWrite, sizeof(NewWrite));
}
}
CloseHandle(Handle);
if (CompareFileTime(LastWrite, &NewWrite) == 0)
{
// No change.
return IDCANCEL;
}
Changed:
return
g_QuietSourceMode == QMODE_ALWAYS_YES ? IDYES :
(g_QuietSourceMode == QMODE_ALWAYS_NO ? IDCANCEL :
QuestionBox(ERR_File_Has_Changed, MB_YESNO, File));
}
//
//
//
SCRATCH_PAD_DATA::SCRATCH_PAD_DATA()
: EDITWIN_DATA(16)
{
m_enumType = SCRATCH_PAD_WINDOW;
}
void
SCRATCH_PAD_DATA::Validate()
{
EDITWIN_DATA::Validate();
Assert(SCRATCH_PAD_WINDOW == m_enumType);
}
void
SCRATCH_PAD_DATA::Cut()
{
SendMessage(m_hwndChild, WM_CUT, 0, 0);
}
void
SCRATCH_PAD_DATA::Paste()
{
SendMessage(m_hwndChild, EM_PASTESPECIAL, CF_TEXT, 0);
}
BOOL
SCRATCH_PAD_DATA::CanWriteTextToFile()
{
return TRUE;
}
HRESULT
SCRATCH_PAD_DATA::WriteTextToFile(HANDLE File)
{
return RicheditWriteToFile(m_hwndChild, File);
}
BOOL
SCRATCH_PAD_DATA::OnCreate(void)
{
if (!EDITWIN_DATA::OnCreate())
{
return FALSE;
}
SendMessage(m_hwndChild, EM_SETOPTIONS, ECOOP_AND, ~ECO_READONLY);
SendMessage(m_hwndChild, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS);
return TRUE;
}
LRESULT
SCRATCH_PAD_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm)
{
MSGFILTER* Filter = (MSGFILTER *)Lpm;
if (EN_MSGFILTER != Filter->nmhdr.code)
{
return 0;
}
if (WM_RBUTTONDOWN == Filter->msg ||
WM_RBUTTONDBLCLK == Filter->msg)
{
// If there's a selection copy it to the clipboard
// and clear it. Otherwise try to paste.
if (CanCopy())
{
Copy();
CHARRANGE Sel;
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM)&Sel);
Sel.cpMax = Sel.cpMin;
SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel);
}
else if (SendMessage(m_hwndChild, EM_CANPASTE, CF_TEXT, 0))
{
Paste();
}
// Ignore right-button events.
return 1;
}
return 0;
}
//
//
//
DISASMWIN_DATA::DISASMWIN_DATA()
: EDITWIN_DATA(2048)
{
m_enumType = DISASM_WINDOW;
sprintf(m_OffsetExpr, "0x%I64x", g_EventIp);
m_UpdateExpr = FALSE;
m_FirstInstr = 0;
m_LastInstr = 0;
}
void
DISASMWIN_DATA::Validate()
{
EDITWIN_DATA::Validate();
Assert(DISASM_WINDOW == m_enumType);
}
HRESULT
DISASMWIN_DATA::ReadState(void)
{
HRESULT Status;
// Sample these values right away in case the UI changes them.
ULONG LinesTotal = m_LineHeight;
ULONG LinesBefore = LinesTotal / 2;
DEBUG_VALUE Value;
if ((Status = g_pDbgControl->Evaluate(m_OffsetExpr, DEBUG_VALUE_INT64,
&Value, NULL)) != S_OK)
{
return Status;
}
m_PrimaryInstr = Value.I64;
// Reserve space at the beginning of the buffer to
// store the line to offset mapping table.
PULONG64 LineMap;
Empty();
LineMap = (PULONG64)AddData(sizeof(ULONG64) * LinesTotal);
if (LineMap == NULL)
{
return E_OUTOFMEMORY;
}
// We also need to allocate a temporary line map to
// pass to the engine for filling. This can't be
// the state buffer data since that may move as
// output is generated.
LineMap = new ULONG64[LinesTotal];
if (LineMap == NULL)
{
return E_OUTOFMEMORY;
}
g_OutStateBuf.SetBuffer(this);
if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK)
{
delete [] LineMap;
return Status;
}
Status = g_pOutCapControl->
OutputDisassemblyLines(DEBUG_OUTCTL_THIS_CLIENT |
DEBUG_OUTCTL_OVERRIDE_MASK |
DEBUG_OUTCTL_NOT_LOGGED,
LinesBefore, LinesTotal, m_PrimaryInstr,
DEBUG_DISASM_EFFECTIVE_ADDRESS |
DEBUG_DISASM_MATCHING_SYMBOLS,
&m_PrimaryLine, &m_FirstInstr, &m_LastInstr,
LineMap);
memcpy(m_Data, LineMap, sizeof(ULONG64) * LinesTotal);
delete [] LineMap;
if (Status != S_OK)
{
g_OutStateBuf.End(FALSE);
return Status;
}
m_TextLines = LinesTotal;
m_TextOffset = LinesTotal * sizeof(ULONG64);
// The line map is generated with offsets followed by
// invalid offsets for continuation lines. We want
// the offsets to be on the last line of the disassembly
// for a continuation set so move them down.
// We don't want to move the offsets down to blank lines,
// though, such as the blank lines that separate bundles
// in IA64 disassembly.
LineMap = (PULONG64)m_Data;
PULONG64 LineMapEnd = LineMap + m_TextLines;
PULONG64 SetStart;
PSTR Text = (PSTR)m_Data + m_TextOffset;
PSTR PrevText;
while (LineMap < LineMapEnd)
{
if (*LineMap != DEBUG_INVALID_OFFSET)
{
SetStart = LineMap;
for (;;)
{
PrevText = Text;
Text = strchr(Text, '\n') + 1;
LineMap++;
if (LineMap >= LineMapEnd ||
*LineMap != DEBUG_INVALID_OFFSET ||
*Text == '\n')
{
break;
}
}
LineMap--;
Text = PrevText;
if (LineMap > SetStart)
{
*LineMap = *SetStart;
*SetStart = DEBUG_INVALID_OFFSET;
}
}
LineMap++;
Text = strchr(Text, '\n') + 1;
}
#ifdef DEBUG_DISASM
LineMap = (PULONG64)m_Data;
for (Line = 0; Line < m_TextLines; Line++)
{
DebugPrint("%d: %I64x\n", Line, LineMap[Line]);
}
#endif
return g_OutStateBuf.End(TRUE);
}
HRESULT
DISASMWIN_DATA::CodeExprAtCaret(PSTR Expr, ULONG ExprSize, PULONG64 Offset)
{
HRESULT Status;
LRESULT LineChar;
LONG Line;
PULONG64 LineMap;
if ((Status = UiLockForRead()) != S_OK)
{
// Don't want to return any success codes here.
return FAILED(Status) ? Status : E_FAIL;
}
LineChar = SendMessage(m_hwndChild, EM_LINEINDEX, -1, 0);
Line = (LONG)SendMessage(m_hwndChild, EM_EXLINEFROMCHAR, 0, LineChar);
if (Line < 0 || (ULONG)Line >= m_TextLines)
{
Status = E_INVALIDARG;
goto Unlock;
}
ULONG64 LineOff;
// Look up the offset in the line map. If it's part of
// a multiline group move forward to the offset.
LineMap = (PULONG64)m_Data;
LineOff = LineMap[Line];
while ((ULONG)(Line + 1) < m_TextLines && LineOff == DEBUG_INVALID_OFFSET)
{
Line++;
LineOff = LineMap[Line];
}
if (Expr != NULL)
{
if (!PrintString(Expr, ExprSize, "0x%I64x", LineOff))
{
Status = E_INVALIDARG;
goto Unlock;
}
}
if (Offset != NULL)
{
*Offset = LineOff;
}
Status = S_OK;
Unlock:
UnlockStateBuffer(this);
return Status;
}
BOOL
DISASMWIN_DATA::OnCreate(void)
{
RECT Rect;
ULONG Height;
Height = GetSystemMetrics(SM_CYVSCROLL) + 4 * GetSystemMetrics(SM_CYEDGE);
m_Toolbar = CreateWindowEx(0, REBARCLASSNAME, NULL,
WS_VISIBLE | WS_CHILD |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
CCS_NODIVIDER | CCS_NOPARENTALIGN |
RBS_VARHEIGHT | RBS_BANDBORDERS,
0, 0, m_Size.cx, Height, m_Win,
(HMENU)ID_TOOLBAR,
g_hInst, NULL);
if (m_Toolbar == NULL)
{
return FALSE;
}
REBARINFO BarInfo;
BarInfo.cbSize = sizeof(BarInfo);
BarInfo.fMask = 0;
BarInfo.himl = NULL;
SendMessage(m_Toolbar, RB_SETBARINFO, 0, (LPARAM)&BarInfo);
m_ToolbarEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL,
WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL,
0, 0, 18 * m_Font->Metrics.tmAveCharWidth,
Height, m_Toolbar, (HMENU)IDC_EDIT_OFFSET,
g_hInst, NULL);
if (m_ToolbarEdit == NULL)
{
return FALSE;
}
SendMessage(m_ToolbarEdit, WM_SETFONT, (WPARAM)m_Font->Font, 0);
SendMessage(m_ToolbarEdit, EM_LIMITTEXT, sizeof(m_OffsetExpr) - 1, 0);
GetClientRect(m_ToolbarEdit, &Rect);
REBARBANDINFO BandInfo;
BandInfo.cbSize = sizeof(BandInfo);
BandInfo.fMask = RBBIM_STYLE | RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE;
BandInfo.fStyle = RBBS_FIXEDSIZE;
BandInfo.lpText = "Offset:";
BandInfo.hwndChild = m_ToolbarEdit;
BandInfo.cxMinChild = Rect.right - Rect.left;
BandInfo.cyMinChild = Rect.bottom - Rect.top;
SendMessage(m_Toolbar, RB_INSERTBAND, -1, (LPARAM)&BandInfo);
// If the toolbar is allowed to shrink too small it hangs
// while resizing. Just let it clip off below a certain width.
m_MinToolbarWidth = BandInfo.cxMinChild * 2;
PSTR PrevText = "Previous";
m_PreviousButton =
AddButtonBand(m_Toolbar, PrevText, PrevText, IDC_DISASM_PREVIOUS);
m_NextButton =
AddButtonBand(m_Toolbar, "Next", PrevText, IDC_DISASM_NEXT);
if (m_PreviousButton == NULL || m_NextButton == NULL)
{
return FALSE;
}
// Maximize the space for the offset expression.
SendMessage(m_Toolbar, RB_MAXIMIZEBAND, 0, FALSE);
GetClientRect(m_Toolbar, &Rect);
m_ToolbarHeight = Rect.bottom - Rect.top;
m_ShowToolbar = TRUE;
if (!EDITWIN_DATA::OnCreate())
{
return FALSE;
}
// Suppress the scroll bar as the text is always
// fitted to the window size.
SendMessage(m_hwndChild, EM_SHOWSCROLLBAR, SB_VERT, FALSE);
SendMessage(m_hwndChild, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
return TRUE;
}
LRESULT
DISASMWIN_DATA::OnCommand(WPARAM Wpm, LPARAM Lpm)
{
switch(LOWORD(Wpm))
{
case IDC_EDIT_OFFSET:
if (HIWORD(Wpm) == EN_CHANGE)
{
// This message is sent on every keystroke
// which causes a bit too much updating.
// Set up a timer to trigger the actual
// update in half a second.
SetTimer(m_Win, IDC_EDIT_OFFSET, EDIT_DELAY, NULL);
m_UpdateExpr = TRUE;
}
break;
case IDC_DISASM_PREVIOUS:
ScrollLower();
break;
case IDC_DISASM_NEXT:
ScrollHigher();
break;
}
return 0;
}
void
DISASMWIN_DATA::OnSize(void)
{
EDITWIN_DATA::OnSize();
// Force buffer to refill for new line count.
UiRequestRead();
}
void
DISASMWIN_DATA::OnTimer(WPARAM TimerId)
{
if (TimerId == IDC_EDIT_OFFSET && m_UpdateExpr)
{
m_UpdateExpr = FALSE;
GetWindowText(m_ToolbarEdit, m_OffsetExpr, sizeof(m_OffsetExpr));
UiRequestRead();
}
}
LRESULT
DISASMWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm)
{
MSGFILTER* Filter = (MSGFILTER*)Lpm;
if (Filter->nmhdr.code != EN_MSGFILTER)
{
return EDITWIN_DATA::OnNotify(Wpm, Lpm);
}
if (Filter->msg == WM_KEYDOWN)
{
switch(Filter->wParam)
{
case VK_UP:
{
CHARRANGE range;
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM) &range);
if (!SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0))
{
// up arrow on top line, scroll
ScrollLower();
return 1;
}
break;
}
case VK_DOWN:
{
CHARRANGE range;
int MaxLine;
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM) &range);
MaxLine = (int) SendMessage(m_hwndChild, EM_GETLINECOUNT, 0, 0);
if (MaxLine == (1+SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0)))
{
// down arrow on bottom line, scroll
ScrollHigher();
return 1;
}
break;
}
case VK_PRIOR:
ScrollLower();
return 1;
case VK_NEXT:
ScrollHigher();
return 1;
}
}
else if (WM_SYSKEYDOWN == Filter->msg ||
WM_SYSKEYUP == Filter->msg ||
WM_SYSCHAR == Filter->msg)
{
// Force default processing for menu operations
// so that the Alt-minus menu comes up.
return 1;
}
return 0;
}
void
DISASMWIN_DATA::OnUpdate(UpdateType Type)
{
if (Type == UPDATE_BP ||
Type == UPDATE_END_SESSION)
{
UpdateBpMarks();
return;
}
else if (Type != UPDATE_BUFFER)
{
return;
}
HRESULT Status;
Status = UiLockForRead();
if (Status == S_OK)
{
PULONG64 LineMap;
ULONG Line;
if (!g_LineMarkers)
{
SendMessage(m_hwndChild, WM_SETTEXT,
0, (LPARAM)m_Data + m_TextOffset);
}
else
{
SendMessage(m_hwndChild, WM_SETTEXT, 0, (LPARAM)"");
PSTR Text = (PSTR)m_Data + m_TextOffset;
for (;;)
{
SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)" ");
PSTR NewLine = strchr(Text, '\n');
if (NewLine != NULL)
{
*NewLine = 0;
}
SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)Text);
if (NewLine == NULL)
{
break;
}
SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)"\n");
*NewLine = '\n';
Text = NewLine + 1;
}
}
// Highlight the last line of multiline disassembly.
LineMap = (PULONG64)m_Data;
Line = m_PrimaryLine;
while (Line + 1 < m_TextLines &&
LineMap[Line] == DEBUG_INVALID_OFFSET)
{
Line++;
}
SetCurrentLineHighlight(Line);
UnlockStateBuffer(this);
RicheditUpdateColors(m_hwndChild,
g_Colors[COL_PLAIN_TEXT].Color, TRUE,
0, FALSE);
UpdateCurrentLineHighlight();
UpdateBpMarks();
EnableWindow(m_PreviousButton, m_FirstInstr != m_PrimaryInstr);
EnableWindow(m_NextButton, m_LastInstr != m_PrimaryInstr);
}
else
{
SendLockStatusMessage(m_hwndChild, WM_SETTEXT, Status);
RemoveCurrentLineHighlight();
}
}
void
DISASMWIN_DATA::UpdateBpMarks(void)
{
if (m_TextLines == 0 ||
UiLockForRead() != S_OK)
{
return;
}
if (g_BpBuffer->UiLockForRead() != S_OK)
{
UnlockStateBuffer(this);
return;
}
SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0);
// Remove existing BP highlights.
RemoveAllHighlights(EHL_ANY_BP);
//
// Highlight every line that matches a breakpoint.
//
PULONG64 LineMap = (PULONG64)m_Data;
BpBufferData* BpData = (BpBufferData*)g_BpBuffer->GetDataBuffer();
ULONG Line;
BpStateType State;
for (Line = 0; Line < m_TextLines; Line++)
{
if (*LineMap != DEBUG_INVALID_OFFSET)
{
State = IsBpAtOffset(BpData, *LineMap, NULL);
if (State != BP_NONE)
{
AddHighlight(Line, State == BP_ENABLED ?
EHL_ENABLED_BP : EHL_DISABLED_BP);
}
}
LineMap++;
}
SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0);
InvalidateRect(m_hwndChild, NULL, TRUE);
UnlockStateBuffer(g_BpBuffer);
UnlockStateBuffer(this);
}
void
DISASMWIN_DATA::SetCurInstr(ULONG64 Offset)
{
// Any pending user update is now irrelevant.
m_UpdateExpr = FALSE;
sprintf(m_OffsetExpr, "0x%I64x", Offset);
// Force engine to update buffer.
UiRequestRead();
}
void
RicheditFind(HWND Edit,
PTSTR Text, ULONG Flags,
CHARRANGE* SaveSel, PULONG SaveFlags,
BOOL HideSel)
{
if (Text == NULL)
{
// Clear last find.
if (SaveSel->cpMax >= SaveSel->cpMin)
{
if (*SaveFlags & FR_DOWN)
{
SaveSel->cpMin = SaveSel->cpMax;
}
else
{
SaveSel->cpMax = SaveSel->cpMin;
}
if (HideSel)
{
SendMessage(Edit, EM_SETOPTIONS, ECOOP_AND, ~ECO_NOHIDESEL);
}
SendMessage(Edit, EM_EXSETSEL, 0, (LPARAM)SaveSel);
SendMessage(Edit, EM_SCROLLCARET, 0, 0);
SaveSel->cpMin = 1;
SaveSel->cpMax = 0;
}
}
else
{
LRESULT Match;
FINDTEXTEX Find;
SendMessage(Edit, EM_EXGETSEL, 0, (LPARAM)&Find.chrg);
if (Flags & FR_DOWN)
{
if (Find.chrg.cpMax > Find.chrg.cpMin)
{
Find.chrg.cpMin++;
}
Find.chrg.cpMax = LONG_MAX;
}
else
{
Find.chrg.cpMax = 0;
}
Find.lpstrText = Text;
Match = SendMessage(Edit, EM_FINDTEXTEX, Flags, (LPARAM)&Find);
if (Match != -1)
{
*SaveSel = Find.chrgText;
*SaveFlags = Flags;
if (HideSel)
{
SendMessage(Edit, EM_SETOPTIONS, ECOOP_OR, ECO_NOHIDESEL);
}
SendMessage(Edit, EM_EXSETSEL, 0, (LPARAM)SaveSel);
SendMessage(Edit, EM_SCROLLCARET, 0, 0);
}
else
{
if (g_FindDialog)
{
EnableWindow(g_FindDialog, FALSE);
}
InformationBox(ERR_No_More_Matches, Text);
if (g_FindDialog)
{
EnableWindow(g_FindDialog, TRUE);
SetFocus(g_FindDialog);
}
}
}
}
DWORD CALLBACK
StreamOutCb(DWORD_PTR File, LPBYTE Buffer, LONG Request, PLONG Done)
{
return WriteFile((HANDLE)File, Buffer, Request, (LPDWORD)Done, NULL) ?
0 : GetLastError();
}
HRESULT
RicheditWriteToFile(HWND Edit, HANDLE File)
{
EDITSTREAM Stream;
Stream.dwCookie = (DWORD_PTR)File;
Stream.dwError = 0;
Stream.pfnCallback = StreamOutCb;
SendMessage(Edit, EM_STREAMOUT, SF_TEXT, (LPARAM)&Stream);
if (Stream.dwError)
{
return HRESULT_FROM_WIN32(Stream.dwError);
}
return S_OK;
}
void
RicheditUpdateColors(HWND Edit,
COLORREF Fg, BOOL UpdateFg,
COLORREF Bg, BOOL UpdateBg)
{
if (UpdateBg)
{
if (UpdateFg)
{
SendMessage(Edit, WM_SETREDRAW, FALSE, 0);
}
SendMessage(Edit, EM_SETBKGNDCOLOR, FALSE, Bg);
if (UpdateFg)
{
SendMessage(Edit, WM_SETREDRAW, TRUE, 0);
}
}
if (UpdateFg)
{
CHARFORMAT2 Fmt;
ZeroMemory(&Fmt, sizeof(Fmt));
Fmt.cbSize = sizeof(Fmt);
Fmt.dwMask = CFM_COLOR;
Fmt.crTextColor = Fg;
SendMessage(Edit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&Fmt);
}
}
#define EXTRA_VIS 3
void
RicheditScrollToLine(HWND Edit, ULONG Line, ULONG VisLines)
{
CHARRANGE Sel;
ULONG CurLine;
ULONG VisAround;
ULONG TotalLines;
LONG Scroll;
//
// Scroll the given line into view. Try to keep
// the line from being the first or last line
// in view.
//
// Disable the window during this to prevent
// the default richedit scrolling from occurring.
//
VisAround = VisLines / 2;
if (VisAround > EXTRA_VIS)
{
VisAround = EXTRA_VIS;
}
TotalLines = (ULONG)SendMessage(Edit, EM_GETLINECOUNT, 0, 0);
CurLine = (ULONG)SendMessage(Edit, EM_GETFIRSTVISIBLELINE, 0, 0);
if (Line < CurLine + VisAround)
{
Scroll = (LONG)Line - (LONG)(CurLine + VisAround);
if ((ULONG)-Scroll > CurLine)
{
Scroll = -(LONG)CurLine;
}
}
else if (Line >= CurLine + VisLines - VisAround &&
CurLine + VisLines < TotalLines)
{
Scroll = (LONG)Line - (LONG)(CurLine + VisLines - VisAround) + 1;
}
else
{
Scroll = 0;
}
if (Scroll)
{
SendMessage(Edit, EM_LINESCROLL, 0, Scroll);
}
Sel.cpMax = Sel.cpMin = (LONG)
SendMessage(Edit, EM_LINEINDEX, Line, 0);
SendMessage(Edit, EM_EXSETSEL, 0, (LPARAM)&Sel);
}
ULONG
RicheditGetSelectionText(HWND Edit, PTSTR Buffer, ULONG BufferChars)
{
CHARRANGE Sel;
SendMessage(Edit, EM_EXGETSEL, 0, (LPARAM)&Sel);
if (Sel.cpMin >= Sel.cpMax)
{
return 0;
}
Sel.cpMax -= Sel.cpMin;
if ((ULONG)Sel.cpMax + 1 > BufferChars)
{
return 0;
}
SendMessage(Edit, EM_GETSELTEXT, 0, (LPARAM)Buffer);
return Sel.cpMax;
}
ULONG
RicheditGetSourceToken(HWND Edit, PTSTR Buffer, ULONG BufferChars,
CHARRANGE* Range)
{
LRESULT Idx;
TEXTRANGE GetRange;
CHARRANGE Sel;
//
// Get the text for the line containing the selection.
//
SendMessage(Edit, EM_EXGETSEL, 0, (LPARAM)&Sel);
if (Sel.cpMin > Sel.cpMax)
{
return 0;
}
if ((Idx = SendMessage(Edit, EM_LINEINDEX, -1, 0)) < 0)
{
return 0;
}
GetRange.chrg.cpMin = (LONG)Idx;
if (!(Idx = SendMessage(Edit, EM_LINELENGTH, GetRange.chrg.cpMin, 0)))
{
return 0;
}
if (BufferChars <= (ULONG)Idx)
{
Idx = (LONG)BufferChars - 1;
}
GetRange.chrg.cpMax = GetRange.chrg.cpMin + (LONG)Idx;
GetRange.lpstrText = Buffer;
if (!SendMessage(Edit, EM_GETTEXTRANGE, 0, (LPARAM)&GetRange))
{
return 0;
}
//
// Check and see if the selection is within a source token.
//
PTSTR Scan = Buffer + (Sel.cpMin - GetRange.chrg.cpMin);
if (!iscsym(*Scan))
{
return 0;
}
//
// Find the start of the token and validate it.
//
PTSTR Start = Scan;
if (Start > Buffer)
{
while (--Start >= Buffer && iscsym(*Start))
{
// Back up.
}
Start++;
}
if (!iscsymf(*Start))
{
return 0;
}
//
// Find the end of the token.
//
Scan++;
while (iscsym(*Scan))
{
Scan++;
}
ULONG Len;
// Chop the buffer down to just the token and return.
Len = (ULONG)(Scan - Start);
memmove(Buffer, Start, Len);
Buffer[Len] = 0;
Range->cpMin = GetRange.chrg.cpMin + (LONG)(Start - Buffer);
Range->cpMax = Range->cpMin + Len;
return Len;
}
#undef DEFINE_GET_WINDATA
#undef ASSERT_CLASS_TYPE
#ifndef DBG
#define ASSERT_CLASS_TYPE(p, ct) ((VOID)0)
#else
#define ASSERT_CLASS_TYPE(p, ct) if (p) { AssertType(*p, ct); }
#endif
#define DEFINE_GET_WINDATA(ClassType, FuncName) \
ClassType * \
Get##FuncName##WinData( \
HWND hwnd \
) \
{ \
ClassType *p = (ClassType *) \
GetWindowLongPtr(hwnd, GWLP_USERDATA); \
\
ASSERT_CLASS_TYPE(p, ClassType); \
\
return p; \
}
#include "fncdefs.h"
#undef DEFINE_GET_WINDATA
#undef ASSERT_CLASS_TYPE