/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 All rights reserved. Module Name: portslv.hxx Abstract: Ports List View header Author: Albert Ting (AlbertT) 17-Aug-1995 Steve Kiraly (SteveKi) 29-Mar-1996 Revision History: --*/ #include "precomp.hxx" #pragma hdrstop #include "portslv.hxx" #if DBG //#define DBG_PORTSINFO DBG_INFO #define DBG_PORTSINFO DBG_NONE #endif MSG_ERRMAP gaMsgErrMapPorts[] = { ERROR_NOT_SUPPORTED, IDS_ERR_PORT_NOT_IMPLEMENTED, ERROR_ALREADY_EXISTS, IDS_ERR_PORT_ALREADY_EXISTS, 0, 0 }; /******************************************************************** Ports List view class. ********************************************************************/ TPortsLV:: TPortsLV( VOID ) : _bSelectionState( TRUE ), _bSingleSelection( TRUE ), _bTwoColumnMode( FALSE ), _iSelectedItem( -1 ), _ColumnSortState( kPortHeaderMax ), _uCurrentColumn( 0 ), _bAllowSelectionChange( FALSE ), _bHideFaxPorts( FALSE ) { vCreatePortDataList(); } TPortsLV:: ~TPortsLV( VOID ) { vDestroyPortDataList(); } BOOL TPortsLV:: bReloadPorts( IN LPCTSTR pszServerName, IN BOOL bSelectNewPort ) /*++ Routine Description: Read in the remote ports and put them in the listview. If level 2 fails, we will try 1. Arguments: pszServerName - Pointer to server name. bSelectNewPort - Indicates if a new port is to be located and selected. TRUE select new port, FALSE do not located new port. Return Value: TRUE if ports list loaded, FALSE if error occurred. --*/ { TStatusB bStatus( DBG_WARN, ERROR_INSUFFICIENT_BUFFER, ERROR_INVALID_LEVEL ); DWORD cbPorts = 0; PPORT_INFO_2 pPorts = NULL; DWORD cPorts = 0; DWORD dwLevel = 2; // // Preserve the current check state. // TCHAR szPortList[kPortListMax]; vGetPortList( szPortList, COUNTOF( szPortList ) ); // // Enumerate the port starting at level 2. // bStatus DBGCHK = VDataRefresh::bEnumPortsMaxLevel( pszServerName, &dwLevel, (PVOID *)&pPorts, &cbPorts, &cPorts ); // // If the ports list cannot be enumerated fail with an error. // if( !bStatus ){ DBGMSG( DBG_WARN, ( "PortsLV.bReloadPorts: can't alloc %d %d\n", cbPorts, GetLastError( ))); return FALSE; } // // If option to select newly added port was selected. // TString strNewPort; if( bSelectNewPort ){ // // Located the newly added port. // bSelectNewPort = bLocateAddedPort( strNewPort, pPorts, cPorts, dwLevel ); if( bSelectNewPort ){ DBGMSG( DBG_TRACE, ("New port found " TSTR "\n", (LPCTSTR)strNewPort ) ); } } // // Get the printers // PRINTER_INFO_2 *pPrinterInfo2 = NULL; DWORD cPrinterInfo2 = 0; DWORD cbPrinterInfo2 = 0; DWORD dwFlags = PRINTER_ENUM_NAME; bStatus DBGCHK = VDataRefresh::bEnumPrinters( dwFlags, (LPTSTR)pszServerName, 2, (PVOID *)&pPrinterInfo2, &cbPrinterInfo2, &cPrinterInfo2 ); // // Delete current ports if they exist. // bStatus DBGCHK = ListView_DeleteAllItems( _hwndLV ); // // Clear the item count. // _cLVPorts = 0; // // Delete all the port data items. // vDestroyPortDataList(); TString strDescription; // // Add LPT?: ports // strDescription.bLoadString( ghInst, IDS_TEXT_PRINTERPORT ); vInsertPortsByMask( cPorts, pPorts, cPrinterInfo2, pPrinterInfo2, dwLevel, TEXT("lpt?:"), strDescription ); // // Add COM?: ports // strDescription.bLoadString( ghInst, IDS_TEXT_SERIALPORT ); vInsertPortsByMask( cPorts, pPorts, cPrinterInfo2, pPrinterInfo2, dwLevel, TEXT("com?:"), strDescription ); // // Add FILE: ports // strDescription.bLoadString( ghInst, IDS_TEXT_PRINTTOFILE ); vInsertPortsByMask( cPorts, pPorts, cPrinterInfo2, pPrinterInfo2, dwLevel, TEXT("file:"), strDescription ); // // Add all the rest // vInsertPortsByMask( cPorts, pPorts, cPrinterInfo2, pPrinterInfo2, dwLevel, NULL, NULL ); // // Restore the previous check state. // vCheckPorts( szPortList ); // // Select and check the newly added port. // if( bSelectNewPort ){ // // Check off other selected ports // INT iItem = -1; do { iItem = ListView_GetNextItem( _hwndLV, iItem, LVNI_SELECTED ); if( iItem != -1 ) { ListView_SetItemState( _hwndLV, iItem, 0, LVIS_SELECTED | LVIS_FOCUSED ); } } while( iItem != -1 ); // // New port is added select and scroll it into view. // vItemClicked( iSelectPort( strNewPort ) ); } // // This arrays of numbers represents the percentages of // each column width from the total LV width. The sum // of all numbers in the array should be equal to 100 // (100% = the total LV width) // static UINT arrColW2[] = { 50, 50 }; static UINT arrColW3[] = { 18, 35, 47 }; // // Adjust columns ... // if( !_bTwoColumnMode ) { vAdjustHeaderColumns( _hwndLV, COUNTOF(arrColW3), arrColW3 ); } else { vAdjustHeaderColumns( _hwndLV, COUNTOF(arrColW2), arrColW2 ); } // // Release the enum ports and enum printer memory. // FreeMem( pPorts ); FreeMem( pPrinterInfo2 ); return TRUE; } BOOL TPortsLV:: bLocateAddedPort( IN LPCTSTR pszServerName, IN TString &strNewPort ) /*++ Routine Description: Located the first port which is not in the port list view. Arguments: strPort - New port which has been added. Return Value: TRUE success, FALSE error occurred. --*/ { TStatusB bStatus; DWORD cbPorts = 0; PPORT_INFO_2 pPorts = NULL; DWORD cPorts = 0; DWORD dwLevel = 2; // // Enumerate the port starting at level 2. // bStatus DBGCHK = VDataRefresh::bEnumPortsMaxLevel( pszServerName, &dwLevel, (PVOID *)&pPorts, &cbPorts, &cPorts ); // // If the ports list cannot be enumerated fail with an error. // if( bStatus ) { // // Located the newly added port. // bStatus DBGCHK = bLocateAddedPort( strNewPort, pPorts, cPorts, dwLevel ); if( bStatus ) { DBGMSG( DBG_TRACE, ("New port found " TSTR "\n", (LPCTSTR)strNewPort ) ); } } // // Release the port memory. // FreeMem( pPorts ); return bStatus; } BOOL TPortsLV:: bLocateAddedPort( IN OUT TString &strPort, IN PPORT_INFO_2 pPorts, IN DWORD cPorts, IN DWORD dwLevel ) /*++ Routine Description: Located the first port which is not in the port list view. Arguments: strPort - New port which has been added. pPorts - Points to a ports enum structure array. cPorts - Number of elements in the ports enum array. dwLevel - Level of the ports enum structure array. Return Value: TRUE success, FALSE error occurred. --*/ { TStatusB bStatus; LPTSTR pszPort; bStatus DBGNOCHK = FALSE; // // Go through all the ports. // for( UINT i=0; i<cPorts; ++i ){ if( dwLevel == 2 ){ // // If we are to hide the fax ports then // ignore newly added fax ports. // if( _bHideFaxPorts && bIsFaxPort( pPorts[i].pPortName, pPorts[i].pMonitorName ) ) { DBGMSG( DBG_TRACE, ( "PortsLV.bLocateAddedPort: fax device being skipped.\n" ) ); continue; } pszPort = pPorts[i].pPortName; } else { pszPort = ((PPORT_INFO_1)pPorts)[i].pName; } // // Look for a portname which is not in the list view. // if( iFindPort( pszPort ) < 0 ){ // // Update the passed in string object. // bStatus DBGCHK = strPort.bUpdate( pszPort ); break; } } return bStatus; } VOID TPortsLV:: vSelectPort( IN LPCTSTR pszPort ) { SPLASSERT( pszPort ); iSelectPort( pszPort ); } VOID TPortsLV:: vEnable( IN BOOL bRetainSelection ) { if( bRetainSelection ) { if( _iSelectedItem != -1 ) { ListView_SetItemState( _hwndLV, _iSelectedItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); ListView_EnsureVisible( _hwndLV, _iSelectedItem, FALSE ); } } EnableWindow( _hwndLV, TRUE ); } VOID TPortsLV:: vDisable( IN BOOL bRetainSelection ) { if( bRetainSelection ) { _iSelectedItem = ListView_GetNextItem( _hwndLV, -1, LVNI_SELECTED ); if( _iSelectedItem != -1 ) { ListView_SetItemState( _hwndLV, _iSelectedItem, 0, LVIS_SELECTED | LVIS_FOCUSED ); } } EnableWindow( _hwndLV, FALSE ); } VOID TPortsLV:: vCheckPorts( IN OUT LPTSTR pszPortString CHANGE ) /*++ Routine Description: Set the ports in the listview based on the printers port string. Arguments: pszPortName - List of ports, comma delimited. When this is returns, pszPortName is the same on entry, but it's modified inside this function. Return Value: --*/ { // // We will walk though the port string, replacing commas with // NULLs, but we'll change them back. // LPTSTR psz = pszPortString; SPLASSERT( psz ); LPTSTR pszPort; INT iItem; INT iItemFirst = kMaxInt; while( psz && *psz ){ pszPort = psz; psz = _tcschr( psz, TEXT( ',' )); if( psz ){ *psz = 0; } iItem = iCheckPort( pszPort ); if( iItem == -1 ){ DBGMSG( DBG_WARN, ( "PortsLV.vCheckPort: Port "TSTR" not checked.\n", pszPort )); } if( iItem < iItemFirst ){ iItemFirst = iItem; } if( psz ){ *psz = TEXT( ',' ); ++psz; } } if( iItemFirst == kMaxInt ){ DBGMSG( DBG_PORTSINFO, ( "PortsLV.vCheckPorts: No ports selected.\n" )); iItemFirst = 0; } // // Select the first item and make sure it is visible. // ListView_SetItemState( _hwndLV, iItemFirst, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); ListView_EnsureVisible( _hwndLV, iItemFirst, FALSE ); } VOID TPortsLV:: vSelectItem( INT iItem ) /*++ Routine Description: Selects an item in the ListView. Arguments: iItem - Index of item to select. Return Value: --*/ { ListView_SetItemState( _hwndLV, iItem, LVIS_SELECTED, LVIS_SELECTED ); } BOOL TPortsLV:: bSetUI( IN HWND hwndLV, IN BOOL bTwoColumnMode, IN BOOL bSelectionState, IN BOOL bAllowSelectionChange, IN HWND hwnd, IN WPARAM wmDoubleClickMsg, IN WPARAM wmSingleClickMsg, IN WPARAM wmDeleteKeyMsg ) { _hwndLV = hwndLV; _bTwoColumnMode = bTwoColumnMode; _bSelectionState = bSelectionState; _bAllowSelectionChange = bAllowSelectionChange; _wmDoubleClickMsg = wmDoubleClickMsg; _wmSingleClickMsg = wmSingleClickMsg; _wmDeleteKeyMsg = wmDeleteKeyMsg; _hwnd = hwnd; SPLASSERT( _hwndLV ); if( _bSelectionState ){ // // Add check boxes. // HIMAGELIST himlState = ImageList_Create( 16, 16, TRUE, 2, 0 ); // // !! LATER !! // Should be created once then shared. // if( !himlState ){ DBGMSG( DBG_ERROR, ( "PortsLV.bSetUI: ImageList_Create failed %d\n", GetLastError( ))); return FALSE; } // // Load the bitmap for the check states. // HBITMAP hbm = LoadBitmap( ghInst, MAKEINTRESOURCE( IDB_CHECKSTATES )); if( !hbm ){ DBGMSG( DBG_ERROR, ( "PortsLV.bSetUI: LoadBitmap failed %d\n", GetLastError( ))); return FALSE; } // // Add the bitmaps to the image list. // ImageList_AddMasked( himlState, hbm, RGB( 255, 0, 0 )); SendMessage( _hwndLV, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)himlState ); DeleteObject( hbm ); } INT iNumColumns = _bTwoColumnMode ? 2 : kPortHeaderMax; // // Initialize the LV_COLUMN structure. // LV_COLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvc.fmt = LVCFMT_LEFT; lvc.cx = kPortHeaderWidthDefault; RECT rc; if( !GetClientRect( _hwndLV, &rc )){ DBGMSG( DBG_WARN, ( "PortsLV.bSetUI: GetClientRect failed %d\n", GetLastError( ))); return FALSE; } // // Calculate the column width, less the scroll bar width. // lvc.cx = ( ( rc.right - rc.left ) - GetSystemMetrics( SM_CYVSCROLL ) ) / iNumColumns; TStatusB bStatus; TString strHeader; for( INT iCol = 0; iCol < iNumColumns; ++iCol ){ bStatus DBGCHK = strHeader.bLoadString( ghInst, IDS_PHEAD_BEGIN + iCol ); lvc.pszText = (LPTSTR)(LPCTSTR)strHeader; lvc.iSubItem = iCol; if( ListView_InsertColumn( _hwndLV, iCol, &lvc ) == -1 ){ DBGMSG( DBG_WARN, ( "PortsLV.bSetUI: LV_Insert failed %d\n", GetLastError( ))); return FALSE; } } // // Enable full row selection. // DWORD dwExStyle = ListView_GetExtendedListViewStyle( _hwndLV ); ListView_SetExtendedListViewStyle( _hwndLV, dwExStyle | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP ); // // !!LATER!! // We should read an override flag from the registry. // _bHideFaxPorts = TRUE; return TRUE; } VOID TPortsLV:: vAddPortToListView( IN LPCTSTR pszName, IN LPCTSTR pszMonitor, IN LPCTSTR pszDescription, IN LPCTSTR pszPrinters ) { if( !pszName || !pszName[0] ){ DBGMSG( DBG_WARN, ( "PortsLV.vAddPortToListView: pszName "TSTR" invalid\n", DBGSTR( pszName ))); return; } // // If we are to hide the fax ports and this is a fax // port then just return with out adding the port to // the list view. // if( _bHideFaxPorts && bIsFaxPort( pszName, pszMonitor ) ) { DBGMSG( DBG_TRACE, ( "PortsLV.vAddPortToListView: fax device being removed.\n" ) ); return; } // // Add this port to the port data list. // TPortData *pPortData = AddPortDataList( pszName, pszMonitor, pszDescription, pszPrinters ); LV_ITEM lvi; if( _bSelectionState ){ lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_PARAM; } else { lvi.mask = LVIF_TEXT | LVIF_PARAM; } lvi.state = kStateUnchecked; lvi.pszText = (LPTSTR)pszName; lvi.iItem = _cLVPorts; lvi.iSubItem = 0; lvi.lParam = (LPARAM)pPortData; ListView_InsertItem( _hwndLV, &lvi ); ListView_SetItemText( _hwndLV, _cLVPorts, 1, (LPTSTR)pszDescription ); if( !_bTwoColumnMode ) { ListView_SetItemText( _hwndLV, _cLVPorts, 2, (LPTSTR)pszPrinters ); } ++_cLVPorts; } VOID TPortsLV:: vDeletePortFromListView( IN LPCTSTR pszName ) { // // Locate the port in the list view. // INT iItem = iFindPort ( pszName ); if( iItem != -1 ){ // // Delete this port from the port data list. // DeletePortDataList( pszName ); ListView_DeleteItem( _hwndLV, iItem ); // // Select next item. If the item we just deleted is the last item, // we need to select the previous one. // // If we deleted the last item, leave it as is. // if( ListView_GetItemCount( _hwndLV ) == iItem && iItem > 0 ) { --iItem; } ListView_SetItemState( _hwndLV, iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); } } INT TPortsLV:: iFindPort( IN LPCTSTR pszPort ) /*++ Routine Description: Located the specified port name in the list view. Arguments: pszPort - Port name to locate. Return Value: iItem id if found, -1 if item was not found. --*/ { SPLASSERT( pszPort ); LV_FINDINFO lvfi; lvfi.flags = LVFI_STRING; lvfi.psz = pszPort; INT iItem = ListView_FindItem( _hwndLV, -1, &lvfi ); if( iItem == -1 ){ DBGMSG( DBG_WARN, ( "PortsLV.iFindPort: port "TSTR" not found\n", pszPort )); } return iItem; } INT TPortsLV:: iCheckPort( IN LPCTSTR pszPort ) /*++ Routine Description: Places the check mark next to a port in the list view. Arguments: pszPort - Port to check. Return Value: iItem checked, -1 == error. --*/ { // // Locate the port in the list view. // INT iItem = iFindPort ( pszPort ); if( iItem != -1 ){ // // Set the item selection state. // ListView_SetItemState( _hwndLV, iItem, kStateChecked, kStateMask ); // // Try and make as many ports visible as possible. // ListView_EnsureVisible( _hwndLV, iItem, FALSE ); } return iItem; } INT TPortsLV:: iSelectPort( IN LPCTSTR pszPort ) /*++ Routine Description: Select the port in the list view. Arguments: pszPort - Port to check. Return Value: iItem checked, -1 == error. --*/ { // // Locate the port in the list view. // INT iItem = iFindPort ( pszPort ); if( iItem != -1 ){ // // Select the port specified by pszPort. // ListView_SetItemState( _hwndLV, iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); // // Try and make as many ports visible as possible. // ListView_EnsureVisible( _hwndLV, iItem, FALSE ); } return iItem; } VOID TPortsLV:: vGetPortList( OUT LPTSTR pszPortList, IN COUNT cchPortList ) { INT cPorts = 0; DWORD i; LV_ITEM lvi; LPTSTR pszPort = pszPortList; DWORD cchSpaceLeft = cchPortList - 1; DWORD cchLen; lvi.iSubItem = 0; DWORD cItems = ListView_GetItemCount( _hwndLV ); for( pszPortList[0] = 0, i=0; i<cItems; ++i ){ if( ListView_GetItemState( _hwndLV, i, kStateMask ) & kStateChecked ){ lvi.pszText = pszPort; lvi.cchTextMax = cchSpaceLeft; cchLen = (DWORD)SendMessage( _hwndLV, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)&lvi ); if( cchLen + 1 > cchSpaceLeft ){ DBGMSG( DBG_WARN, ( "PortsLV.iGetPorts: Out of string space!\n" )); return; } pszPort += cchLen; cchSpaceLeft -= cchLen+1; *pszPort = TEXT( ',' ); ++pszPort; ++cPorts; } } // // If we had any ports, back up to remove the last comma. // if( cPorts ){ --pszPort; } // // Null terminate. // *pszPort = 0; } BOOL TPortsLV:: bReadUI( OUT TString &strPortString, IN BOOL bSelectedPort ) { TCHAR szPortList[kPortListMax]; szPortList[0] = 0; // // If we are in single select mode just return // the selected port. // if( bSelectedPort ) { (VOID)bGetSelectedPort( szPortList, COUNTOF( szPortList ) ); } else { // // Get the list of check ports from the list view. // vGetPortList( szPortList, COUNTOF( szPortList ) ); } // // Update the port list. // return strPortString.bUpdate( szPortList ); } VOID TPortsLV:: vItemClicked( IN INT iItem ) /*++ Routine Description: User clicked in listview. Check if item state should be changed. The item will also be selected. Arguments: iItem - Item that has been clicked. Return Value: --*/ { if( iItem == -1 ){ DBGMSG( DBG_WARN, ( "PortsLV.vItemClicked: -1 passed in\n" )); return; } // // If in single selection mode clear all items checked and only // check the specified item. // if( _bSingleSelection ){ DWORD cItems = ListView_GetItemCount( _hwndLV ); for( UINT i=0; i<cItems; ++i ){ if( ListView_GetItemState( _hwndLV, i, kStateMask ) & kStateChecked ){ if( iItem == (INT)i ){ continue; } else { ListView_SetItemState( _hwndLV, i, kStateUnchecked, kStateMask ); } } } } // // Retrieve the old state, toggle it, then set it. // DWORD dwState = ListView_GetItemState( _hwndLV, iItem, kStateMask ); // // When we are in single select mode we want to always check // the currently selected item. // if( !_bSingleSelection ) { dwState = ( dwState == kStateChecked ) ? kStateUnchecked | LVIS_SELECTED | LVIS_FOCUSED : kStateChecked | LVIS_SELECTED | LVIS_FOCUSED; } else { dwState = kStateChecked | LVIS_SELECTED | LVIS_FOCUSED; } // // Set the new item state. // ListView_SetItemState( _hwndLV, iItem, dwState, kStateMask | LVIS_SELECTED | LVIS_FOCUSED ); } COUNT TPortsLV:: cSelectedPorts( VOID ) /*++ Routine Description: Returns the number of items which have a check mark next to them. Arguments: None. Return Value: Return the number of checked items. --*/ { DWORD cItems = ListView_GetItemCount( _hwndLV ); COUNT cItemsSelected = 0; DWORD i; for( i = 0; i < cItems; ++i ) { if( ListView_GetItemState( _hwndLV, i, kStateMask ) & kStateChecked ) { ++cItemsSelected; } } return cItemsSelected; } COUNT TPortsLV:: cSelectedItems( VOID ) /*++ Routine Description: Returns the number of items which are currently selected. Arguments: None. Return Value: Return the number of selected items. --*/ { DWORD cItems = ListView_GetItemCount( _hwndLV ); COUNT cItemsSelected = 0; DWORD i; for( i = 0; i < cItems; ++i ) { if( ListView_GetItemState( _hwndLV, i, LVIS_SELECTED ) & LVIS_SELECTED ) { ++cItemsSelected; } } return cItemsSelected; } VOID TPortsLV:: vRemoveAllChecks( VOID ) /*++ Routine Description: Removes all the check marks for the list view. Arguments: None. Return Value: Nothing. --*/ { DWORD cItems = ListView_GetItemCount( _hwndLV ); COUNT cItemsSelected = 0; DWORD i; for( i=0; i<cItems; ++i ){ if( ListView_GetItemState( _hwndLV, i, kStateMask ) & kStateChecked ){ ListView_SetItemState( _hwndLV, i, kStateUnchecked, kStateMask ); } } } VOID TPortsLV:: vSetFocus( VOID ) { SetFocus( _hwndLV ); } BOOL TPortsLV:: bGetSelectedPort( OUT LPTSTR pszPort, IN COUNT cchPort ) /*++ Routine Description: Retrieve the currently selected port in the list view. Arguments: pszPort - TCHAR array to receive port cchPort - COUNTOF port string. Return Value: TRUE - success, FALSE = fail. --*/ { INT iItem = ListView_GetNextItem( _hwndLV, -1, LVNI_SELECTED ); if( iItem == -1 ){ DBGMSG( DBG_WARN, ( "PrinterPort.bGetSelectedPort: Unable to retrieve next selected item %d\n", GetLastError( ))); return FALSE; } ListView_GetItemText( _hwndLV, iItem, 0, pszPort, cchPort ); return TRUE; } BOOL TPortsLV:: bGetSelectedPort( OUT LPTSTR pszPort, IN COUNT cchPort, INT *pItem ) /*++ Routine Description: Retrieve the currently selected port in the list view. Arguments: pszPort - TCHAR array to receive port cchPort - COUNTOF port string. iItem - Index of the item with which to begin the search Return Value: TRUE - success, FALSE = fail. iItem will be set to the new found index. --*/ { INT iItem = *pItem; iItem = ListView_GetNextItem( _hwndLV, iItem, LVNI_SELECTED ); if( iItem == -1 ){ DBGMSG( DBG_WARN, ( "PrinterPort.bGetSelectedPort: Unable to retrieve next selected item %d\n", GetLastError( ))); return FALSE; } ListView_GetItemText( _hwndLV, iItem, 0, pszPort, cchPort ); *pItem = iItem; return TRUE; } BOOL TPortsLV:: bHandleNotifyMessage( LPARAM lParam ) { BOOL bStatus = TRUE; LPNMHDR pnmh = (LPNMHDR)lParam; switch( pnmh->code ) { case NM_DBLCLK: vHandleItemClicked( lParam ); if( _wmDoubleClickMsg ) { PostMessage( _hwnd, WM_COMMAND, _wmDoubleClickMsg, 0 ); } break; case NM_CLICK: vHandleItemClicked( lParam ); if( _wmSingleClickMsg ) { PostMessage( _hwnd, WM_COMMAND, _wmSingleClickMsg, 0 ); } break; case LVN_KEYDOWN: { LV_KEYDOWN* plvnkd = (LV_KEYDOWN *)lParam; if( _bSelectionState && _bAllowSelectionChange ) { // // !! LATER !! // // Is this the best way to check whether the ALT // key is _not_ down? // if( plvnkd->wVKey == TEXT( ' ' ) && !( GetKeyState( VK_LMENU ) & 0x80000000 ) && !( GetKeyState( VK_RMENU ) & 0x80000000 )) { vItemClicked( ListView_GetNextItem( _hwndLV, -1, LVNI_SELECTED )); } } // // If the delete key was used then post a message to // appropriate window with the specified message. // if(plvnkd->wVKey == VK_DELETE ) { if( _wmDeleteKeyMsg ) { PostMessage( _hwnd, WM_COMMAND, _wmDeleteKeyMsg, 0 ); } } } break; case LVN_COLUMNCLICK: { NM_LISTVIEW *pNm = (NM_LISTVIEW *)lParam; (VOID)bListViewSort( pNm->iSubItem ); } break; default: bStatus = FALSE; break; } return bStatus; } VOID TPortsLV:: vHandleItemClicked( IN LPARAM lParam ) { if( _bSelectionState && _bAllowSelectionChange ) { LV_HITTESTINFO lvhti; DWORD dwPos = GetMessagePos(); POINTS &pt = MAKEPOINTS(dwPos); lvhti.pt.x = pt.x; lvhti.pt.y = pt.y; MapWindowPoints( HWND_DESKTOP, _hwndLV, &lvhti.pt, 1 ); INT iItem = ListView_HitTest( _hwndLV, &lvhti ); if( iItem != -1 ) { vItemClicked( iItem ); } } } VOID TPortsLV:: vInsertPortsByMask( IN UINT cPorts, IN PORT_INFO_2 pPorts[], IN UINT cPrinters, IN PRINTER_INFO_2 pPrinters[], IN DWORD dwLevel, IN LPCTSTR pszTemplate, IN LPCTSTR pszDescription ) { TString strPrinters; // // Go through the ports and add them. // for( UINT i=0; i<cPorts; ++i ) { if( dwLevel == 2 ) { // // Check if this port has been added // if( NULL == pPorts[i].pPortName ) { continue; } // // Check if the port name matches the template // if( pszTemplate && !bMatchTemplate( pszTemplate, pPorts[i].pPortName ) ) { continue; } // // Assign proper description // LPCTSTR pszDescr = pszDescription; if( !pszDescr ) { pszDescr = pPorts[i].pDescription; } // // If we have printers on this machine. // if( pPrinters && cPrinters ) { vPrintersUsingPort( strPrinters, pPrinters, cPrinters, pPorts[i].pPortName ); } vAddPortToListView( pPorts[i].pPortName, pPorts[i].pMonitorName, pszDescr, strPrinters ); // // Mark the port as added // pPorts[i].pPortName = NULL; } else { // // Check if this port has been added // if( NULL == ((PPORT_INFO_1)pPorts)[i].pName ) { continue; } // // Check if the port name matches the template // if( pszTemplate && !bMatchTemplate( pszTemplate, ((PPORT_INFO_1)pPorts)[i].pName ) ) { continue; } // // If we have printers on this machine. // if( pPrinters && cPrinters ) { vPrintersUsingPort( strPrinters, pPrinters, cPrinters, ((PPORT_INFO_1)pPorts)[i].pName ); } vAddPortToListView( ((PPORT_INFO_1)pPorts)[i].pName, gszNULL, gszNULL, strPrinters ); // // Mark the port as added // ((PPORT_INFO_1)pPorts)[i].pName = NULL; } } } BOOL TPortsLV:: bDeletePorts( IN HWND hDlg, IN LPCTSTR pszServer ) /*++ Routine Description: Delete selected ports for given print server. Arguments: hDlg - dialog handle for port tab. pszServer - print server name. Return Value: True if at least one port is deleted, false otherwise. --*/ { TStatusB bStatus; TCHAR szPortName[TPortsLV::kPortNameMax]; COUNT cItems; INT iItem = -1; INT i; BOOL bFailed = FALSE; BOOL bDeleted = FALSE; // // Check whether multi ports are selected // cItems = cSelectedItems(); if(cItems == 0) { return FALSE; } // // Get the first selected port name to compose the warning message // bStatus DBGCHK = bGetSelectedPort( szPortName, COUNTOF( szPortName ) ); if( IDYES == iMessage( hDlg, IDS_DELETE_PORT_TITLE, cItems > 1 ? IDS_DELETE_PORTN : IDS_DELETE_PORT, MB_YESNO | MB_ICONQUESTION, kMsgNone, NULL, szPortName )) { // // Try to delete all selected items // for( i = 0; i < (INT)cItems ; i++ ) { // // Get each selected port name // bStatus DBGCHK = bGetSelectedPort( szPortName, COUNTOF( szPortName ), &iItem ); SPLASSERT( bStatus ); SPLASSERT( iItem != -1 ); // // Attempt to delete the selected port. // bStatus DBGCHK = DeletePort( (LPTSTR)pszServer, hDlg, szPortName ); if( bStatus ) { // // Succeeded, refresh the ports UI by deleting the port. // vDeletePortFromListView( szPortName ); // // Decrease the iItem because deleting one item in the list // iItem--; bDeleted = TRUE; } else { if( GetLastError() != ERROR_CANCELLED ) { bFailed = TRUE; } } } // // Only show an error message if the did not cancel the // the action. // if( bFailed ) { iMessage( hDlg, IDS_DELETE_PORT_TITLE, cItems > 1 ? IDS_ERR_DELETE_PORTN : IDS_ERR_DELETE_PORT, MB_OK | MB_ICONEXCLAMATION, kMsgGetLastError, gaMsgErrMapPorts); } bStatus DBGNOCHK = bDeleted; } else { bStatus DBGNOCHK = FALSE; } return bStatus; } BOOL TPortsLV:: bConfigurePort( IN HWND hDlg, IN LPCTSTR pszServer ) { static MSG_ERRMAP aMsgErrMapPorts[] = { ERROR_INVALID_PARAMETER, IDS_ERR_PORT_DOES_NOT_EXIST, 0, 0 }; TStatusB bStatus; TCHAR szPortName[TPortsLV::kPortNameMax]; bStatus DBGCHK = bGetSelectedPort( szPortName, COUNTOF( szPortName ) ); if( bStatus ) { bStatus DBGCHK = ConfigurePort( (LPTSTR)pszServer, hDlg, szPortName ); if( !bStatus ) { if( GetLastError() != ERROR_CANCELLED ) { iMessage( hDlg, IDS_CONFIGURE_PORT_TITLE, IDS_ERR_CONFIG_PORT, MB_OK | MB_ICONEXCLAMATION, kMsgGetLastError, aMsgErrMapPorts ); } } } else { DBGMSG( DBG_WARN, ( "PrinterPorts.vConfigure: failed %d\n", GetLastError( ))); } return bStatus; } VOID TPortsLV:: vPrintersUsingPort( IN OUT TString &strPrinters, IN PRINTER_INFO_2 *pPrinterInfo, IN DWORD cPrinterInfo, IN LPCTSTR pszPortName ) /*++ Routine Description: Builds a comma separated string of all the printers using the specified port. Arguments: strPrinters - TString refrence where to return resultant string. pPrinterInfo - Pointer to a printer info level 2 structure array. cPrinterInfo - Number of printers in the printer info 2 array. pszPortName - Pointer to string or port name to match. Return Value: Nothing. Notes: If no printer is using the specfied port the string refrence will contain an empty string. --*/ { SPLASSERT( pPrinterInfo ); SPLASSERT( pszPortName ); LPTSTR psz; LPTSTR pszPort; LPTSTR pszPrinter; UINT i; // // Clear the current printer buffer. // TStatusB bStatus; bStatus DBGCHK = strPrinters.bUpdate( NULL ); // // Traverse the printer info array. // for( i = 0; i < cPrinterInfo; i++ ){ for( psz = pPrinterInfo[i].pPortName; psz && *psz; ){ // // Look for a comma if found terminate the port string. // pszPort = psz; psz = _tcschr( psz, TEXT( ',' ) ); if( psz ){ *psz = 0; } // // Check for a port match. // if( !_tcsicmp( pszPort, pszPortName ) ){ // // Point to printer name. // pszPrinter = pPrinterInfo[i].pPrinterName; // // Strip the server name here. // if( pPrinterInfo[i].pPrinterName[0] == TEXT( '\\' ) && pPrinterInfo[i].pPrinterName[1] == TEXT( '\\' ) ){ // // Locate the printer name. // pszPrinter = _tcschr( pPrinterInfo[i].pPrinterName+2, TEXT( '\\' ) ); pszPrinter = pszPrinter ? pszPrinter+1 : pPrinterInfo[i].pPrinterName; } // // If this is the first time do not place a comma separator. // if( !strPrinters.bEmpty() ){ bStatus DBGCHK = strPrinters.bCat( TEXT( ", " ) ); if( !bStatus ){ DBGMSG( DBG_WARN, ( "Error cat string line: %d file : %s.\n", __LINE__, __FILE__ ) ); break; } } // // Tack on the printer name // bStatus DBGCHK = strPrinters.bCat( pszPrinter ); if( !bStatus ){ DBGMSG( DBG_WARN, ( "Error cat string line : %d file : %s.\n", __LINE__, __FILE__ ) ); break; } } // // Replace the previous comma. // if( psz ){ *psz = TEXT( ',' ); ++psz; } } } } VOID TPortsLV:: vSetSingleSelection( IN BOOL bSingleSelection ) /*++ Routine Description: Set the list view into single selection mode. Arguments: bSingleSelection - TRUE single selection, FALSE multi selection. Return Value: Nothing. --*/ { _bSingleSelection = bSingleSelection; } BOOL TPortsLV:: bGetSingleSelection( VOID ) /*++ Routine Description: Get the current list view selection mode. Arguments: None. Return Value: TURE in single selection mode, FALSE in multi selection mode. --*/ { return _bSingleSelection; } VOID TPortsLV:: vCreatePortDataList( VOID ) /*++ Routine Description: Initialize the port data list. Arguments: None. Return Value: Nothing. --*/ { DBGMSG( DBG_TRACE, ( "PortsLV::vCreatePortDataList\n" ) ); PortDataList_vReset(); } VOID TPortsLV:: vDestroyPortDataList( VOID ) /*++ Routine Description: Destroy the port data list. Arguments: None. Return Value: Nothing. --*/ { DBGMSG( DBG_TRACE, ( "PortsLV::vDestroyPortDataList\n" ) ); TIter Iter; TPortData *pPortData; for( PortDataList_vIterInit( Iter ), Iter.vNext(); Iter.bValid(); ) { pPortData = PortDataList_pConvert( Iter ); Iter.vNext(); delete pPortData; } } BOOL TPortsLV:: bListViewSort( IN UINT uColumn ) /*++ Routine Description: This function is called to sort the items. The current column indes is specified to indicated witch column to sort. Arguments: Column index to sort. Return Value: TRUE list is sorted, FALSE error occurred. --*/ { DBGMSG( DBG_TRACE, ( "PortsLV::bListViewSort Column %d\n", uColumn ) ); // // Set the surrent column number. // _uCurrentColumn = uColumn; // // Tell the list view to sort. // TStatusB bStatus; bStatus DBGCHK = ListView_SortItems( _hwndLV, iCompareProc, (LPARAM)this ); // // Toggle the specified column sort state. // _ColumnSortState.bToggle( uColumn ); return bStatus; } INT CALLBACK TPortsLV:: iCompareProc( IN LPARAM lParam1, IN LPARAM lParam2, IN LPARAM RefData ) /*++ Routine Description: List view defined compare routine. Arguments: None. Return Value: Nothing. --*/ { TPortData *pPortData1 = reinterpret_cast<TPortData *>( lParam1 ); TPortData *pPortData2 = reinterpret_cast<TPortData *>( lParam2 ); TPortsLV *pPortsLV = reinterpret_cast<TPortsLV *>( RefData ); INT iResult = 0; LPCTSTR strName1 = NULL; LPCTSTR strName2 = NULL; if( pPortsLV && pPortData1 && pPortData2 ) { BOOL bStatus = TRUE; switch( pPortsLV->_uCurrentColumn ) { case 0: strName1 = pPortData1->_strName; strName2 = pPortData2->_strName; break; case 1: strName1 = pPortData1->_strDescription; strName2 = pPortData2->_strDescription; break; case 2: strName1 = pPortData1->_strPrinters; strName2 = pPortData2->_strPrinters; break; default: bStatus = FALSE; break; } if( bStatus ) { if( pPortsLV->_ColumnSortState.bRead( pPortsLV->_uCurrentColumn ) ) iResult = _tcsicmp( strName2, strName1 ); else iResult = _tcsicmp( strName1, strName2 ); } } return iResult; } TPortsLV::TPortData * TPortsLV:: AddPortDataList( IN LPCTSTR pszName, IN LPCTSTR pszMonitor, IN LPCTSTR pszDescription, IN LPCTSTR pszPrinters ) /*++ Routine Description: Add port to port data list. Arguments: pszName - pointer to port name. pszDescription - pointer to description string. pszPrinters - pointer to printers using this port string. Return Value: TRUE port added to data list, FALSE error occurred. --*/ { DBGMSG( DBG_TRACE, ( "PortsLV::AddPortDataList\n" ) ); // // Allocate the port data. // TPortData *pPortData = new TPortData( pszName, pszMonitor, pszDescription, pszPrinters ); // // If valid object created. // if( VALID_PTR( pPortData ) ) { // // Add the port data to the list. // PortDataList_vAppend( pPortData ); } else { // // The object may have been allocated, however failed construction. // delete pPortData; pPortData = NULL; } return pPortData; } BOOL TPortsLV:: DeletePortDataList( IN LPCTSTR pszName ) /*++ Routine Description: Delete the specified port from the port data list. Arguments: pszName - pointer to port name. Return Value: TRUE port delete, FALSE error occurred. --*/ { DBGMSG( DBG_TRACE, ( "PortsLV::DeletePortDataList\n" ) ); BOOL bStatus = FALSE; TIter Iter; for( PortDataList_vIterInit( Iter ), Iter.vNext(); Iter.bValid(); Iter.vNext() ) { TPortData *pPortData = PortDataList_pConvert( Iter ); if( pPortData->_strName == pszName ) { DBGMSG( DBG_TRACE, ( "PortsLV::DeletePortDataList Port "TSTR" deleted\n", pszName ) ); delete pPortData; bStatus = TRUE; break; } } return bStatus; } /******************************************************************** Port Data helper class. ********************************************************************/ TPortsLV::TPortData:: TPortData( IN LPCTSTR pszName, IN LPCTSTR pszMonitor, IN LPCTSTR pszDescription, IN LPCTSTR pszPrinters ) { DBGMSG( DBG_TRACE, ( "PortsLV::TPortData ctor\n" ) ); _strName.bUpdate( pszName ); _strMonitor.bUpdate( pszMonitor ); _strDescription.bUpdate( pszDescription ); _strPrinters.bUpdate( pszPrinters ); } TPortsLV::TPortData:: ~TPortData( VOID ) { DBGMSG( DBG_TRACE, ( "PortsLV::TPortData dtor\n" ) ); // // If we are linked then remove ourself. // if( PortData_bLinked() ) { PortData_vDelinkSelf(); } } BOOL TPortsLV::TPortData:: bValid( VOID ) { return VALID_OBJ( _strName ) && VALID_OBJ( _strMonitor ) && VALID_OBJ( _strDescription ) && VALID_OBJ( _strPrinters ); }