windows-nt/Source/XPSP1/NT/printscan/ui/printui/portslv.cxx
2020-09-26 16:20:57 +08:00

2183 lines
45 KiB
C++

/*++
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 );
}