/*++ Copyright (c) 1992-2001 Microsoft Corporation Module Name: dualwin.cpp Abstract: Header for new window architecture functions. --*/ #include "precomp.hxx" #pragma hdrstop #define NAME_BUFFER 1024 BOOL g_UseTextMode = FALSE; // // DUALLISTWIN_DATA methods // DUALLISTWIN_DATA::DUALLISTWIN_DATA(ULONG ChangeBy) : SINGLE_CHILDWIN_DATA(ChangeBy) { m_wFlags = DL_EDIT_SECONDPANE; m_hwndEditControl = NULL; m_nItem_LastSelected = -1; m_nSubItem_LastSelected = 0; m_nItem_CurrentlyEditing = -1; m_nSubItem_CurrentlyEditing = -1; } void DUALLISTWIN_DATA::Validate() { SINGLE_CHILDWIN_DATA::Validate(); } void DUALLISTWIN_DATA::SetFont(ULONG FontIndex) { SINGLE_CHILDWIN_DATA::SetFont(FontIndex); SendMessage(m_hwndEditControl, WM_SETFONT, (WPARAM)m_Font->Font, TRUE); } BOOL DUALLISTWIN_DATA::OnCreate(void) { m_hwndChild = CreateWindowEx( WS_EX_CLIENTEDGE, // Extended style WC_LISTVIEW, // class name NULL, // title WS_CHILD | WS_VISIBLE | WS_MAXIMIZE | WS_HSCROLL | WS_VSCROLL | LVS_SHOWSELALWAYS | LVS_REPORT | LVS_SINGLESEL | ((m_enumType != CPU_WINDOW) ? LVS_OWNERDRAWFIXED : 0), // style 0, // x 0, // y CW_USEDEFAULT, // width CW_USEDEFAULT, // height m_Win, // parent 0, // control id g_hInst, // hInstance NULL // user defined data ); if (m_hwndChild == NULL) { return FALSE; } m_hwndEditControl = CreateWindowEx( 0, // Extended style RICHEDIT_CLASS, // class name NULL, // title WS_CHILD, 0, // x 0, // y CW_USEDEFAULT, // width CW_USEDEFAULT, // height m_Win, // parent 0, // control id g_hInst, // hInstance NULL // user defined data ); if (m_hwndEditControl == NULL) { DestroyWindow(m_hwndChild); return FALSE; } SetFont(FONT_FIXED); SendMessage(m_hwndEditControl, EM_SETEVENTMASK, 0, (LPARAM) ENM_KEYEVENTS | ENM_MOUSEEVENTS ); return TRUE; } LRESULT DUALLISTWIN_DATA::OnCommand( WPARAM wParam, LPARAM lParam ) { WORD wCode = HIWORD(wParam); WORD wId = LOWORD(wParam); HWND hwnd = (HWND) lParam; if (hwnd != m_hwndEditControl) { return 1; // Not handled } switch (wCode) { default: return 1; // not handled case EN_KILLFOCUS: // // Duplicate code in OnNotify : EN_MSGFILTER // if (-1 != m_nItem_CurrentlyEditing) { if (!SetItemFromEdit(m_nItem_CurrentlyEditing, m_nSubItem_CurrentlyEditing)) { break; } ShowWindow(hwnd, SW_HIDE); SetFocus(m_hwndChild); InvalidateItem(m_nItem_CurrentlyEditing); m_nItem_CurrentlyEditing = -1; m_nSubItem_CurrentlyEditing = -1; } break; } return 0; } LRESULT DUALLISTWIN_DATA::OnNotify( WPARAM wParam, LPARAM lParam ) { // Branch depending on the specific notification message. switch (((LPNMHDR) lParam)->code) { case LVN_ITEMCHANGED: InvalidateItem( ((LPNMLISTVIEW) lParam)->iItem); break; case NM_CLICK: case NM_DBLCLK: // Figure out whether an item or a sub-item was clicked OnClick((LPNMLISTVIEW) lParam); break; case NM_CUSTOMDRAW: return OnCustomDraw((LPNMLVCUSTOMDRAW)lParam); case EN_MSGFILTER: // // Duplicate code in OnCommand : EN_KILLFOCUS // if (WM_KEYDOWN == ((MSGFILTER *)lParam)->msg) { MSGFILTER * pMsgFilter = (MSGFILTER *) lParam; switch (pMsgFilter->wParam) { case VK_RETURN: // Ignore this message so the richedit // doesn't beep. return 1; } } else if (WM_CHAR == ((MSGFILTER *)lParam)->msg) { MSGFILTER * pMsgFilter = (MSGFILTER *) lParam; switch (pMsgFilter->wParam) { case VK_RETURN: if (!SetItemFromEdit(m_nItem_CurrentlyEditing, m_nSubItem_CurrentlyEditing)) { break; } // fall through... case VK_ESCAPE: InvalidateItem(m_nItem_CurrentlyEditing); // // Invalidate these before changing focus, so that Itemchanged // doesn't get called again on same item // m_nItem_CurrentlyEditing = -1; m_nSubItem_CurrentlyEditing = -1; // // Hide the edit box and set focus to the list view // ShowWindow(m_hwndEditControl, SW_HIDE); SetFocus(m_hwndChild); break; } } else if (WM_RBUTTONDOWN == ((MSGFILTER *)lParam)->msg || WM_RBUTTONDBLCLK == ((MSGFILTER *)lParam)->msg) { // process cpoy/passte selection if (CanCopy()) { Copy(); CHARRANGE Sel; SendMessage(m_hwndEditControl, EM_EXGETSEL, 0, (LPARAM)&Sel); Sel.cpMax = Sel.cpMin; SendMessage(m_hwndEditControl, EM_EXSETSEL, 0, (LPARAM)&Sel); } else if (SendMessage(m_hwndEditControl, EM_CANPASTE, CF_TEXT, 0)) { SetFocus(m_hwndEditControl); Paste(); } // Ignore right-button events. return 1; } return 0; // process this message case LVN_COLUMNCLICK: // LVN_COLUMNCLICK pnmv = (LPNMLISTVIEW) lParam; break; } return DefMDIChildProc(m_Win, WM_NOTIFY, wParam, lParam); } BOOL DUALLISTWIN_DATA::ClearList(ULONG ClearFrom) { if (!ClearFrom) { return ListView_DeleteAllItems(m_hwndChild); } else { ULONG nItems = ListView_GetItemCount(m_hwndChild); BOOL res = TRUE; while (res && (ClearFrom < nItems)) { res = ListView_DeleteItem(m_hwndChild, --nItems); } return res; } } void DUALLISTWIN_DATA::InvalidateItem( int nItem ) { RECT rc = {0}; if (-1 == nItem) { // Invalidate the entire window GetClientRect(m_hwndChild, &rc); } else { // Invalidate the item row if (!ListView_GetItemRect(m_hwndChild,nItem,&rc,LVIR_BOUNDS)) { // Invalidate the entire window GetClientRect(m_hwndChild, &rc); } } InvalidateRect(m_hwndChild, &rc, TRUE); } void DUALLISTWIN_DATA::ItemChanged( int Item, PCSTR Text ) { // Do-nothing placeholder. } LRESULT DUALLISTWIN_DATA::OnCustomDraw(LPNMLVCUSTOMDRAW Custom) { static int s_SelectedItem = -1; static ULONG s_SubItem; switch (Custom->nmcd.dwDrawStage) { case CDDS_PREPAINT: s_SelectedItem = ListView_GetNextItem(m_hwndChild, -1, LVNI_SELECTED); if (m_wFlags & DL_CUSTOM_ITEMS) { return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT; } else { return CDRF_NOTIFYPOSTPAINT; } case CDDS_ITEMPREPAINT: s_SubItem = 0; return CDRF_NOTIFYSUBITEMDRAW; case CDDS_ITEMPREPAINT | CDDS_SUBITEM: return OnCustomItem(s_SubItem++, Custom); case CDDS_POSTPAINT: if (-1 != s_SelectedItem) { RECT rc; // If we ask for subitem 0, then we get the rectangle for the // entire item, so we ask for item 1, and do the math Dbg( ListView_GetSubItemRect(m_hwndChild, s_SelectedItem, 1, LVIR_BOUNDS, &rc)); if (0 == m_nSubItem_LastSelected) { rc.right = rc.left - 1; rc.left = 0; } InvertRect(Custom->nmcd.hdc, &rc); } return CDRF_NOTIFYPOSTPAINT; default: return 0; } } LRESULT DUALLISTWIN_DATA::OnCustomItem(ULONG SubItem, LPNMLVCUSTOMDRAW Custom) { return CDRF_DODEFAULT; } void DUALLISTWIN_DATA::OnClick( LPNMLISTVIEW Notify ) { LVHITTESTINFO lvHTInfo = {0}; lvHTInfo.pt = Notify->ptAction; if (-1 != ListView_SubItemHitTest(m_hwndChild, &lvHTInfo) ) { // success // // If the user click on a different item than the one currently selected // then the LVN_ITEMCHANGED message will take care of updating the screen. // // If the user clicked on the currently slected item then we need to // check whether he wants to edit the contents or select a different subitem. // if (m_nItem_CurrentlyEditing == lvHTInfo.iItem && m_nSubItem_CurrentlyEditing == lvHTInfo.iSubItem) { ShowWindow(m_hwndEditControl, SW_SHOW); SetFocus(m_hwndEditControl); } else if (m_nItem_LastSelected == lvHTInfo.iItem && m_nSubItem_LastSelected == lvHTInfo.iSubItem) { // If we clicked on the currently selected item & subitem // then the user wants to edit the text. // // Is editing allowed if ( ( (0 == m_nSubItem_LastSelected) && (m_wFlags & DL_EDIT_LEFTPANE) ) || ( (1 == m_nSubItem_LastSelected) && (m_wFlags & DL_EDIT_SECONDPANE) ) || ( (2 == m_nSubItem_LastSelected) && (m_wFlags & DL_EDIT_THIRDPANE) ) ) { m_nItem_CurrentlyEditing = m_nItem_LastSelected; m_nSubItem_CurrentlyEditing = m_nSubItem_LastSelected; EditText(); } } else { // User wants to select a different subitem m_nItem_LastSelected = lvHTInfo.iItem; m_nSubItem_LastSelected = lvHTInfo.iSubItem; InvalidateItem(lvHTInfo.iItem); } } } void DUALLISTWIN_DATA::EditText() { RECT rc; TCHAR sz[NAME_BUFFER * 10], *psz; LVITEM lvi = {0}; // Get the item's text ListView_GetItemText(m_hwndChild, m_nItem_CurrentlyEditing, m_nSubItem_CurrentlyEditing, sz, _tsizeof(sz) ); // If we ask for subitem 0, then we get the rectangle for the // entire item, so we ask for item m_nItem_CurrentlyEditing, and do the math // if we need subitem 0 Dbg( ListView_GetSubItemRect(m_hwndChild, m_nItem_CurrentlyEditing, (m_nSubItem_CurrentlyEditing ? m_nSubItem_CurrentlyEditing : 1), LVIR_BOUNDS, &rc)); psz = &sz[0]; if (0 == m_nSubItem_CurrentlyEditing) { rc.right = rc.left - 1; rc.left = 0; while (*psz && (*psz == ' ')) { ++psz; } } SetWindowText(m_hwndEditControl, psz); CHARRANGE charRange={0}; charRange.cpMax = strlen(psz); charRange.cpMin = 0; SendMessage(m_hwndEditControl, EM_EXSETSEL, (WPARAM) 0, (LPARAM) &charRange); SendMessage(m_hwndEditControl, EM_SETSEL, (WPARAM) 0, (LPARAM) -1); POINT ChildBase = {0, 0}; MapWindowPoints(m_hwndChild, m_Win, &ChildBase, 1); MoveWindow(m_hwndEditControl, rc.left + ChildBase.x, rc.top + ChildBase.y, rc.right - rc.left, rc.bottom - rc.top, FALSE); ShowWindow(m_hwndEditControl, SW_SHOW); SetFocus(m_hwndEditControl); } BOOL DUALLISTWIN_DATA::CanCopy() { HWND hwnd = (m_nItem_CurrentlyEditing == -1) ? m_hwndChild : m_hwndEditControl; CHARRANGE chrg; SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM) (CHARRANGE *) &chrg); return chrg.cpMin != chrg.cpMax; } BOOL DUALLISTWIN_DATA::CanCut() { return(m_nItem_CurrentlyEditing != -1) && CanCopy(); } BOOL DUALLISTWIN_DATA::CanPaste() { return(m_nItem_CurrentlyEditing != -1) && SendMessage(m_hwndEditControl, EM_CANPASTE, CF_TEXT, 0 ); } CHAR ListCopy[500]; void DUALLISTWIN_DATA::Copy() { if ((m_nItem_CurrentlyEditing == -1) && (m_nItem_LastSelected != -1)) { PCHAR sz = &ListCopy[0]; ZeroMemory(sz, sizeof(ListCopy)); if (!GlobalLock( (HGLOBAL) sz)) { return; } ListView_GetItemText(m_hwndChild, m_nItem_LastSelected, m_nSubItem_LastSelected, sz, sizeof(ListCopy)); CopyToClipboard(sz); } else { SendMessage(m_hwndEditControl, WM_COPY, 0, 0); } } void DUALLISTWIN_DATA::Cut() { SendMessage(m_hwndEditControl, WM_CUT, 0, 0); } void DUALLISTWIN_DATA::Paste() { SendMessage(m_hwndEditControl, WM_PASTE, 0, 0); } ULONG DUALLISTWIN_DATA::GetItemFlags(ULONG Item) { LVITEM LvItem; LvItem.mask = LVIF_PARAM; LvItem.iItem = Item; LvItem.iSubItem = 0; if (ListView_GetItem(m_hwndChild, &LvItem)) { return (ULONG)LvItem.lParam; } else { return 0; } } void DUALLISTWIN_DATA::SetItemFlags(ULONG Item, ULONG Flags) { LVITEM LvItem; LvItem.mask = LVIF_PARAM; LvItem.iItem = Item; LvItem.iSubItem = 0; LvItem.lParam = (LPARAM)Flags; ListView_SetItem(m_hwndChild, &LvItem); } BOOL DUALLISTWIN_DATA::SetItemFromEdit(ULONG Item, ULONG SubItem) { // // Save the text from the edit box to list item. // int nLen = GetWindowTextLength(m_hwndEditControl) + 1; PTSTR psz = (PTSTR)calloc( nLen, sizeof(TCHAR) ); if (psz == NULL) { return FALSE; } GetWindowText(m_hwndEditControl, psz, nLen); ListView_SetItemText(m_hwndChild, Item, SubItem, psz); SetItemFlags(Item, GetItemFlags(Item) | ITEM_CHANGED); ItemChanged(Item, psz); free(psz); return TRUE; } // // SYMWIN_DATA methods // HMENU SYMWIN_DATA::s_ContextMenu; SYMWIN_DATA::SYMWIN_DATA(IDebugSymbolGroup **pDbgSymbolGroup) : DUALLISTWIN_DATA(2048) { m_pWinSyms = NULL; m_nWinSyms = 0; m_pDbgSymbolGroup = pDbgSymbolGroup; m_NumSymsDisplayed = 0; m_DisplayTypes = FALSE; m_DisplayOffsets = FALSE; m_RefreshItem = 0; m_UpdateItem = -1; m_SplitWindowAtItem = 0; m_MaxNameWidth = 0; m_NumCols = 0; SetMaxSyms(1); // Use text for Accessibility readers SystemParametersInfo(SPI_GETSCREENREADER, 0, &g_UseTextMode, 0); } #define VALUE_COLM 1 #define TYPE_COLM 2 #define OFFSET_COLM (m_DisplayTypes ? 3 : 2) SYMWIN_DATA::~SYMWIN_DATA() { if (m_pWinSyms) { free (m_pWinSyms); } return; } void SYMWIN_DATA::Validate() { DUALLISTWIN_DATA::Validate(); } #define SYMWIN_CONTEXT_ID_BASE 0x100 #define SYMWIN_TBB_TYPECAST 0 #define SYMWIN_TBB_OFFSETS 1 TBBUTTON g_SymWinTbButtons[] = { TEXT_TB_BTN(SYMWIN_TBB_TYPECAST, "Typecast", BTNS_CHECK), TEXT_TB_BTN(SYMWIN_TBB_OFFSETS, "Offsets", BTNS_CHECK), SEP_TB_BTN(), TEXT_TB_BTN(ID_SHOW_TOOLBAR, "Toolbar", 0), }; #define NUM_SYMWIN_MENU_BUTTONS \ (sizeof(g_SymWinTbButtons) / sizeof(g_SymWinTbButtons[0])) #define NUM_SYMWIN_TB_BUTTONS \ (NUM_SYMWIN_MENU_BUTTONS - 2) #define IsAParent(Sym) ((Sym)->SubElements) #define SYM_LEVEL(sym) ((sym)->Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) #define IsRootSym(Sym) !SYM_LEVEL(Sym) HMENU SYMWIN_DATA::GetContextMenu(void) { // // We only keep one menu around for all call windows // so apply the menu check state for this particular // window. // CheckMenuItem(s_ContextMenu, SYMWIN_TBB_TYPECAST + SYMWIN_CONTEXT_ID_BASE, MF_BYCOMMAND | (m_DisplayTypes ? MF_CHECKED : 0)); CheckMenuItem(s_ContextMenu, SYMWIN_TBB_OFFSETS + SYMWIN_CONTEXT_ID_BASE, MF_BYCOMMAND | (m_DisplayOffsets ? MF_CHECKED : 0)); CheckMenuItem(s_ContextMenu, ID_SHOW_TOOLBAR + SYMWIN_CONTEXT_ID_BASE, MF_BYCOMMAND | (m_ShowToolbar ? MF_CHECKED : 0)); return s_ContextMenu; } void SYMWIN_DATA::OnContextMenuSelection(UINT Item) { ULONG Changed = 0; Item -= SYMWIN_CONTEXT_ID_BASE; switch(Item) { case SYMWIN_TBB_TYPECAST: SetDisplayTypes(Item, !m_DisplayTypes); Changed = 1 << SYMWIN_TBB_TYPECAST; break; case SYMWIN_TBB_OFFSETS: SetDisplayTypes(Item, !m_DisplayOffsets); Changed = 1 << SYMWIN_TBB_OFFSETS; break; case ID_SHOW_TOOLBAR: SetShowToolbar(!m_ShowToolbar); break; } SyncUiWithFlags(Changed); } BOOL SYMWIN_DATA::OnCreate(void) { if (s_ContextMenu == NULL) { s_ContextMenu = CreateContextMenuFromToolbarButtons (NUM_SYMWIN_MENU_BUTTONS, g_SymWinTbButtons, SYMWIN_CONTEXT_ID_BASE); if (s_ContextMenu == NULL) { return FALSE; } } if (!DUALLISTWIN_DATA::OnCreate()) { return FALSE; } m_Toolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_WRAPABLE | TBSTYLE_LIST | CCS_TOP, 0, 0, m_Size.cx, 0, m_Win, (HMENU)ID_TOOLBAR, g_hInst, NULL); if (m_Toolbar == NULL) { return FALSE; } SendMessage(m_Toolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SendMessage(m_Toolbar, TB_ADDBUTTONS, NUM_SYMWIN_TB_BUTTONS, (LPARAM)&g_SymWinTbButtons); SendMessage(m_Toolbar, TB_AUTOSIZE, 0, 0); RECT Rect; GetClientRect(m_Toolbar, &Rect); m_ToolbarHeight = Rect.bottom - Rect.top + GetSystemMetrics(SM_CYEDGE); m_ShowToolbar = TRUE; SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0); RECT rc; LV_COLUMN lvc = {0}; GetClientRect(m_hwndChild, &rc); rc.right -= rc.left + GetSystemMetrics(SM_CXVSCROLL); //initialize the columns lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT; lvc.fmt = LVCFMT_LEFT; lvc.iSubItem = 0; lvc.cx = m_Font->Metrics.tmAveCharWidth * 20; if (lvc.cx > rc.right / 2) { lvc.cx = rc.right / 2; } lvc.pszText = _T("Name"); Dbg( (0 == ListView_InsertColumn(m_hwndChild, 0, &lvc)) ); // Give the rest of the space to the value. lvc.cx = rc.right - lvc.cx; lvc.pszText = _T("Value"); Dbg( (1 == ListView_InsertColumn(m_hwndChild, 1, &lvc)) ); m_NumCols = 2; ListView_SetExtendedListViewStyle(m_hwndChild, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT ); SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndChild, NULL, TRUE); return TRUE; } void SYMWIN_DATA::OnSize(void) { DUALLISTWIN_DATA::OnSize(); // ListView_SetColumnWidth(m_hwndChild, 0 , m_MaxNameWidth); } LRESULT SYMWIN_DATA::OnNotify( WPARAM wParam, LPARAM lParam ) { if (((LPNMHDR) lParam)->code == LVN_KEYDOWN) { if (m_nItem_LastSelected != -1 && m_nItem_CurrentlyEditing == -1 && g_ExecStatus == DEBUG_STATUS_BREAK) { NMLVKEYDOWN * pNmKeyDown = (NMLVKEYDOWN *) lParam; switch (pNmKeyDown->wVKey) { case VK_LEFT: if (m_nSubItem_LastSelected == 0) { ExpandSymbol(m_nItem_LastSelected, FALSE); return TRUE; } break; case VK_RIGHT: if (m_nSubItem_LastSelected == 0) { ExpandSymbol(m_nItem_LastSelected, TRUE); return TRUE; } break; case VK_RETURN: switch (m_nSubItem_LastSelected) { case 0: if (IsRootSym(&m_pWinSyms[m_nItem_LastSelected]) && (m_enumType == WATCH_WINDOW)) { m_nItem_CurrentlyEditing = m_nItem_LastSelected; m_nSubItem_CurrentlyEditing=0; EditText(); } break; case 1: if (!(m_pWinSyms[m_nItem_LastSelected].Flags & DEBUG_SYMBOL_READ_ONLY)) { m_nItem_CurrentlyEditing = m_nItem_LastSelected; m_nSubItem_CurrentlyEditing=1; EditText(); } break; case 2: m_nItem_CurrentlyEditing = m_nItem_LastSelected; m_nSubItem_CurrentlyEditing=2; EditText(); break; default: break; } break; default: break; } } } else if (((LPNMHDR) lParam)->code == LVN_ITEMCHANGED) { LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam; if (pnmv->uNewState & LVIS_SELECTED) { m_nItem_LastSelected = pnmv->iItem; m_nSubItem_LastSelected = pnmv->iSubItem; } } return DUALLISTWIN_DATA::OnNotify(wParam, lParam); } void SYMWIN_DATA::OnUpdate( UpdateType Type ) { if (Type == UPDATE_EXEC) { // Disallow editing when the debuggee is running. if (g_ExecStatus == DEBUG_STATUS_BREAK) { if (m_enumType != LOCALS_WINDOW) { m_wFlags |= DL_EDIT_LEFTPANE; } else { m_wFlags &= ~DL_EDIT_LEFTPANE; } ListView_SetTextBkColor(m_hwndChild, GetSysColor(COLOR_WINDOW)); } else { m_wFlags = 0; ListView_SetTextBkColor(m_hwndChild, GetSysColor(COLOR_3DFACE)); } InvalidateRect(m_hwndChild, NULL, FALSE); return; } else if (Type != UPDATE_BUFFER) { return; } SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0); UpdateNames(); SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndChild, NULL, TRUE); } void SYMWIN_DATA::ExpandSymbol( ULONG Index, BOOL Expand ) { // // Expand the Item // UIC_SYMBOL_WIN_DATA* WatchItem; WatchItem = StartStructCommand(UIC_SYMBOL_WIN); if (WatchItem != NULL) { WatchItem->Type = EXPAND_SYMBOL; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; WatchItem->u.ExpandSymbol.Index = Index; WatchItem->u.ExpandSymbol.Expand = Expand; FinishCommand(); UiRequestRead(); } } ULONG SYMWIN_DATA::GetWorkspaceSize(void) { return DUALLISTWIN_DATA::GetWorkspaceSize() + 2*sizeof(BOOL); } PUCHAR SYMWIN_DATA::SetWorkspace(PUCHAR Data) { Data = DUALLISTWIN_DATA::SetWorkspace(Data); *(PBOOL)Data = m_DisplayTypes; Data += sizeof(BOOL); *(PBOOL)Data = m_DisplayOffsets; Data += sizeof(BOOL); return Data; } PUCHAR SYMWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End) { UIC_SYMBOL_WIN_DATA* WatchItem; Data = DUALLISTWIN_DATA::ApplyWorkspace1(Data, End); // Clear the window WatchItem = StartStructCommand(UIC_SYMBOL_WIN); if (WatchItem != NULL) { WatchItem->Type = DEL_SYMBOL_WIN_ALL; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; FinishCommand(); } ULONG Changed = 0; if (End - Data >= sizeof(BOOL)) { SetDisplayTypes(SYMWIN_TBB_TYPECAST, *(PBOOL)Data); Changed |= 1 << SYMWIN_TBB_TYPECAST; Data += sizeof(BOOL); } if (End - Data >= sizeof(BOOL)) { SetDisplayTypes(SYMWIN_TBB_OFFSETS, *(PBOOL)Data); Changed |= 1 << SYMWIN_TBB_OFFSETS; Data += sizeof(BOOL); } SyncUiWithFlags(Changed); return Data; } BOOL SYMWIN_DATA::AddListItem( ULONG iItem, PSTR ItemText, ULONG Level, BOOL HasChildren, BOOL Expanded) { HBITMAP hbmp; LVITEM LvItem = {0}; CHAR Name[NAME_BUFFER], OldName[NAME_BUFFER]; ULONG i; LvItem.mask = LVIF_TEXT | LVIF_INDENT; Name[0] = 0; ULONG NameUsed = strlen(Name); // HACK - Column qutosize doesn't take indent into account // while autosizing the column, so padd with spaces in front to make it work i=0; while (i<=(Level+2)) { Name[i++]=' '; } Name[i]=0; strcat(Name, ItemText); LvItem.pszText = Name; LvItem.iItem = iItem; LvItem.iIndent = Level + 1; if ((ULONG)ListView_GetItemCount(m_hwndChild) <= iItem) { ListView_InsertItem(m_hwndChild, &LvItem); return TRUE; } else { ListView_GetItemText(m_hwndChild, iItem, 0, OldName, sizeof(OldName)); ListView_SetItem(m_hwndChild, &LvItem); return (strcmp(Name, OldName) != 0); } } void SYMWIN_DATA::UpdateNames() { ULONG Sym, Items; PSTR Buf; HRESULT Status; PDEBUG_SYMBOL_PARAMETERS SymParams = GetSymParam(); BOOL NameChanged; HDC hDC = GetDC(m_hwndChild); TEXTMETRIC tm = {0}; Items = ListView_GetItemCount(m_hwndChild); GetTextMetrics(hDC, &tm); ReleaseDC(m_hwndChild, hDC); if (UiLockForRead() == S_OK) { Buf = (PSTR)GetDataBuffer(); ULONG Len, i; CHAR Name[NAME_BUFFER], Value[NAME_BUFFER], Type[NAME_BUFFER], Offset[20]; ULONG LastArgSym=-1, LastParent=0; Items = 0; if (m_UpdateItem < m_SplitWindowAtItem) { LastArgSym = m_UpdateItem; m_SplitWindowAtItem = 0; } for (Sym = m_UpdateItem; Sym < m_NumSymsDisplayed; Sym++) { PSTR EndTag, pName; PSTR NameSt, NameEn; PSTR OffSt, OffEn; if (Buf == NULL) { strcpy(Name, _T("Unknown")); strcpy(Value, _T("")); } else { // DebugPrint("SYM_WIN: Buffer left %s\n", Buf); Name[0] = 0; EndTag = strstr(Buf, DEBUG_OUTPUT_NAME_END); if (!EndTag) { break; } NameSt = Buf; NameEn = EndTag; Buf = EndTag + strlen(DEBUG_OUTPUT_NAME_END); EndTag = strstr(Buf, DEBUG_OUTPUT_VALUE_END); if (!EndTag) { break; } Len = (ULONG) (EndTag - Buf); if (Len >= sizeof(Value)) { Len = sizeof(Value) - 1; } memcpy(Value, Buf, Len); Value[Len] = 0; Buf = EndTag + strlen(DEBUG_OUTPUT_VALUE_END); if (m_DisplayOffsets) { EndTag = strstr(Buf, DEBUG_OUTPUT_OFFSET_END); if (!EndTag) { EndTag = Buf; } Len = (ULONG) (EndTag - Buf); if (Len >= sizeof(Offset)) { Len = sizeof(Offset) - 1; } memcpy(Offset, Buf, Len); Offset[Len] = 0; Buf = EndTag + strlen(DEBUG_OUTPUT_OFFSET_END); } Len = (ULONG) (NameEn - NameSt); if (Len >= sizeof(Name) - 10) { Len = sizeof(Name) - 11; } strncat(Name, NameSt, Len); if (m_DisplayTypes) { EndTag = strstr(Buf, DEBUG_OUTPUT_TYPE_END); if (!EndTag) { EndTag = Buf; } Len = (ULONG) (EndTag - Buf); if (Len >= sizeof(Type)) { Len = sizeof(Type) - 1; } memcpy(Type, Buf, Len); Type[Len] = 0; Buf = EndTag + strlen(DEBUG_OUTPUT_TYPE_END); } } if (GetMaxSyms() > Sym) { NameChanged = AddListItem(Sym, Name, SYM_LEVEL((SymParams + Sym)), SymParams[Sym].SubElements, SymParams[Sym].Flags & DEBUG_SYMBOL_EXPANDED); } else { break; } if (!(SymParams[Sym].Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK)) { LastParent = Sym; if ((SymParams[Sym].Flags & DEBUG_SYMBOL_IS_ARGUMENT) && (m_enumType == LOCALS_WINDOW)) { LastArgSym = Sym; } if ((LastParent > LastArgSym) && !m_SplitWindowAtItem) { m_SplitWindowAtItem = Sym; } } if (!NameChanged) { // Check if the value changed PCHAR OldValue = &Name[0]; ListView_GetItemText(m_hwndChild, Sym, VALUE_COLM, OldValue, sizeof(Name)); if (strcmp(OldValue, Value)) { // Value changed SymParams[Sym].Flags |= ITEM_VALUE_CHANGED; // ListView_SetTextColor(m_hwndChild, RGB(0xff, 0, 0)); } else { SymParams[Sym].Flags &= ~ITEM_VALUE_CHANGED; } } ListView_SetItemText(m_hwndChild, Sym, VALUE_COLM, Value); if (m_DisplayOffsets) { ListView_SetItemText(m_hwndChild, Sym, OFFSET_COLM, Offset); } if (m_DisplayTypes) { ListView_SetItemText(m_hwndChild, Sym, TYPE_COLM, Type); } if (Sym < sizeof(m_ListItemLines)) { m_ListItemLines[Sym] = 2; } } UnlockStateBuffer(this); } ClearList(m_NumSymsDisplayed); if (Items == 0 && (m_enumType == WATCH_WINDOW)) { // // add a dummy to enable adding new items // LVITEM LvItem = {0}; LvItem.mask = LVIF_TEXT | LVIF_INDENT; LvItem.pszText = ""; LvItem.iItem = m_NumSymsDisplayed; ListView_InsertItem(m_hwndChild, &LvItem); // ListView_SetItemText(m_hwndChild, m_NumSymsDisplayed, 1, "Dummy"); } m_MaxNameWidth = 0; m_UpdateItem = -1; } #define ALLOCATE_CHUNK 0x100 HRESULT SYMWIN_DATA::SetMaxSyms( ULONG nSyms ) { if (m_nWinSyms < nSyms) { m_nWinSyms = ALLOCATE_CHUNK * ((nSyms + ALLOCATE_CHUNK - 1 )/ ALLOCATE_CHUNK); m_pWinSyms = (DEBUG_SYMBOL_PARAMETERS *) realloc(m_pWinSyms, m_nWinSyms*sizeof(DEBUG_SYMBOL_PARAMETERS)); } if (m_pWinSyms) { return S_OK; } m_nWinSyms = 0; return E_OUTOFMEMORY; } LRESULT SYMWIN_DATA::OnCommand( WPARAM wParam, LPARAM lParam ) { if ((HWND) lParam == m_Toolbar) { OnContextMenuSelection(LOWORD(wParam) + SYMWIN_CONTEXT_ID_BASE); return 0; } return DUALLISTWIN_DATA::OnCommand(wParam, lParam); } void SYMWIN_DATA::OnClick( LPNMLISTVIEW Notify ) { LVHITTESTINFO lvHTInfo = {0}; RECT itemRect; ULONG item; lvHTInfo.pt = Notify->ptAction; if (-1 != ListView_SubItemHitTest(m_hwndChild, &lvHTInfo) && (m_NumSymsDisplayed > (ULONG) lvHTInfo.iItem) && (g_ExecStatus == DEBUG_STATUS_BREAK)) { PDEBUG_SYMBOL_PARAMETERS SymParams = GetSymParam(); item = lvHTInfo.iItem; if (ListView_GetItemRect(m_hwndChild, lvHTInfo.iItem, &itemRect, LVIR_BOUNDS)) { if (((int) SYM_LEVEL(&SymParams[item]) * m_IndentWidth < Notify->ptAction.x) && (Notify->ptAction.x < (int) (itemRect.left + m_IndentWidth * (2+SYM_LEVEL(&SymParams[item])))) && (lvHTInfo.iSubItem == 0) && (SymParams[item].SubElements)) { BOOL Expand = TRUE; if (SymParams[item].SubElements) { if ((m_NumSymsDisplayed > item + 1) && (SymParams[item+1].ParentSymbol == item) && ((SymParams[item+1].Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) == (SymParams[item].Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) + 1)) { // // Already expanded // Expand = FALSE; } ExpandSymbol(item, Expand); } m_RefreshItem = item; m_nItem_LastSelected = lvHTInfo.iItem; m_nSubItem_LastSelected = lvHTInfo.iSubItem; return; } } // // Check if ok to edit right pane // if (SymParams[lvHTInfo.iItem].Flags & DEBUG_SYMBOL_READ_ONLY) { m_wFlags &= ~DL_EDIT_SECONDPANE; } else { m_wFlags |= DL_EDIT_SECONDPANE; } // // Check if ok to edit left pane // if ((SymParams[lvHTInfo.iItem].Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) || (m_enumType != WATCH_WINDOW)) { m_wFlags &= ~DL_EDIT_LEFTPANE; } else { m_wFlags |= DL_EDIT_LEFTPANE; } if (m_DisplayTypes) { m_wFlags |= DL_EDIT_THIRDPANE; } else { m_wFlags &= ~DL_EDIT_THIRDPANE; } } else if ((m_enumType == WATCH_WINDOW) && (g_ExecStatus == DEBUG_STATUS_BREAK) && (m_NumSymsDisplayed == (ULONG) lvHTInfo.iItem)) { m_wFlags |= DL_EDIT_LEFTPANE; } // // Default processing // DUALLISTWIN_DATA::OnClick(Notify); } HRESULT SYMWIN_DATA::ReadState(void) { HRESULT Status; ULONG getSyms; if (m_pDbgSymbolGroup == NULL || *m_pDbgSymbolGroup == NULL) { return E_UNEXPECTED; } (*m_pDbgSymbolGroup)->GetNumberSymbols(&m_NumSymsDisplayed); if (m_NumSymsDisplayed < m_RefreshItem) { // numsyms changed since last click - might happen for locals m_RefreshItem = 0; } getSyms = m_NumSymsDisplayed - m_RefreshItem; if (m_NumSymsDisplayed > GetMaxSyms()) { if ((Status = SetMaxSyms(m_NumSymsDisplayed)) != S_OK) { m_NumSymsDisplayed = 0; return Status; } } (*m_pDbgSymbolGroup)->GetSymbolParameters(m_RefreshItem, getSyms, GetSymParam() + m_RefreshItem); Empty(); g_OutStateBuf.SetBuffer(this); if ((Status = g_OutStateBuf.Start(TRUE)) != S_OK) { return Status; } (*m_pDbgSymbolGroup)->OutputSymbols(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_OVERRIDE_MASK | DEBUG_OUTCTL_NOT_LOGGED, (m_DisplayOffsets ? 0 : DEBUG_OUTPUT_SYMBOLS_NO_OFFSETS) | (m_DisplayTypes ? 0 : DEBUG_OUTPUT_SYMBOLS_NO_TYPES), m_RefreshItem, m_NumSymsDisplayed - m_RefreshItem); Status = g_OutStateBuf.End(FALSE); m_UpdateItem = m_RefreshItem; m_RefreshItem = 0; return Status; } void SYMWIN_DATA::ItemChanged(int Item, PCSTR Text) { UIC_SYMBOL_WIN_DATA* WatchItem; if (m_nItem_CurrentlyEditing == -1) { return; } if (m_nSubItem_CurrentlyEditing == 0) { // // First delete, then add the item // if (Item < (int) GetMaxSyms() && Item < (int) m_NumSymsDisplayed) { // // See if this item can be changed or not - only root and dummy can be chnged // if ((GetSymParam())[Item].Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK) { UiRequestRead(); return; } } WatchItem = StartStructCommand(UIC_SYMBOL_WIN); if (WatchItem != NULL) { WatchItem->Type = DEL_SYMBOL_WIN_INDEX; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; WatchItem->u.DelIndex = Item; FinishCommand(); } else { // XXX drewb - Failure? } WatchItem = StartStructCommand(UIC_SYMBOL_WIN); while ((*Text == ' ' || *Text == '+' || *Text == '-') && *Text != '\0' ) Text++; if (WatchItem != NULL) { PCHAR End; WatchItem->Type = ADD_SYMBOL_WIN; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; strcpy(m_ChangedName, Text); End = strchr(m_ChangedName, ' '); if (End) *End = 0; WatchItem->u.Add.Name = m_ChangedName; WatchItem->u.Add.Index = Item; FinishCommand(); } if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } } else if (m_nSubItem_CurrentlyEditing == 1) { WatchItem = StartStructCommand(UIC_SYMBOL_WIN); if (WatchItem != NULL) { strcpy(m_ChangedName, Text); WatchItem->Type = EDIT_SYMBOL; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; WatchItem->u.WriteSymbol.Index = m_nItem_CurrentlyEditing; WatchItem->u.WriteSymbol.Value = m_ChangedName; FinishCommand(); } } else if (m_nSubItem_CurrentlyEditing == 2) { WatchItem = StartStructCommand(UIC_SYMBOL_WIN); if (WatchItem != NULL) { strcpy(m_ChangedName, Text); WatchItem->Type = EDIT_TYPE; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; WatchItem->u.OutputAsType.Index = m_nItem_CurrentlyEditing; WatchItem->u.OutputAsType.Type = m_ChangedName; FinishCommand(); } } UiRequestRead(); } void SYMWIN_DATA::SetDisplayTypes(LONG Id, BOOL Set) { if (Id == g_SymWinTbButtons[0].idCommand || Id == g_SymWinTbButtons[1].idCommand) { // // Add / remove a column // if (Id == g_SymWinTbButtons[0].idCommand) { m_wFlags |= DL_EDIT_THIRDPANE; if (Set == m_DisplayTypes) { return; } m_DisplayTypes = Set; } else { if (Set == m_DisplayOffsets) { return; } m_DisplayOffsets = Set; } if (g_Workspace != NULL) { g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS); } if (Id == g_SymWinTbButtons[1].idCommand) { //UiRequestRead(); //return; } if (Set) { LV_COLUMN lvc = {0}; int Col1Width; Col1Width = ListView_GetColumnWidth(m_hwndChild, VALUE_COLM); lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT; lvc.fmt = LVCFMT_LEFT; lvc.iSubItem = 0; if (Col1Width >= (m_Font->Metrics.tmAveCharWidth * 20)) { lvc.cx = max(Col1Width / 4, m_Font->Metrics.tmAveCharWidth * 10); Col1Width -= lvc.cx; } else { lvc.cx = Col1Width >> 1; Col1Width -= lvc.cx; } ListView_SetColumnWidth(m_hwndChild, VALUE_COLM, Col1Width); lvc.pszText = _T((Id == g_SymWinTbButtons[0].idCommand) ? "Type" : "Offset"); ListView_InsertColumn( m_hwndChild, ((Id == g_SymWinTbButtons[0].idCommand) ? TYPE_COLM : OFFSET_COLM), &lvc); m_NumCols++; } else { if (Id == g_SymWinTbButtons[0].idCommand) { m_wFlags &= ~DL_EDIT_THIRDPANE; } int Col2Width; int Col1Width; int ColToDel = ((Id == g_SymWinTbButtons[0].idCommand) ? TYPE_COLM : OFFSET_COLM); Col1Width = ListView_GetColumnWidth(m_hwndChild, VALUE_COLM); Col2Width = ListView_GetColumnWidth(m_hwndChild, ColToDel); ListView_DeleteColumn(m_hwndChild, ColToDel); ListView_SetColumnWidth(m_hwndChild, VALUE_COLM, Col1Width + Col2Width); m_NumCols--; } UiRequestRead(); } } //the basic rutine making the ... thing LPTSTR MakeShortString(HDC hDC, LPTSTR lpszLong, LONG nColumnLen, LONG nOffset, PULONG pActualLen ) { static const _TCHAR szThreeDots[]=_T("..."); SIZE strSz; int nStringLen=lstrlen(lpszLong); if(nStringLen==0 || (GetTextExtentPoint(hDC, lpszLong,nStringLen, &strSz), strSz.cx + nOffset < nColumnLen)) { *pActualLen = nStringLen ? strSz.cx : 0; return(lpszLong); } *pActualLen = strSz.cx; static _TCHAR szShort[1024]; if (nStringLen < sizeof(szShort) - 4) { lstrcpy(szShort,lpszLong); } else { nStringLen = sizeof(szShort) - 4; ZeroMemory(szShort, sizeof(szShort)); strncpy(szShort,lpszLong, nStringLen); } GetTextExtentPoint(hDC, szThreeDots, sizeof(szThreeDots), &strSz); int nAddLen = strSz.cx; for(int i=nStringLen-1; i > 0; i--) { szShort[i]=0; GetTextExtentPoint(hDC, szShort, i, &strSz); if(strSz.cx + nOffset + nAddLen < nColumnLen) break; } lstrcat(szShort,szThreeDots); return(szShort); } void DrawRectangle(HDC hDc, POINT pt, ULONG width) { POINT corners[5]; corners[0] = pt; corners[1].x = pt.x; corners[1].y = pt.y + width; corners[2].x = pt.x + width; corners[2].y = pt.y + width; corners[3].x = pt.x + width; corners[3].y = pt.y; corners[4] = pt; Polyline(hDc, &corners[0], 5); } void DrawHorizLine(HDC hDc, POINT start, ULONG length, ULONG thick) { POINT curr, pt = start; while (thick) { MoveToEx(hDc, pt.x, pt.y, &curr); LineTo(hDc, pt.x + length, pt.y); --thick; ++pt.y; } } void DrawPlus(HDC hDc, POINT topLeft, ULONG width) { if (g_UseTextMode) { TextOut(hDc, topLeft.x, topLeft.y - width/2, "+", 1); return; } POINT curr, pt = topLeft; DrawRectangle(hDc, pt, width); MoveToEx(hDc, pt.x, pt.y + width/2, &curr); LineTo(hDc, pt.x + width, pt.y + width/2); MoveToEx(hDc, pt.x + width/2, pt.y, &curr); LineTo(hDc, pt.x + width/2, pt.y + width); } void DrawMinus(HDC hDc, POINT topLeft, ULONG width) { if (g_UseTextMode) { TextOut(hDc, topLeft.x, topLeft.y - width/2, "-", 1); return; } POINT curr, pt = topLeft; DrawRectangle(hDc, pt, width); MoveToEx(hDc, pt.x, pt.y + width/2, &curr); LineTo(hDc, pt.x + width, pt.y + width/2); } void DrawIndentLevel(HDC hDc, ULONG Indent, RECT leftRect) { POINT curr, pt; ULONG i; INT width, height; pt.x = leftRect.left; pt.y = leftRect.top; width = leftRect.right - leftRect.left; height = leftRect.bottom - leftRect.top; for (i=0;i= m_NumSymsDisplayed) { return; } // derive the Width of +/- from tm GetTextMetrics(hDC, &tm); RectWidth = (tm.tmHeight * 2) / 3; level = m_pWinSyms[itemID].Flags & DEBUG_SYMBOL_EXPANSION_LEVEL_MASK; // Rectangle for One indent level RECT IndentRc; IndentRc = ItemRect; IndentRc.left = ItemRect.left + 2; IndentRc.right = IndentRc.left + RectWidth + 1; DrawIndentLevel(hDC, level, IndentRc); *pIndentOffset = level * (RectWidth+1) + NAME_LEFT_PAD; POINT pt; pt.x = ItemRect.left + *pIndentOffset - 4; pt.y = (ItemRect.top + ItemRect.bottom - RectWidth) / 2; if (m_pWinSyms[itemID].Flags & DEBUG_SYMBOL_EXPANDED) { DrawMinus(hDC, pt, RectWidth); } else if (m_pWinSyms[itemID].SubElements) { DrawPlus(hDC, pt, RectWidth); } m_IndentWidth = RectWidth+1; *pIndentOffset += RectWidth+1; } LRESULT SYMWIN_DATA::OnOwnerDraw(UINT uMsg, WPARAM wParam, LPARAM lParam) { LPDRAWITEMSTRUCT lpdis; TEXTMETRIC tm; ULONG IndentOffset = 0, ActualWidth; if (uMsg == WM_MEASUREITEM) { MEASUREITEMSTRUCT *lpmis = (MEASUREITEMSTRUCT * ) lParam; HDC hDC = GetDC(m_hwndChild); GetTextMetrics(hDC, &tm); lpmis->CtlType = ODT_LISTVIEW; lpmis->itemHeight = tm.tmHeight; lpmis->itemWidth = m_MaxNameWidth; ReleaseDC(m_hwndChild, hDC); return TRUE; } else { // Assert (uMsg == WM_DRAWITEM); } lpdis = (LPDRAWITEMSTRUCT) lParam; int y; TCHAR Buffer[NAME_BUFFER]; int Col; LPTSTR pszText=&Buffer[0]; // If there are no list box items, skip this message. if (lpdis->itemID == -1) { return FALSE; } switch (lpdis->itemAction) { case ODA_SELECT: case ODA_DRAWENTIRE: { HBRUSH hBrush; BOOL DelBrush = FALSE; DWORD dwOldTextColor, dwOldBkColor, TextColor; if (g_ExecStatus != DEBUG_STATUS_BREAK) { dwOldTextColor = ListView_GetTextColor(m_hwndChild); dwOldBkColor = ListView_GetBkColor(m_hwndChild); hBrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); DelBrush = TRUE; TextColor = dwOldTextColor = SetTextColor(lpdis->hDC, dwOldTextColor); dwOldBkColor = SetBkColor(lpdis->hDC, GetSysColor(COLOR_3DFACE)); } else if (lpdis->itemState & ODS_SELECTED) { hBrush = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT)); DelBrush = TRUE; dwOldTextColor = SetTextColor(lpdis->hDC, TextColor = GetSysColor(COLOR_HIGHLIGHTTEXT)); dwOldBkColor = SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT)); } else // item not selected { hBrush = (HBRUSH) CreateSolidBrush(GetSysColor(COLOR_WINDOW)); DelBrush = TRUE; dwOldTextColor = SetTextColor(lpdis->hDC, TextColor = GetSysColor(COLOR_WINDOWTEXT)); dwOldBkColor = SetBkColor(lpdis->hDC, GetSysColor(COLOR_WINDOW)); } if (hBrush != NULL) { FillRect(lpdis->hDC, (LPRECT)&lpdis->rcItem, hBrush); if (DelBrush) { DeleteObject(hBrush); } } // Display the text associated with the item. ListView_GetItemText(m_hwndChild, lpdis->itemID, 0, Buffer, sizeof(Buffer)); GetTextMetrics(lpdis->hDC, &tm); y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; RECT rc = lpdis->rcItem; if (m_SplitWindowAtItem && (lpdis->itemID == m_SplitWindowAtItem)) { POINT pt = {rc.left, rc.top-1}; DrawHorizLine(lpdis->hDC, pt, rc.right - rc.left, 2); rc.top ++; } LV_COLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH; RECT rc2; ListView_GetSubItemRect(m_hwndChild, lpdis->itemID, 1, LVIR_BOUNDS, &rc2); rc.right = rc2.left; DrawTreeItem(lpdis->hDC, lpdis->itemID, rc, &IndentOffset); while (*pszText && *pszText == ' ') { ++pszText; } pszText = MakeShortString(lpdis->hDC, pszText, rc.right-rc.left, 2 + IndentOffset, &ActualWidth); TextOut(lpdis->hDC, rc.left + IndentOffset, y, pszText, strlen(pszText)); if (m_MaxNameWidth < (2 + IndentOffset*3/2 + ActualWidth)) { m_MaxNameWidth = 2 + IndentOffset*3/2 + ActualWidth; } for (Col = 1; //ListView_GetColumn(m_hwndChild, Col, &lvc); Col < (int) m_NumCols; Col++) { ULONG SaveColor; if (!ListView_GetSubItemRect(m_hwndChild, lpdis->itemID, Col, LVIR_BOUNDS, &rc)) { // invalid coulmn break; } ListView_GetItemText(m_hwndChild, lpdis->itemID, Col, Buffer, sizeof(Buffer)); int nRetLen = strlen(Buffer); if (nRetLen == 0) { pszText=""; } else { pszText = MakeShortString(lpdis->hDC, Buffer, rc.right - rc.left, 2, &ActualWidth); } if ((Col == 1) && (m_NumSymsDisplayed > lpdis->itemID)) { if (m_pWinSyms[lpdis->itemID].Flags & ITEM_VALUE_CHANGED) { SetTextColor(lpdis->hDC, RGB(0xff,0,0)); } } TextOut(lpdis->hDC, rc.left + 2, y, pszText, strlen(pszText)); if (m_pWinSyms[lpdis->itemID].Flags & ITEM_VALUE_CHANGED) { SetTextColor(lpdis->hDC, TextColor); } } // restore text and back ground color of list box's selection SetTextColor(lpdis->hDC, dwOldTextColor); SetBkColor(lpdis->hDC, dwOldBkColor); return TRUE; } case ODA_FOCUS: // Do not process focus changes. The focus caret // (outline rectangle) indicates the selection. break; } return DefMDIChildProc(m_Win, WM_DRAWITEM, wParam, lParam); } void SYMWIN_DATA::SyncUiWithFlags(ULONG Changed) { if (Changed & (1 << SYMWIN_TBB_TYPECAST)) { SendMessage(m_Toolbar, TB_SETSTATE, SYMWIN_TBB_TYPECAST, TBSTATE_ENABLED | (m_DisplayTypes ? TBSTATE_CHECKED : 0)); } if (Changed & (1 << SYMWIN_TBB_OFFSETS)) { SendMessage(m_Toolbar, TB_SETSTATE, SYMWIN_TBB_OFFSETS, TBSTATE_ENABLED | (m_DisplayOffsets ? TBSTATE_CHECKED : 0)); } } // // WATCHWIN_DATA methods // extern IDebugSymbolGroup * g_pDbgWatchSymbolGroup; WATCHWIN_DATA::WATCHWIN_DATA() : SYMWIN_DATA(&g_pDbgWatchSymbolGroup) { m_wFlags = DL_EDIT_LEFTPANE; m_enumType = WATCH_WINDOW; } void WATCHWIN_DATA::Validate() { SYMWIN_DATA::Validate(); Assert(WATCH_WINDOW == m_enumType); } HRESULT WATCHWIN_DATA::ReadState(void) { m_pDbgSymbolGroup = &g_pDbgWatchSymbolGroup; return SYMWIN_DATA::ReadState(); } #define WATCH_WRKSPC_TAG 0x40404040 // // Workspace // 0 4 8 // WATCH_WRKSPC_TAG NUM syms [Null terminated names] // ULONG WATCHWIN_DATA::GetWorkspaceSize(void) { ULONG i; ULONG Size = 2*sizeof(ULONG); PDEBUG_SYMBOL_PARAMETERS SymParam; for (SymParam = GetSymParam(), i=0; i < m_NumSymsDisplayed; SymParam++, i++) { if (IsRootSym(SymParam)) { CHAR Text[500]={0}; ListView_GetItemText(m_hwndChild, i, 0, Text, sizeof(Text)); Size+= strlen(Text) + 1; } } return SYMWIN_DATA::GetWorkspaceSize() + Size; } PUCHAR WATCHWIN_DATA::SetWorkspace(PUCHAR Data) { Data = SYMWIN_DATA::SetWorkspace(Data); PULONG pNumSyms, watchWrkspc = (PULONG) Data; *watchWrkspc = WATCH_WRKSPC_TAG; pNumSyms = (PULONG) (Data + sizeof(ULONG)); *pNumSyms = 0; Data += 2 * sizeof(ULONG); ULONG i; PDEBUG_SYMBOL_PARAMETERS SymParam; for (SymParam = GetSymParam(), i=0; i < m_NumSymsDisplayed; SymParam++, i++) { if (IsRootSym(SymParam)) { CHAR Text[500]={0}, *pName=&Text[0]; ListView_GetItemText(m_hwndChild, i, 0, Text, sizeof(Text)); strcpy((PCHAR) Data, pName); Data+= strlen(pName) + 1; *pNumSyms = *pNumSyms + 1; } } return Data; } PUCHAR WATCHWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End) { Data = SYMWIN_DATA::ApplyWorkspace1(Data, End); if (*((PULONG) Data) == WATCH_WRKSPC_TAG && End >= Data + 2 * sizeof(ULONG)) { Data += sizeof(ULONG); ULONG NumSyms = *((PULONG) Data); ULONG i=0; Data += sizeof(ULONG); PCHAR Name = (PCHAR) Data, pCopyName = &m_ChangedName[0]; while ((Data < End) && (i= &m_ChangedName[sizeof(m_ChangedName) - 1]) { Data = End; break; } strcpy(pCopyName, Name); WatchItem = StartStructCommand(UIC_SYMBOL_WIN); if (WatchItem != NULL) { WatchItem->Type = ADD_SYMBOL_WIN; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; WatchItem->u.Add.Name = pCopyName; WatchItem->u.Add.Index = i; FinishCommand(); } ++i; pCopyName += strlen(pCopyName) + 1; while (*Data != 0) { ++Data; } ++Data; Name = (PCHAR) Data; } } return Data; } // // LOCALSWIN_DATA methods // extern IDebugSymbolGroup * g_pDbgLocalSymbolGroup; LOCALSWIN_DATA::LOCALSWIN_DATA() : SYMWIN_DATA(&g_pDbgLocalSymbolGroup) { m_enumType = LOCALS_WINDOW; } void LOCALSWIN_DATA::Validate() { DUALLISTWIN_DATA::Validate(); Assert(LOCALS_WINDOW == m_enumType); } LOCALSWIN_DATA::~LOCALSWIN_DATA() { } BOOL LOCALSWIN_DATA::OnCreate(void) { if (!SYMWIN_DATA::OnCreate()) { return FALSE; } // // Get the locals list from engine // UIC_SYMBOL_WIN_DATA* WatchItem; WatchItem = StartStructCommand(UIC_SYMBOL_WIN); if (WatchItem != NULL) { WatchItem->Type = ADD_SYMBOL_WIN; WatchItem->pSymbolGroup = m_pDbgSymbolGroup; WatchItem->u.Add.Name = "*"; WatchItem->u.Add.Index = 0; FinishCommand(); } else { // XXX drewb - Failure? } return TRUE; } HRESULT LOCALSWIN_DATA::ReadState(void) { HRESULT Hr; IDebugSymbolGroup *pLocalSymbolGroup; // // Get the new locals // if ((Hr = g_pDbgSymbols->GetScopeSymbolGroup(DEBUG_SCOPE_GROUP_LOCALS, g_pDbgLocalSymbolGroup, &pLocalSymbolGroup)) == E_NOTIMPL) { // Older engine version Hr = g_pDbgSymbols->GetScopeSymbolGroup(DEBUG_SCOPE_GROUP_ALL, g_pDbgLocalSymbolGroup, &pLocalSymbolGroup); } if (Hr == S_OK) { g_pDbgLocalSymbolGroup = pLocalSymbolGroup; m_pDbgSymbolGroup = &g_pDbgLocalSymbolGroup; } else { // // Keep the old values // return E_PENDING; } return SYMWIN_DATA::ReadState(); } // // CPUWIN_DATA methods // HMENU CPUWIN_DATA::s_ContextMenu; CPUWIN_DATA::CPUWIN_DATA() : DUALLISTWIN_DATA(1024) { m_wFlags |= DL_CUSTOM_ITEMS; m_enumType = CPU_WINDOW; m_NamesValid = FALSE; m_NewSession = TRUE; } void CPUWIN_DATA::Validate() { DUALLISTWIN_DATA::Validate(); Assert(CPU_WINDOW == m_enumType); } HRESULT CPUWIN_DATA::ReadState(void) { HRESULT Status; PDEBUG_VALUE OldVals; ULONG NumOld; PDEBUG_VALUE Vals; PDEBUG_VALUE Coerced; PULONG Types; PBOOL Changed; NumOld = m_DataUsed / (sizeof(DEBUG_VALUE) + sizeof(BOOL)); Empty(); // // Retrieve all register values and diff them. // Also keep space for a coercion type map and // temporary coerced values. // OldVals = (PDEBUG_VALUE) AddData(g_NumRegisters * (3 * sizeof(DEBUG_VALUE) + sizeof(ULONG) + sizeof(BOOL))); if (OldVals == NULL) { return E_OUTOFMEMORY; } Changed = (PBOOL)(OldVals + g_NumRegisters); Coerced = (PDEBUG_VALUE)(Changed + g_NumRegisters); Vals = Coerced + g_NumRegisters; Types = (PULONG)(Vals + g_NumRegisters); Status = g_pDbgRegisters->GetValues(g_NumRegisters, NULL, 0, Vals); if (Status != S_OK) { return Status; } ULONG i; // Coerce values into known types. // If it's an integer value coerce it to 64-bit. // If it's a float value coerce to 64-bit also, // which loses precision but has CRT support for // formatting. for (i = 0; i < g_NumRegisters; i++) { if (Vals[i].Type >= DEBUG_VALUE_INT8 && Vals[i].Type <= DEBUG_VALUE_INT64) { Types[i] = DEBUG_VALUE_INT64; } else if (Vals[i].Type >= DEBUG_VALUE_FLOAT32 && Vals[i].Type <= DEBUG_VALUE_FLOAT128) { Types[i] = DEBUG_VALUE_FLOAT64; } else if (Vals[i].Type == DEBUG_VALUE_VECTOR64 || Vals[i].Type == DEBUG_VALUE_VECTOR128) { Types[i] = Vals[i].Type; } else { // Unknown type. return E_INVALIDARG; } } if ((Status = g_pDbgControl-> CoerceValues(g_NumRegisters, Vals, Types, Coerced)) != S_OK) { return Status; } // Diff new values against the old. for (i = 0; i < g_NumRegisters; i++) { if (i < NumOld) { switch(Types[i]) { case DEBUG_VALUE_INT64: Changed[i] = Coerced[i].I64 != OldVals[i].I64; break; case DEBUG_VALUE_FLOAT64: Changed[i] = Coerced[i].F64 != OldVals[i].F64; break; case DEBUG_VALUE_VECTOR64: Changed[i] = memcmp(Coerced[i].RawBytes, OldVals[i].RawBytes, 8); break; case DEBUG_VALUE_VECTOR128: Changed[i] = memcmp(Coerced[i].RawBytes, OldVals[i].RawBytes, 16); break; } } else { Changed[i] = FALSE; } } // Copy new values into permanent storage area. memmove(OldVals, Coerced, g_NumRegisters * sizeof(*Vals)); // Trim off temporary information. RemoveTail(g_NumRegisters * (2 * sizeof(DEBUG_VALUE) + sizeof(ULONG))); return Status; } #define CPU_CONTEXT_ID_BASE 0x100 TBBUTTON g_CpuTbButtons[] = { TEXT_TB_BTN(ID_CUSTOMIZE, "Customize...", 0), SEP_TB_BTN(), TEXT_TB_BTN(ID_SHOW_TOOLBAR, "Toolbar", 0), }; #define NUM_CPU_MENU_BUTTONS \ (sizeof(g_CpuTbButtons) / sizeof(g_CpuTbButtons[0])) #define NUM_CPU_TB_BUTTONS \ (NUM_CPU_MENU_BUTTONS - 2) HMENU CPUWIN_DATA::GetContextMenu(void) { // // We only keep one menu around for all CPU windows // so apply the menu check state for this particular // window. // In reality there's only one CPU window anyway, // but this is a good example of how to handle // multi-instance windows. // CheckMenuItem(s_ContextMenu, ID_SHOW_TOOLBAR + CPU_CONTEXT_ID_BASE, MF_BYCOMMAND | (m_ShowToolbar ? MF_CHECKED : 0)); return s_ContextMenu; } void CPUWIN_DATA::OnContextMenuSelection(UINT Item) { Item -= CPU_CONTEXT_ID_BASE; switch(Item) { case ID_CUSTOMIZE: if (!m_NamesValid || (g_RegisterNamesBuffer->UiLockForRead() != S_OK)) { ErrorBox(NULL, 0, ERR_No_Register_Names); } else { StartDialog(IDD_DLG_REG_CUSTOMIZE, DlgProc_RegCustomize, (LPARAM)this); UnlockStateBuffer(g_RegisterNamesBuffer); } break; case ID_SHOW_TOOLBAR: SetShowToolbar(!m_ShowToolbar); break; } } BOOL CPUWIN_DATA::OnCreate(void) { if (s_ContextMenu == NULL) { s_ContextMenu = CreateContextMenuFromToolbarButtons (NUM_CPU_MENU_BUTTONS, g_CpuTbButtons, CPU_CONTEXT_ID_BASE); if (s_ContextMenu == NULL) { return FALSE; } } if (!DUALLISTWIN_DATA::OnCreate()) { return FALSE; } m_Toolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_WRAPABLE | TBSTYLE_LIST | CCS_TOP, 0, 0, m_Size.cx, 0, m_Win, (HMENU)ID_TOOLBAR, g_hInst, NULL); if (m_Toolbar == NULL) { return FALSE; } SendMessage(m_Toolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SendMessage(m_Toolbar, TB_ADDBUTTONS, NUM_CPU_TB_BUTTONS, (LPARAM)&g_CpuTbButtons); SendMessage(m_Toolbar, TB_AUTOSIZE, 0, 0); RECT Rect; GetClientRect(m_Toolbar, &Rect); m_ToolbarHeight = Rect.bottom - Rect.top + GetSystemMetrics(SM_CYEDGE); m_ShowToolbar = TRUE; SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0); RECT rc; LV_COLUMN lvc = {0}; GetClientRect(m_hwndChild, &rc); rc.right -= rc.left + GetSystemMetrics(SM_CXVSCROLL); //initialize the columns lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT; lvc.fmt = LVCFMT_LEFT; lvc.iSubItem = 0; // Keep the register name column narrow since most names are short. lvc.cx = m_Font->Metrics.tmAveCharWidth * 7; if (lvc.cx > rc.right / 2) { lvc.cx = rc.right / 2; } lvc.pszText = _T("Reg"); Dbg( (0 == ListView_InsertColumn(m_hwndChild, 0, &lvc)) ); // Give the rest of the space to the value. lvc.cx = rc.right - lvc.cx; lvc.pszText = _T("Value"); Dbg( (1 == ListView_InsertColumn(m_hwndChild, 1, &lvc)) ); ListView_SetExtendedListViewStyle(m_hwndChild, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT ); if (g_RegisterNamesBuffer->UiLockForRead() == S_OK) { UpdateNames((PSTR)g_RegisterNamesBuffer->GetDataBuffer()); m_NamesValid = TRUE; UnlockStateBuffer(g_RegisterNamesBuffer); } else { UpdateNames(NULL); } SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndChild, NULL, TRUE); return TRUE; } LRESULT CPUWIN_DATA::OnCommand( WPARAM wParam, LPARAM lParam ) { if ((HWND)lParam == m_Toolbar) { OnContextMenuSelection(LOWORD(wParam) + CPU_CONTEXT_ID_BASE); return 0; } return DUALLISTWIN_DATA::OnCommand(wParam, lParam); } void CPUWIN_DATA::OnSize(void) { DUALLISTWIN_DATA::OnSize(); // The register label column stays fixed in size so // resize the value column to fit the remaining space. ListView_SetColumnWidth(m_hwndChild, 1, LVSCW_AUTOSIZE_USEHEADER); } void CPUWIN_DATA::OnUpdate(UpdateType Type) { if (Type == UPDATE_EXEC) { // Disallow editing when the debuggee is running. if (g_ExecStatus == DEBUG_STATUS_BREAK) { m_wFlags |= DL_EDIT_SECONDPANE; ListView_SetTextBkColor(m_hwndChild, GetSysColor(COLOR_WINDOW)); } else { m_wFlags &= ~DL_EDIT_SECONDPANE; ListView_SetTextBkColor(m_hwndChild, GetSysColor(COLOR_3DFACE)); } InvalidateRect(m_hwndChild, NULL, FALSE); return; } else if (Type == UPDATE_START_SESSION || Type == UPDATE_END_SESSION) { m_NamesValid = FALSE; m_NewSession = Type == UPDATE_START_SESSION; return; } else if (Type != UPDATE_BUFFER) { return; } ULONG ul; TCHAR sz[256]; PTSTR Str; PDEBUG_VALUE Vals, Val; BOOL ChangeFlag; PBOOL Changed; HRESULT Status; SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0); if (!m_NamesValid && g_RegisterNamesBuffer->UiLockForRead() == S_OK) { UpdateNames((PSTR)g_RegisterNamesBuffer->GetDataBuffer()); m_NamesValid = TRUE; UnlockStateBuffer(g_RegisterNamesBuffer); } Status = UiLockForRead(); if (Status == S_OK) { Vals = (PDEBUG_VALUE)m_Data; Changed = (PBOOL)(Vals + g_NumRegisters); } for (ul = 0; ul < g_NumRegisters; ul++) { ChangeFlag = FALSE; if (Status == S_FALSE) { _tcscpy(sz, _T("Retrieving")); } else if (FAILED(Status)) { _tcscpy(sz, _T("Error")); } else { Val = Vals + MAP_REGISTER(ul); // If this is a new session consider everything // unchanged since comparisons may be against // values from the previous session. if (!m_NewSession) { ChangeFlag = Changed[MAP_REGISTER(ul)]; } // Buffer values are coerced into known types. switch(Val->Type) { case DEBUG_VALUE_INT64: CPFormatMemory(sz, sizeof(sz), (LPBYTE)&Val->I64, 64, fmtUInt, g_NumberRadix); break; case DEBUG_VALUE_FLOAT64: CPFormatMemory(sz, sizeof(sz), (LPBYTE)&Val->F64, 64, fmtFloat, 10); break; case DEBUG_VALUE_VECTOR64: // Assume they want it as v4i16. Str = sz; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VI16[3], 16, fmtUInt, g_NumberRadix); Str += strlen(Str); *Str++ = ':'; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VI16[2], 16, fmtUInt, g_NumberRadix); Str += strlen(Str); *Str++ = ':'; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VI16[1], 16, fmtUInt, g_NumberRadix); Str += strlen(Str); *Str++ = ':'; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VI16[0], 16, fmtUInt, g_NumberRadix); break; case DEBUG_VALUE_VECTOR128: // Assume they want it as v4f32. Str = sz; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VF32[3], 32, fmtFloat, 10); Str += strlen(Str); *Str++ = ':'; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VF32[2], 32, fmtFloat, 10); Str += strlen(Str); *Str++ = ':'; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VF32[1], 32, fmtFloat, 10); Str += strlen(Str); *Str++ = ':'; CPFormatMemory(Str, sizeof(sz) - (ULONG)(Str - sz), (LPBYTE)&Val->VF32[0], 32, fmtFloat, 10); break; default: Assert(FALSE); break; } } if (ChangeFlag) { SetItemFlags(ul, GetItemFlags(ul) | ITEM_CHANGED); } else { SetItemFlags(ul, GetItemFlags(ul) & ~ITEM_CHANGED); } ListView_SetItemText(m_hwndChild, ul, 1, sz); } if (Status == S_OK) { UnlockStateBuffer(this); } SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndChild, NULL, TRUE); m_NewSession = FALSE; } void CPUWIN_DATA::ItemChanged(int Item, PCSTR Text) { UIC_SET_REG_DATA* SetRegData; SetRegData = StartStructCommand(UIC_SET_REG); if (SetRegData != NULL) { SetRegData->Reg = MAP_REGISTER((ULONG)Item); // If the text contains a decimal point set it as // a float value, otherwise use an integer value. // XXX drewb - This should probably be more sophisticated, // perhaps by remembering the types of registers. if (strchr(Text, '.') != NULL) { SetRegData->Val.Type = DEBUG_VALUE_FLOAT64; if (sscanf(Text, "%lf", &SetRegData->Val.F64) != 1) { SetRegData->Val.F64 = 0.0; } } else { SetRegData->Val.Type = DEBUG_VALUE_INT64; switch(g_NumberRadix) { case 10: if (sscanf(Text, "%I64d", &SetRegData->Val.I64) != 1) { SetRegData->Val.I64 = 0; } break; default: if (sscanf(Text, "%I64x", &SetRegData->Val.I64) != 1) { SetRegData->Val.I64 = 0; } break; } // XXX drewb - What about IA64 NAT bits? SetRegData->Val.Nat = FALSE; } FinishCommand(); } } LRESULT CPUWIN_DATA::OnCustomItem(ULONG SubItem, LPNMLVCUSTOMDRAW Custom) { if (SubItem == 1) { // Check changed flag stored in lParam. if (Custom->nmcd.lItemlParam & ITEM_CHANGED) { Custom->clrText = RGB(0xff, 0, 0); } } return CDRF_NOTIFYSUBITEMDRAW; } void CPUWIN_DATA::UpdateNames(PSTR Buf) { ULONG ul; PSTR Name; LVITEM lvitem = {0}; if (Buf != NULL) { AssertStateBufferLocked(g_RegisterNamesBuffer); } ListView_DeleteAllItems(m_hwndChild); lvitem.mask = LVIF_TEXT; for (ul = 0; ul < g_NumRegisters; ul++) { if (Buf == NULL) { Name = _T("Unknown"); } else { ULONG MapIndex = MAP_REGISTER(ul); Name = Buf; while (MapIndex-- > 0) { Name += strlen(Name) + 1; } } lvitem.pszText = Name; lvitem.iItem = ul; ListView_InsertItem(m_hwndChild, &lvitem); } }