// File: roomlist.cpp #include "precomp.h" #include "resource.h" #include "ConfRoom.h" #include "RoomList.h" #include "RToolbar.h" #include "VidView.h" #include "RostInfo.h" #include "particip.h" static const int RL_NUM_ICON_COLUMNS = 0; /* C R O O M L I S T V I E W */ /*------------------------------------------------------------------------- %%Function: CRoomListView -------------------------------------------------------------------------*/ CRoomListView::CRoomListView(): // m_iSortColumn (0), m_lUserData(0), m_fSortAscending (TRUE) { DbgMsg(iZONE_OBJECTS, "Obj: %08X created CRoomListView", this); } CRoomListView::~CRoomListView() { ListView_DeleteAllItems(m_hWnd); if(IsWindow()) { DestroyWindow(); } DbgMsg(iZONE_OBJECTS, "Obj: %08X destroyed CRoomListView", this); } CParticipant * CRoomListView::GetParticipant(void) { LPARAM lParam = GetSelectedLParam(); return (CParticipant *) lParam; } ////////////////////////////////////////////////////////////////////////// // CRoomListView::CView VOID CRoomListView::Show(BOOL fVisible) { if (NULL != m_hWnd) { ::ShowWindow(m_hWnd, fVisible ? SW_SHOW : SW_HIDE); } } VOID CRoomListView::ShiftFocus(HWND hwndCur, BOOL fForward) { if ((NULL == hwndCur) && (NULL != m_hWnd)) { ::SetFocus(m_hWnd); } } VOID CRoomListView::Redraw(void) { if (NULL == m_hWnd) return; // Invalidate entire client area and force erase: ::InvalidateRect(m_hWnd, NULL, TRUE); // Force an immediate repaint: ::UpdateWindow(m_hWnd); } VOID CRoomListView::ForwardSysChangeMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) { // Pass all messages to the list view window: if (NULL != m_hWnd) { ::SendMessage(m_hWnd, uMsg, wParam, lParam); } } BOOL CRoomListView::Create(HWND hwndParent) { DBGENTRY(CRoomListView::FCreate); RECT rcPos; SetRect( &rcPos, 0, 0, 0, 0 ); DWORD dwStyle = WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_SINGLESEL | LVS_AUTOARRANGE | LVS_SHAREIMAGELISTS; DWORD dwExStyle = WS_EX_CLIENTEDGE; CWindowImpl::Create( hwndParent, rcPos, g_szEmpty, dwStyle, dwExStyle, ID_LISTVIEW ); if (NULL == m_hWnd) return FALSE; // initialize the list view window // Associate the image list with the list view ListView_SetImageList(m_hWnd, g_himlIconSmall, LVSIL_SMALL); // Now initialize the columns we will need // Initialize the LV_COLUMN structure // the mask specifies that the .fmt, .ex, width, and .subitem members // of the structure are valid, LV_COLUMN lvC; // List View Column structure ClearStruct(&lvC); TCHAR pszText[256]; // place to store some text lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvC.fmt = LVCFMT_LEFT; SIZE size; GetDesiredSize(&size); int iWidth = size.cx; // default width of the non-icon columns (-1 to prevent creating a scrollbar) int iIconColWidth = ::GetSystemMetrics(SM_CXSMICON) + 7; // 7 additional pixels int iTextColWidth = ((iWidth - (RL_NUM_ICON_COLUMNS * iIconColWidth)) / (NUM_COLUMNS - RL_NUM_ICON_COLUMNS)) - 1; lvC.pszText = pszText; // Add the columns. for (int index = 0; index < NUM_COLUMNS; index++) { HD_ITEM hdi; hdi.mask = HDI_IMAGE | HDI_FORMAT; hdi.fmt = HDF_IMAGE; lvC.iSubItem = index; switch (index) { case COLUMN_INDEX_NAME: default: { // These are text or text&icon columns hdi.iImage = -1; lvC.cx = iTextColWidth; break; } } LoadString(::GetInstanceHandle(), IDS_COLUMN_NAME + index, pszText, CCHMAX(pszText)); if (-1 != ListView_InsertColumn(m_hWnd, index, &lvC)) { if (-1 != hdi.iImage) { ASSERT(ListView_GetHeader(m_hWnd)); Header_SetItem(ListView_GetHeader(m_hWnd), index, &hdi); } } else { WARNING_OUT(("Could not insert column %d in list view", index)); } } ASSERT(ListView_GetHeader(m_hWnd)); Header_SetImageList(ListView_GetHeader(m_hWnd), g_himlIconSmall); // set the style to do drag and drop headers and full row select dwExStyle = ListView_GetExtendedListViewStyle(m_hWnd); dwExStyle |= (LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP); ListView_SetExtendedListViewStyle(m_hWnd, dwExStyle); return TRUE; } LRESULT CRoomListView::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { int cx = LOWORD(lParam); HWND hwndHeader = ListView_GetHeader(m_hWnd); if (NULL != hwndHeader) { // This check is to avoid a RIP in debug windows ListView_SetColumnWidth(m_hWnd, 0, cx); } bHandled = FALSE; return(0); } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: Add(int iPosition, LPARAM lParam) * * PURPOSE: Adds a item to the list view * ****************************************************************************/ BOOL CRoomListView::Add(int iPosition, CParticipant * pPart) { DebugEntry(CRoomListView::Add); BOOL bRet = FALSE; int nPrevCount = ListView_GetItemCount(m_hWnd); LV_ITEM lvI; // List view item structure // Fill in the LV_ITEM structure // The mask specifies the the .pszText, .iImage, .lParam and .state // members of the LV_ITEM structure are valid. lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; lvI.state = (0 == nPrevCount) ? LVIS_FOCUSED : 0; lvI.stateMask = LVIS_FOCUSED; lvI.iItem = iPosition; lvI.iSubItem = 0; // The parent window is responsible for storing the text. The List view // window will send a LVN_GETDISPINFO when it needs the text to display lvI.pszText = LPSTR_TEXTCALLBACK; lvI.cchTextMax = MAX_ITEMLEN; lvI.iImage = I_IMAGECALLBACK; lvI.lParam = (LPARAM) pPart; if (-1 != ListView_InsertItem(m_hWnd, &lvI)) { pPart->AddRef(); // the ListView keeps a reference on this // set the text for each of the columns for report view for (int iSubItem = 1; iSubItem < NUM_COLUMNS; iSubItem++) { ListView_SetItemText( m_hWnd, iPosition, iSubItem, LPSTR_TEXTCALLBACK); } ListView_SortItems( m_hWnd, CRoomListView::RoomListViewCompareProc, (LPARAM)(this)); } DebugExitBOOL(CRoomListView::Add, bRet); return bRet; } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: LParamToPos(LPARAM lParam) * * PURPOSE: Returns the position index associated with the lParam value * ****************************************************************************/ int CRoomListView::LParamToPos(LPARAM lParam) { // Note: retuns -1 on failure LV_FINDINFO lvF; lvF.flags = LVFI_PARAM; lvF.lParam = lParam; return ListView_FindItem(m_hWnd, -1, &lvF); } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: GetSelectedLParam() * * PURPOSE: Returns the lParam of the selected item or NULL (no sel.) * ****************************************************************************/ LPARAM CRoomListView::GetSelectedLParam() { LPARAM lRet = NULL; if (::GetFocus() == m_hWnd) { // The list view has the focus, so find the selected item (if any) int iItem = ListView_GetNextItem(m_hWnd, -1, LVNI_ALL | LVNI_SELECTED); if (-1 != iItem) { LV_ITEM lvi; lvi.iItem = iItem; lvi.iSubItem = 0; lvi.mask = LVIF_PARAM; if (ListView_GetItem(m_hWnd, &lvi)) { lRet = lvi.lParam; } } } return lRet; } /* R E M O V E */ /*------------------------------------------------------------------------- %%Function: Remove Removes an item from the list view -------------------------------------------------------------------------*/ VOID CRoomListView::Remove(LPARAM lParam) { DBGENTRY(CRoomListView::Remove); int iPosition = LParamToPos(lParam); if (-1 == iPosition) return; ListView_DeleteItem(m_hWnd, iPosition); } /* O N C H A N G E P A R T I C I P A N T */ /*------------------------------------------------------------------------- %%Function: OnChangeParticipant Updates the user's status information -------------------------------------------------------------------------*/ VOID CRoomListView::OnChangeParticipant(CParticipant * pPart, NM_MEMBER_NOTIFY uNotify) { DBGENTRY(CRoomListView::OnChangeParticipant); switch (uNotify) { case NM_MEMBER_ADDED: { // Add the new member to the start of the list Add(0, pPart); break; } case NM_MEMBER_REMOVED: { // Remove the member from the list Remove((LPARAM) pPart); break; } case NM_MEMBER_UPDATED: { int iPos = LParamToPos((LPARAM) pPart); if (-1 == iPos) return; ListView_RedrawItems(m_hWnd, iPos, iPos); ListView_SortItems(m_hWnd, CRoomListView::RoomListViewCompareProc, (LPARAM)(this)); break; } default: break; } } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: OnNotify(WPARAM, LPARAM) * * PURPOSE: Handles WM_NOTIFY messages sent to the list view * ****************************************************************************/ LRESULT CRoomListView::OnNotify(WPARAM wParam, LPARAM lParam) { LRESULT lRet = 0L; LV_DISPINFO *pLvdi = (LV_DISPINFO *)lParam; ASSERT(pLvdi); if (ID_LISTVIEW == wParam) { switch(pLvdi->hdr.code) { case NM_DBLCLK: case NM_RETURN: { if (0x8000 & ::GetKeyState(VK_MENU)) { ::PostMessage(::GetMainWindow(), WM_COMMAND, IDM_POPUP_PROPERTIES, 0); } break; } case NM_CLICK: case NM_RCLICK: { LV_HITTESTINFO lvhi; ::GetCursorPos(&(lvhi.pt)); ::MapWindowPoints(NULL, m_hWnd, &(lvhi.pt), 1); ListView_SubItemHitTest(m_hWnd, &lvhi); if (LVHT_ONITEMICON | lvhi.flags) { lRet = OnClick(&lvhi, (NM_CLICK == pLvdi->hdr.code)); } break; } case LVN_DELETEITEM: { NM_LISTVIEW * pnmv = (NM_LISTVIEW *) lParam; CParticipant * pPart = (CParticipant *) pnmv->lParam; ASSERT(pPart); if (pPart) { pPart->Release(); } break; } case LVN_GETDISPINFO: { GetDispInfo(pLvdi); break; } case LVN_COLUMNCLICK: { // The user clicked on one of the column headings - sort by // this column. TRACE_OUT(("CRoomListView::OnNotify called (NM_COLUMNCLICK)")); NM_LISTVIEW *pNm = (NM_LISTVIEW *)lParam; ASSERT(pNm); if (pNm->iSubItem == m_iSortColumn) { m_fSortAscending = !m_fSortAscending; } else { m_fSortAscending = TRUE; } // m_iSortColumn = pNm->iSubItem; ListView_SortItems( pNm->hdr.hwndFrom, CRoomListView::RoomListViewCompareProc, (LPARAM)(this)); break; } default: break; } } return lRet; } /* G E T D I S P I N F O */ /*------------------------------------------------------------------------- %%Function: GetDispInfo Get the display information for a participant -------------------------------------------------------------------------*/ VOID CRoomListView::GetDispInfo(LV_DISPINFO * pLvdi) { CParticipant * pPart = (CParticipant *) (pLvdi->item.lParam); if (NULL == pPart) return; // Get the text if (pLvdi->item.mask & LVIF_TEXT) { switch (pLvdi->item.iSubItem) { case COLUMN_INDEX_NAME: { lstrcpyn(pLvdi->item.pszText, pPart->GetPszName(), pLvdi->item.cchTextMax); break; } default: break; } } // Get the Image if (pLvdi->item.mask & LVIF_IMAGE) { switch (pLvdi->item.iSubItem) { case COLUMN_INDEX_NAME: { pLvdi->item.iImage = II_USER; break; } default: break; } } } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: LoadColumnInfo(RegEntry* pre) * * PURPOSE: Loads the column position info in the registry * ****************************************************************************/ BOOL CRoomListView::LoadSettings(RegEntry* pre) { DebugEntry(CRoomListView::LoadColumnInfo); // BUGBUG georgep: Using a fixed initial width for now; should take a max // of longest name and the window width ListView_SetColumnWidth(m_hWnd, 0, 300); DebugExitBOOL(CRoomListView::LoadColumnInfo, TRUE); return TRUE; } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: OnClick(LV_HITTESTINFO*, BOOL) * * PURPOSE: Handles creating a popup menu when clicking on some icons * ****************************************************************************/ /* O N C L I C K */ /*------------------------------------------------------------------------- %%Function: OnClick Handles creating a popup menu when clicking on some icons -------------------------------------------------------------------------*/ LRESULT CRoomListView::OnClick(LV_HITTESTINFO* plvhi, BOOL fLeftClick) { ASSERT(NULL != plvhi); // Select the item: ListView_SetItemState(m_hWnd, plvhi->iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); switch (plvhi->iSubItem) { case COLUMN_INDEX_NAME: { if (fLeftClick) return 0; // selection break; } default: return 0; } // convert back to screen coords ::MapWindowPoints(m_hWnd, NULL, &(plvhi->pt), 1); OnPopup(plvhi->pt); return 1; } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: OnPopup(POINT) * * PURPOSE: Handles popup menu * ****************************************************************************/ BOOL CRoomListView::OnPopup(POINT pt) { CParticipant * pPart = GetParticipant(); if (NULL == pPart) return FALSE; // Get the menu for the popup from the resource file. HMENU hMenu = LoadMenu(::GetInstanceHandle(), MAKEINTRESOURCE(IDR_USER_POPUP)); if (NULL == hMenu) return FALSE; // Get the first menu in it which we will use for the call to // TrackPopup(). This could also have been created on the fly using // CreatePopupMenu and then we could have used InsertMenu() or // AppendMenu. HMENU hMenuTrackPopup = GetSubMenu(hMenu, 0); ASSERT(NULL != hMenuTrackPopup); ::EnableMenuItem(hMenuTrackPopup, IDM_POPUP_SPEEDDIAL, pPart->FEnableCmdCreateSpeedDial() ? MF_ENABLED : MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, IDM_POPUP_ADDRESSBOOK, pPart->FEnableCmdCreateWabEntry() ? MF_ENABLED : MF_GRAYED); ::EnableMenuItem(hMenuTrackPopup, IDM_POPUP_EJECT, pPart->FEnableCmdEject() ? MF_ENABLED : MF_GRAYED); // GiveControl/CancelGiveControl() pPart->CalcControlCmd(hMenuTrackPopup); BOOL fShowMenu = TRUE; // Check to see if we have "special" coordinates that signify // that we entered here as a result of a keyboard click // instead of a mouse click - and if so, get some default coords if ((0xFFFF == pt.x) && (0xFFFF == pt.y)) { int iPos = LParamToPos((LPARAM)pPart); RECT rctIcon; if ((-1 == iPos) || (FALSE == ListView_GetItemRect( m_hWnd, iPos, &rctIcon, LVIR_ICON))) { fShowMenu = FALSE; } else { // Convert from client coords to screen coords ::MapWindowPoints(m_hWnd, NULL, (LPPOINT)&rctIcon, 2); pt.x = rctIcon.left + ((rctIcon.right - rctIcon.left) / 2); pt.y = rctIcon.top + ((rctIcon.bottom - rctIcon.top) / 2); } } if (fShowMenu) { HWND hwnd = ::GetMainWindow(); // Draw and track the "floating" popup int cmd = TrackPopupMenu( hMenuTrackPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL); // Get this again since we just ended a message loop pPart = GetParticipant(); if (0 != cmd && NULL != pPart) { pPart->OnCommand(hwnd, (WORD)cmd); } } // We are finished with the menu now, so destroy it DestroyMenu(hMenu); return TRUE; } /**************************************************************************** * * CLASS: CRoomListView * * MEMBER: Callback used for sorting the list view * ****************************************************************************/ int CALLBACK CRoomListView::RoomListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { DebugEntry(RoomListViewCompareProc); CParticipant * pPart1 = (CParticipant *)lParam1; CParticipant * pPart2 = (CParticipant *)lParam2; CRoomListView* prlv = (CRoomListView*) lParamSort; ASSERT(prlv); int iResult = 0; if (pPart1 && pPart2) { switch (prlv->m_iSortColumn) { case COLUMN_INDEX_NAME: { LPTSTR lpStr1 = pPart1->GetPszName(); LPTSTR lpStr2 = pPart2->GetPszName(); iResult = lstrcmpi(lpStr1, lpStr2); break; } default: { ERROR_OUT(("Sorting by unknown label")); break; } } } if (!prlv->m_fSortAscending) { iResult = -iResult; } DebugExitINT(RoomListViewCompareProc, iResult); return iResult; } VOID TileBltWatermark(UINT x, UINT y, UINT cx, UINT cy, UINT xOff, UINT yOff, HDC hdcDst, HDC hdcSrc, UINT cxWatermark, UINT cyWatermark) { DBGENTRY(CRoomListView::TileBltWatermark) int cxPart, cyPart; BOOL fxTile, fyTile; ReCheck: fxTile = ((xOff + cx) > cxWatermark); fyTile = ((yOff + cy) > cyWatermark); if (!fxTile && !fyTile) { // no tiling needed -- blt and leave BitBlt(hdcDst, x, y, cx, cy, hdcSrc, xOff, yOff, SRCCOPY); DBGEXIT(CRoomListView::TileBltWatermark) return; } if (!fxTile) { // vertically tile cyPart = cyWatermark - yOff; BitBlt(hdcDst, x, y, cx, cyPart, hdcSrc, xOff, yOff, SRCCOPY); y += cyPart; cy -= cyPart; yOff = 0; goto ReCheck; } if (!fyTile) { // horizontally tile cxPart = cxWatermark - xOff; BitBlt(hdcDst, x, y, cxPart, cy, hdcSrc, xOff, yOff, SRCCOPY); x += cxPart; cx -= cxPart; xOff = 0; goto ReCheck; } // tile both ways cyPart = cyWatermark - yOff; TileBltWatermark(x, y, cx, cyPart, xOff, yOff, hdcDst, hdcSrc, cxWatermark, cyWatermark); y += cyPart; cy -= cyPart; yOff = 0; goto ReCheck; } void CRoomListView::GetDesiredSize(SIZE *ppt) { // BUGBUG georgep: faking these numbers; should use something dependent on the font size ppt->cx = 200; ppt->cy = 100; } // a quick but simple function to convert the "unicode" characters // from the H.245 roster info struct to ascii int Unicode4ToAsciiOEM009(char *szIn, char *szOut) { while (*szIn) { *szOut = (*szIn) - 4; szIn++; szOut++; } *szOut = '\0'; return 0; }