1196 lines
43 KiB
C++
1196 lines
43 KiB
C++
//+----------------------------------------------------------------------------
|
|
//
|
|
// File: listview.cpp
|
|
//
|
|
// Module: CMAK.EXE
|
|
//
|
|
// Synopsis: Implemenation of the helper functions used by CMAK to deal with the
|
|
// custom action list view control.
|
|
//
|
|
// Copyright (c) 2000 Microsoft Corporation
|
|
//
|
|
// Author: quintinb Created 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
#include "cmmaster.h"
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: UpdateListViewColumnHeadings
|
|
//
|
|
// Synopsis: This function sets the column heading text specified by the given
|
|
// column index and list view control window handle to the string
|
|
// resource specified by the given instance handle and string Id.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hListView - window handle of the list view control
|
|
// UINT uStringID - string id of the desired text
|
|
// int iColumnIndex - desired column to update the text of
|
|
//
|
|
// Returns: BOOL - TRUE on success, FALSE otherwise
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
BOOL UpdateListViewColumnHeadings(HINSTANCE hInstance, HWND hListView, UINT uStringID, int iColumnIndex)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
|
|
MYDBGASSERT(hInstance);
|
|
MYDBGASSERT(hListView);
|
|
MYDBGASSERT(uStringID);
|
|
|
|
if (hInstance && hListView && uStringID)
|
|
{
|
|
//
|
|
// First get the requested string
|
|
//
|
|
LVCOLUMN lvColumn = {0};
|
|
|
|
lvColumn.mask = LVCF_TEXT;
|
|
lvColumn.pszText = CmLoadString(hInstance, uStringID);
|
|
|
|
MYDBGASSERT(lvColumn.pszText);
|
|
if (lvColumn.pszText)
|
|
{
|
|
bReturn = ListView_SetColumn(hListView, iColumnIndex, &lvColumn);
|
|
CmFree(lvColumn.pszText);
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: AddListViewColumnHeadings
|
|
//
|
|
// Synopsis: This function creates the description and type columns
|
|
// used by the default view of the custom action page. Once this
|
|
// function has been called, UpdateListViewColumnHeadings should
|
|
// be used to change the column headings as necessary. This function
|
|
// will need to be modified if more columns are deemed necessary.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hListView - window handle of the list view control
|
|
//
|
|
// Returns: BOOL - TRUE on success, FALSE otherwise
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
BOOL AddListViewColumnHeadings(HINSTANCE hInstance, HWND hListView)
|
|
{
|
|
//
|
|
// Add the column headings
|
|
//
|
|
LVCOLUMN lvColumn = {0};
|
|
|
|
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
|
|
lvColumn.fmt = LVCFMT_LEFT;
|
|
lvColumn.pszText = CmLoadString(hInstance, IDS_DESC_COL_TITLE);
|
|
lvColumn.iSubItem = 0;
|
|
|
|
MYDBGASSERT(lvColumn.pszText);
|
|
if (lvColumn.pszText)
|
|
{
|
|
ListView_InsertColumn(hListView, 0, &lvColumn);
|
|
CmFree(lvColumn.pszText);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lvColumn.pszText = CmLoadString(hInstance, IDS_TYPE_COL_TITLE);
|
|
lvColumn.iSubItem = 1;
|
|
|
|
MYDBGASSERT(lvColumn.pszText);
|
|
if (lvColumn.pszText)
|
|
{
|
|
ListView_InsertColumn(hListView, 1, &lvColumn);
|
|
CmFree(lvColumn.pszText);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now lets size the columns so that the text is visible. Since we
|
|
// only have two columns of text, lets call GetWindowRect on the
|
|
// list view control and then set the column widths to each take
|
|
// up about half of the space available.
|
|
//
|
|
RECT Rect = {0};
|
|
LONG lColumnWidth;
|
|
|
|
if (GetWindowRect(hListView, &Rect))
|
|
{
|
|
//
|
|
// Subtract 5 from each to keep a scroll bar from appearing
|
|
//
|
|
lColumnWidth = (Rect.right - Rect.left)/2 - 5;
|
|
|
|
if (0 < lColumnWidth)
|
|
{
|
|
for (int i=0; i < 2; i++)
|
|
{
|
|
MYVERIFY(ListView_SetColumnWidth(hListView, i, lColumnWidth));
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: MapComboSelectionToType
|
|
//
|
|
// Synopsis: This function gets the current selection from the given
|
|
// combobox and maps the index to a custom action type.
|
|
//
|
|
// Arguments: HWND hDlg - window handle to the dialog that contains the combobox
|
|
// UINT uCtrlID - control id of the combobox
|
|
// BOOL bIncludesAll - when TRUE
|
|
// BOOL bUseTunneling - whether this is a tunneling profile or not
|
|
// CustomActionTypes* pType
|
|
//
|
|
// Returns: HRESULT - Standard COM error codes
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
HRESULT MapComboSelectionToType(HWND hDlg, UINT uCtrlID, BOOL bIncludesAll, BOOL bUseTunneling, CustomActionTypes* pType)
|
|
{
|
|
//
|
|
// Check Params
|
|
//
|
|
if ((NULL == hDlg) || (0 == uCtrlID) || (NULL == pType))
|
|
{
|
|
CMASSERTMSG(FALSE, TEXT("MapComboSelectionToType -- invalid parameter passed"));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
INT_PTR nResult = SendDlgItemMessage(hDlg, uCtrlID, CB_GETCURSEL, 0, (LPARAM)0);
|
|
|
|
if (nResult != LB_ERR)
|
|
{
|
|
//
|
|
// If the combobox contains the All choice, we need to correct
|
|
// the type depending on what the user chose.
|
|
//
|
|
|
|
if (bIncludesAll)
|
|
{
|
|
if (0 == nResult)
|
|
{
|
|
*pType = ALL;
|
|
goto exit;
|
|
}
|
|
else
|
|
{
|
|
nResult--;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to make a correction if we aren't Tunneling because the
|
|
// tunneling type won't be in the combobox
|
|
//
|
|
if (FALSE == bUseTunneling)
|
|
{
|
|
if (PRETUNNEL <= (CustomActionTypes)nResult)
|
|
{
|
|
nResult++;
|
|
}
|
|
}
|
|
|
|
*pType = (CustomActionTypes)nResult;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: GetItemTypeByListViewIndex
|
|
//
|
|
// Synopsis: This function gets the current selection index of the list view
|
|
// control and gets the type string. The type string is then
|
|
// converted into a numeric type and returned via the pType
|
|
// pointer.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle for string resources
|
|
// HWND hListView - window handle of the list view control
|
|
// CustomActionTypes* pType - pointer to hold the type of the item
|
|
//
|
|
// Returns: HRESULT - Standard COM error codes
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
HRESULT GetItemTypeByListViewIndex(HINSTANCE hInstance, HWND hListView, CustomActionTypes* pType, int *piIndex)
|
|
{
|
|
//
|
|
// Check params
|
|
//
|
|
if ((NULL == hListView) || (NULL == pType) || (NULL == g_pCustomActionList))
|
|
{
|
|
CMASSERTMSG(FALSE, TEXT("GetItemTypeByListViewIndex -- invalid parameter passed"));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// The user has the All view selected, further work is needed to select the
|
|
// appropriate type.
|
|
//
|
|
HRESULT hr = S_OK;
|
|
|
|
if (-1 == *piIndex)
|
|
{
|
|
*piIndex = ListView_GetSelectionMark(hListView);
|
|
}
|
|
|
|
int iTemp = *piIndex;
|
|
|
|
if (-1 != iTemp)
|
|
{
|
|
LVITEM lvItem = {0};
|
|
TCHAR szTemp[MAX_PATH+1];
|
|
|
|
szTemp[0] = TEXT('\0');
|
|
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.pszText = szTemp;
|
|
lvItem.cchTextMax = MAX_PATH;
|
|
lvItem.iItem = iTemp;
|
|
lvItem.iSubItem = 1;
|
|
|
|
if (ListView_GetItem(hListView, &lvItem))
|
|
{
|
|
hr = g_pCustomActionList->GetTypeFromTypeString(hInstance, lvItem.pszText, pType);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: GetDescriptionAndTypeOfItem
|
|
//
|
|
// Synopsis: This function gets the type and description of the item specified
|
|
// by the passed in item index. If the caller passes -1 for this index,
|
|
// the currently selected item is used and the actual index is passed
|
|
// back via this in/out pointer.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// UINT uComboBoxId - combo box id containing the type info
|
|
// CustomActionListItem* pItem - pointer to a custom action struct to
|
|
// hold the returned type and description
|
|
// int* piItemIndex - index of the item to get the description and
|
|
// type of. If -1, then the current selection mark
|
|
// is used and the actual index is returned in *piItemIndex
|
|
// BOOL bUseTunneling - whether this profile uses tunneling or not
|
|
//
|
|
// Returns: HRESULT - Standard COM error codes
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
HRESULT GetDescriptionAndTypeOfItem(HINSTANCE hInstance, HWND hDlg, HWND hListView, UINT uComboBoxId,
|
|
CustomActionListItem* pItem, int* piItemIndex, BOOL bUseTunneling)
|
|
{
|
|
//
|
|
// Check Params
|
|
//
|
|
if (NULL == hDlg || NULL == hListView || 0 == uComboBoxId || NULL == pItem || NULL == piItemIndex)
|
|
{
|
|
CMASSERTMSG(FALSE, TEXT("GetDescriptionAndTypeOfSelection -- Invalid parameter passed."));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
//
|
|
// If the user passed us a -1 in *piItemIndex then they want the Description and Type of the
|
|
// selected item. Otherwise, they gave us a specific item index that they want data on.
|
|
//
|
|
int iTemp;
|
|
|
|
if (-1 == *piItemIndex)
|
|
{
|
|
iTemp = ListView_GetSelectionMark(hListView);
|
|
}
|
|
else
|
|
{
|
|
iTemp = ListView_GetItemCount(hListView);
|
|
|
|
if ((0 > *piItemIndex) || (iTemp <= *piItemIndex))
|
|
{
|
|
iTemp = -1;
|
|
}
|
|
else
|
|
{
|
|
iTemp = *piItemIndex;
|
|
}
|
|
}
|
|
|
|
if (-1 != iTemp)
|
|
{
|
|
//
|
|
// Figure out the type of the item
|
|
//
|
|
ZeroMemory(pItem, sizeof(CustomActionListItem));
|
|
|
|
hr = MapComboSelectionToType(hDlg, uComboBoxId, TRUE, bUseTunneling, &(pItem->Type)); //bIncludesAll == TRUE
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (ALL == pItem->Type)
|
|
{
|
|
hr = GetItemTypeByListViewIndex(hInstance, hListView, &(pItem->Type), &iTemp);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now Figure out the description of the item
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ListView_GetItemText(hListView, iTemp, 0, pItem->szDescription, CELEMS(pItem->szDescription));
|
|
}
|
|
|
|
*piItemIndex = iTemp;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: RefreshEditDeleteMoveButtonStates
|
|
//
|
|
// Synopsis: This function sets the enabled/disabled state of the Edit, Delete,
|
|
// Move up and Move Down buttons based on the custom action specified by
|
|
// the list view index passed in through the piIndex param. If this
|
|
// parameter is -1 then the currently selected item is used and
|
|
// the actual index is returned through the int pointer.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// UINT uComboBoxId - combo box id containing the type info
|
|
// int* piIndex - index of the list view item to base the move up
|
|
// and move down button state on. Again -1 will use
|
|
// the currently selected item.
|
|
// BOOl bUseTunneling - whether this profile uses tunneling or not
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void RefreshEditDeleteMoveButtonStates(HINSTANCE hInstance, HWND hDlg, HWND hListView, UINT uComboCtrlId, int* piIndex, BOOL bUseTunneling)
|
|
{
|
|
if ((NULL == hInstance) || (NULL == hDlg) || (NULL == hListView) ||
|
|
(0 == uComboCtrlId) || (NULL == piIndex) || (NULL == g_pCustomActionList))
|
|
{
|
|
CMASSERTMSG(FALSE, TEXT("RefreshEditDeleteMoveButtonStates -- invalid parameter passed."));
|
|
return;
|
|
}
|
|
|
|
int iDisableMoveUp = -1; // -1 is the true value for GetListPositionAndBuiltInState
|
|
int iDisableMoveDown = -1;
|
|
int iDisableDeleteAndEdit = -1;
|
|
CustomActionListItem Item;
|
|
|
|
if (ListView_GetItemCount(hListView))
|
|
{
|
|
//
|
|
// Get the description and type of the item *piIndex (if -1 then the currently selected item)
|
|
//
|
|
//
|
|
|
|
ZeroMemory(&Item, sizeof(Item));
|
|
HRESULT hr = GetDescriptionAndTypeOfItem(hInstance, hDlg, hListView, uComboCtrlId, &Item, piIndex, bUseTunneling);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pCustomActionList->GetListPositionAndBuiltInState(hInstance, &Item, &iDisableMoveUp, &iDisableMoveDown, &iDisableDeleteAndEdit);
|
|
MYDBGASSERT(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
|
|
HWND hCurrentFocus = GetFocus();
|
|
HWND hEditButton = GetDlgItem(hDlg, IDC_BUTTON2);
|
|
HWND hDeleteButton = GetDlgItem(hDlg, IDC_BUTTON3);
|
|
HWND hMoveUpButton = GetDlgItem(hDlg, IDC_BUTTON4);
|
|
HWND hMoveDownButton = GetDlgItem(hDlg, IDC_BUTTON5);
|
|
|
|
if (hEditButton)
|
|
{
|
|
EnableWindow(hEditButton, (iDisableDeleteAndEdit ? 0 : 1));
|
|
}
|
|
|
|
if (hDeleteButton)
|
|
{
|
|
EnableWindow(hDeleteButton, (iDisableDeleteAndEdit ? 0 : 1));
|
|
}
|
|
|
|
if (hMoveUpButton)
|
|
{
|
|
EnableWindow(hMoveUpButton, (iDisableMoveUp ? 0 : 1));
|
|
}
|
|
|
|
if (hMoveDownButton)
|
|
{
|
|
EnableWindow(hMoveDownButton, (iDisableMoveDown ? 0 : 1));
|
|
}
|
|
|
|
|
|
if (hCurrentFocus && (FALSE == IsWindowEnabled(hCurrentFocus)))
|
|
{
|
|
if (hDeleteButton == hCurrentFocus)
|
|
{
|
|
//
|
|
// If delete is disabled and contained the focus, shift it to the Add button
|
|
//
|
|
SendMessage(hDlg, DM_SETDEFID, IDC_BUTTON1, (LPARAM)0L); //lint !e534 DM_SETDEFID doesn't return error info
|
|
HWND hControl = GetDlgItem(hDlg, IDC_BUTTON1);
|
|
|
|
if (hControl)
|
|
{
|
|
SetFocus(hControl);
|
|
}
|
|
}
|
|
else if ((hMoveUpButton == hCurrentFocus) && IsWindowEnabled(hMoveDownButton))
|
|
{
|
|
SendMessage(hDlg, DM_SETDEFID, IDC_BUTTON5, (LPARAM)0L); //lint !e534 DM_SETDEFID doesn't return error info
|
|
SetFocus(hMoveDownButton);
|
|
}
|
|
else if ((hMoveDownButton == hCurrentFocus) && IsWindowEnabled(hMoveUpButton))
|
|
{
|
|
SendMessage(hDlg, DM_SETDEFID, IDC_BUTTON4, (LPARAM)0L); //lint !e534 DM_SETDEFID doesn't return error info
|
|
SetFocus(hMoveUpButton);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If all else fails set the focus to the listview control
|
|
//
|
|
SetFocus(hListView);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: SelectListViewItem
|
|
//
|
|
// Synopsis: This function trys to select a list view item with the given
|
|
// type and description in the given listview control. If the listview
|
|
// doesn't contain the item we are looking for it returns FALSE and
|
|
// doesn't change the selection.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle for resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// UINT uComboBoxId - combo box id containing the type info
|
|
// HWND hListView - window handle to the list view control
|
|
// BOOL bUseTunneling - whether this is a tunneling profile or not,
|
|
// affects whether Pre-Tunnel actions are displayed
|
|
// or not.
|
|
// CustomActionTypes TypeToSelect - type of the item to select
|
|
// LPCTSTR pszDescription - description of the item to select
|
|
//
|
|
// Returns: TRUE if the required item was found and selected
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
/*
|
|
BOOL SelectListViewItem(HWND hDlg, UINT uComboCtrlId, HWND hListView, BOOL bUseTunneling, CustomActionTypes TypeToSelect, LPCTSTR pszDescription)
|
|
{
|
|
CustomActionTypes Type;
|
|
BOOL bReturn = FALSE;
|
|
HRESULT hr = MapComboSelectionToType(hDlg, uComboCtrlId, TRUE, bUseTunneling, &Type); //bIncludesAll == TRUE
|
|
|
|
if ((ALL == Type) || (TypeToSelect == Type))
|
|
{
|
|
LVFINDINFO lvFindInfo = {0};
|
|
LVITEM lvItem = {0};
|
|
|
|
lvFindInfo.flags = LVFI_STRING;
|
|
lvFindInfo.psz = pszDescription;
|
|
|
|
int iIndex = ListView_FindItem(hListView, -1, &lvFindInfo);
|
|
|
|
if (-1 != iIndex)
|
|
{
|
|
//
|
|
// Select the item
|
|
//
|
|
ListView_SetSelectionMark(hListView, iIndex);
|
|
|
|
//
|
|
// Now set the selection state so it shows up as selected in the UI.
|
|
//
|
|
lvItem.mask = LVIF_STATE;
|
|
lvItem.state = LVIS_SELECTED;
|
|
lvItem.stateMask = LVIS_SELECTED;
|
|
lvItem.iItem = iIndex;
|
|
lvItem.iSubItem = 0;
|
|
|
|
MYVERIFY(ListView_SetItem(hListView, &lvItem));
|
|
|
|
//
|
|
// Now Verify that the selection is visible
|
|
//
|
|
MYVERIFY(ListView_EnsureVisible(hListView, iIndex, FALSE)); // FALSE = fPartialOK, we want full visibility
|
|
|
|
bReturn = TRUE;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
*/
|
|
void SetListViewSelection(HWND hListView, int iIndex)
|
|
{
|
|
ListView_SetSelectionMark(hListView, iIndex);
|
|
|
|
//
|
|
// Now set the selection state so it shows up as selected in the UI.
|
|
//
|
|
LVITEM lvItem = {0};
|
|
|
|
lvItem.mask = LVIF_STATE;
|
|
lvItem.state = LVIS_SELECTED;
|
|
lvItem.stateMask = LVIS_SELECTED;
|
|
lvItem.iItem = iIndex;
|
|
lvItem.iSubItem = 0;
|
|
|
|
MYVERIFY(ListView_SetItem(hListView, &lvItem));
|
|
|
|
//
|
|
// Now Verify that the selection is visible
|
|
//
|
|
MYVERIFY(ListView_EnsureVisible(hListView, iIndex, FALSE)); // FALSE = fPartialOK, we want full visibility
|
|
}
|
|
|
|
BOOL SelectListViewItem(HINSTANCE hInstance, HWND hDlg, UINT uComboCtrlId, HWND hListView, BOOL bUseTunneling, CustomActionTypes TypeToSelect, LPCTSTR pszDescription)
|
|
{
|
|
if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]) || (0 == uComboCtrlId) || (NULL == hDlg))
|
|
{
|
|
CMASSERTMSG(FALSE, TEXT("SelectListViewItem -- Invalid parameter passed."));
|
|
return FALSE;
|
|
}
|
|
|
|
CustomActionTypes Type;
|
|
CustomActionTypes TypeSelectedInCombo;
|
|
BOOL bReturn = FALSE;
|
|
|
|
//
|
|
// If the current view is ALL, then we may have multiple items with the same name but different types. Thus
|
|
// we must check the type string of the item and search again if it isn't the correct item. If we are viewing
|
|
// items only of the TypeToSelect then we are guarenteed that there is only one item of that name. Finally if
|
|
// we are viewing a different item type we don't want to do anything to the selection as the item we want to
|
|
// select won't be visible.
|
|
//
|
|
|
|
HRESULT hr = MapComboSelectionToType(hDlg, uComboCtrlId, TRUE, bUseTunneling, &TypeSelectedInCombo); //bIncludesAll == TRUE
|
|
|
|
if (SUCCEEDED(hr) && ((TypeToSelect == TypeSelectedInCombo) || (ALL == TypeSelectedInCombo)))
|
|
{
|
|
//
|
|
// Setup the find structure
|
|
//
|
|
LVFINDINFO lvFindInfo = {0};
|
|
lvFindInfo.flags = LVFI_STRING;
|
|
lvFindInfo.psz = pszDescription;
|
|
|
|
//
|
|
// Setup the Item structure
|
|
//
|
|
LVITEM lvItem = {0};
|
|
TCHAR szTemp[MAX_PATH+1];
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.pszText = szTemp;
|
|
lvItem.cchTextMax = MAX_PATH;
|
|
lvItem.iSubItem = 1;
|
|
|
|
BOOL bExitLoop;
|
|
int iIndex = -1;
|
|
|
|
do
|
|
{
|
|
bExitLoop = TRUE;
|
|
iIndex = ListView_FindItem(hListView, iIndex, &lvFindInfo);
|
|
|
|
if ((-1 != iIndex) && (ALL == TypeSelectedInCombo))
|
|
{
|
|
//
|
|
// Now check to see if this has the type we are looking for
|
|
//
|
|
szTemp[0] = TEXT('\0');
|
|
lvItem.iItem = iIndex;
|
|
|
|
if (ListView_GetItem(hListView, &lvItem))
|
|
{
|
|
hr = g_pCustomActionList->GetTypeFromTypeString(hInstance, lvItem.pszText, &Type);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
bExitLoop = (TypeToSelect == Type);
|
|
}
|
|
}
|
|
}
|
|
|
|
} while(!bExitLoop);
|
|
|
|
if (-1 != iIndex)
|
|
{
|
|
SetListViewSelection(hListView, iIndex);
|
|
bReturn = TRUE;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: RefreshListView
|
|
//
|
|
// Synopsis: This function refreshes the list view data from that contained
|
|
// in the global CustomActionList class. Gets the type of data to
|
|
// display from the combo box specified by the hDlg and uComboCtrlId
|
|
// parameters
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// UINT uComboBoxId - combo box id containing the type info
|
|
// HWND hListView - window handle to the list view control
|
|
// int iItemToSelect - item the caller wants selected after the refresh
|
|
// BOOL bUseTunneling - whether this is a tunneling profile or not,
|
|
// affects whether Pre-Tunnel actions are displayed
|
|
// or not.
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void RefreshListView(HINSTANCE hInstance, HWND hDlg, UINT uComboCtrlId, HWND hListView,
|
|
int iItemToSelect, BOOL bUseTunneling)
|
|
{
|
|
//
|
|
// Refresh the list view
|
|
//
|
|
CustomActionTypes Type;
|
|
BOOL bEnableDeleteAndEdit = FALSE;
|
|
|
|
CMASSERTMSG(hInstance && hDlg && uComboCtrlId && hListView && g_pCustomActionList, TEXT("RefreshListView -- Invalid Parameters passed, skipping refresh"));
|
|
|
|
if (hDlg && uComboCtrlId && hListView && g_pCustomActionList)
|
|
{
|
|
HRESULT hr = MapComboSelectionToType(hDlg, uComboCtrlId, TRUE, bUseTunneling, &Type); //bIncludesAll == TRUE
|
|
|
|
//
|
|
// Add the items to the list view and set the selection to iItemToSelect
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pCustomActionList->AddCustomActionsToListView(hListView, hInstance, Type, bUseTunneling, iItemToSelect, (ALL == Type));
|
|
|
|
MYDBGASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
//
|
|
// If the caller asked for an item that we couldn't select, then the item selected would be the first item. To avoid
|
|
// confusion we will just use the currently selected item by passing -1;
|
|
//
|
|
int iIndex = -1;
|
|
RefreshEditDeleteMoveButtonStates(hInstance, hDlg, hListView, uComboCtrlId, &iIndex, bUseTunneling);
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: OnProcessCustomActionsAdd
|
|
//
|
|
// Synopsis: This function is called when the user presses the Add button
|
|
// on the custom action pane of CMAK. This function is basically a
|
|
// wrapper for the add functionality so that context menus and other
|
|
// commands can also call it with duplicate code.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// BOOL bUseTunneling - whether this profile uses tunneling or not
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void OnProcessCustomActionsAdd(HINSTANCE hInstance, HWND hDlg, HWND hListView, BOOL bUseTunneling)
|
|
{
|
|
MYDBGASSERT(hInstance);
|
|
MYDBGASSERT(hDlg);
|
|
MYDBGASSERT(hListView);
|
|
|
|
if (hInstance && hDlg && hListView)
|
|
{
|
|
CustomActionListItem ListItem;
|
|
CustomActionTypes Type;
|
|
|
|
INT_PTR nResult = -1; // get info on currently selected item
|
|
|
|
//
|
|
// First figure out what type of connect action the list view is showing. We
|
|
// want to preset the combo box on the add/edit dialog to the correct type
|
|
// of custom action, unless it is showing all and then just set it to the first
|
|
// item in the list.
|
|
//
|
|
HRESULT hr = MapComboSelectionToType(hDlg, IDC_COMBO1, TRUE, bUseTunneling, &Type); //bIncludesAll == TRUE
|
|
ZeroMemory(&ListItem, sizeof(CustomActionListItem));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (ALL != Type)
|
|
{
|
|
ListItem.Type = Type;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Still call the Add dialog even if we couldn't determine the type
|
|
//
|
|
nResult = DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_CUSTOM_ACTIONS_POPUP), hDlg,
|
|
ProcessCustomActionPopup,(LPARAM)&ListItem);
|
|
|
|
if (IDOK == nResult)
|
|
{
|
|
RefreshListView(hInstance, hDlg, IDC_COMBO1, hListView, 0, bUseTunneling);
|
|
SelectListViewItem(hInstance, hDlg, IDC_COMBO1, hListView, bUseTunneling, ListItem.Type, ListItem.szDescription);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: OnProcessCustomActionsDelete
|
|
//
|
|
// Synopsis: This function is called when the user presses the Delete button
|
|
// on the custom action pane of CMAK. This function is basically a
|
|
// wrapper for the delete functionality so that context menus and other
|
|
// commands can also call it with duplicate code.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// BOOL bUseTunneling - whether this profile uses tunneling or not
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void OnProcessCustomActionsDelete(HINSTANCE hInstance, HWND hDlg, HWND hListView, BOOL bUseTunneling)
|
|
{
|
|
MYDBGASSERT(hInstance);
|
|
MYDBGASSERT(hDlg);
|
|
MYDBGASSERT(hListView);
|
|
MYDBGASSERT(g_pCustomActionList);
|
|
|
|
if (hInstance && hDlg && hListView && g_pCustomActionList)
|
|
{
|
|
CustomActionListItem ListItem;
|
|
|
|
int iTemp = -1; // get info on currently selected item
|
|
|
|
HRESULT hr = GetDescriptionAndTypeOfItem(hInstance, hDlg, hListView, IDC_COMBO1, &ListItem, &iTemp, bUseTunneling);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pCustomActionList->Delete(hInstance, ListItem.szDescription, ListItem.Type);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
RefreshListView(hInstance, hDlg, IDC_COMBO1, hListView, 0, bUseTunneling);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MYVERIFY(IDOK == ShowMessage(hDlg, IDS_NOSELECTION, MB_OK));
|
|
}
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: OnProcessCustomActionsEdit
|
|
//
|
|
// Synopsis: This function is called when the user presses the Edit button
|
|
// on the custom action pane of CMAK. This function is basically a
|
|
// wrapper for the edit functionality so that context menus and other
|
|
// commands can also call it with duplicate code.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// BOOL bUseTunneling - whether this profile uses tunneling or not
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void OnProcessCustomActionsEdit(HINSTANCE hInstance, HWND hDlg, HWND hListView, BOOL bUseTunneling)
|
|
{
|
|
|
|
MYDBGASSERT(hInstance);
|
|
MYDBGASSERT(hDlg);
|
|
MYDBGASSERT(hListView);
|
|
MYDBGASSERT(g_pCustomActionList);
|
|
|
|
if (hInstance && hDlg && hListView && g_pCustomActionList)
|
|
{
|
|
//
|
|
// First find the name and type of the Connect Action to edit
|
|
//
|
|
CustomActionListItem ListItem;
|
|
|
|
int iTemp = -1; // get info on currently selected item
|
|
HRESULT hr = GetDescriptionAndTypeOfItem(hInstance, hDlg, hListView, IDC_COMBO1, &ListItem, &iTemp, bUseTunneling);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
int iFirstInList;
|
|
int iLastInList;
|
|
int iBuiltIn;
|
|
|
|
//
|
|
// Screen out the built in custom actions
|
|
//
|
|
hr = g_pCustomActionList->GetListPositionAndBuiltInState(hInstance, &ListItem, &iFirstInList, &iLastInList, &iBuiltIn);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (0 == iBuiltIn)
|
|
{
|
|
INT_PTR nResult = DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_CUSTOM_ACTIONS_POPUP), hDlg,
|
|
ProcessCustomActionPopup,(LPARAM)&ListItem);
|
|
|
|
if (IDOK == nResult)
|
|
{
|
|
RefreshListView(hInstance, hDlg, IDC_COMBO1, hListView, 0, bUseTunneling);
|
|
SelectListViewItem(hInstance, hDlg, IDC_COMBO1, hListView, bUseTunneling, ListItem.Type, ListItem.szDescription);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MYVERIFY(IDOK == ShowMessage(hDlg, IDS_NOSELECTION, MB_OK));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: OnProcessCustomActionsMoveUp
|
|
//
|
|
// Synopsis: This function is called when the user presses the Move Up button
|
|
// on the custom action pane of CMAK. This function is basically a
|
|
// wrapper for the move up functionality so that context menus and other
|
|
// commands can also call it with duplicate code.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// BOOL bUseTunneling - whether this profile uses tunneling or not
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void OnProcessCustomActionsMoveUp(HINSTANCE hInstance, HWND hDlg, HWND hListView, BOOL bUseTunneling)
|
|
{
|
|
|
|
MYDBGASSERT(hInstance);
|
|
MYDBGASSERT(hDlg);
|
|
MYDBGASSERT(hListView);
|
|
MYDBGASSERT(g_pCustomActionList);
|
|
|
|
if (hInstance && hDlg && hListView && g_pCustomActionList)
|
|
{
|
|
//
|
|
// First find the name and type of the Connect Action to edit
|
|
//
|
|
CustomActionListItem ListItem;
|
|
|
|
int iTemp = -1; // get info on currently selected item
|
|
|
|
HRESULT hr = GetDescriptionAndTypeOfItem(hInstance, hDlg, hListView, IDC_COMBO1, &ListItem, &iTemp, bUseTunneling);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pCustomActionList->MoveUp(hInstance, ListItem.szDescription, ListItem.Type);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr)) // S_FALSE means it is already first in the list
|
|
{
|
|
RefreshListView(hInstance, hDlg, IDC_COMBO1, hListView, (iTemp - 1), bUseTunneling);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MYVERIFY(IDOK == ShowMessage(hDlg, IDS_NOSELECTION, MB_OK));
|
|
}
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: OnProcessCustomActionsMoveDown
|
|
//
|
|
// Synopsis: This function is called when the user presses the Move Down button
|
|
// on the custom action pane of CMAK. This function is basically a
|
|
// wrapper for the move down functionality so that context menus and other
|
|
// commands can also call it with duplicate code.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// BOOL bUseTunneling - whether this profile uses tunneling or not
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void OnProcessCustomActionsMoveDown(HINSTANCE hInstance, HWND hDlg, HWND hListView, BOOL bUseTunneling)
|
|
{
|
|
|
|
MYDBGASSERT(hInstance);
|
|
MYDBGASSERT(hDlg);
|
|
MYDBGASSERT(hListView);
|
|
MYDBGASSERT(g_pCustomActionList);
|
|
|
|
if (hInstance && hDlg && hListView && g_pCustomActionList)
|
|
{
|
|
//
|
|
// First find the name and type of the Connect Action to edit
|
|
//
|
|
CustomActionListItem ListItem;
|
|
|
|
int iTemp = -1; // get info on currently selected item
|
|
|
|
HRESULT hr = GetDescriptionAndTypeOfItem(hInstance, hDlg, hListView, IDC_COMBO1, &ListItem, &iTemp, bUseTunneling);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pCustomActionList->MoveDown(hInstance, ListItem.szDescription, ListItem.Type);
|
|
|
|
if (SUCCEEDED(hr) && (S_FALSE != hr)) // S_FALSE means it is already last in the list
|
|
{
|
|
RefreshListView(hInstance, hDlg, IDC_COMBO1, hListView, (iTemp + 1), bUseTunneling);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MYVERIFY(IDOK == ShowMessage(hDlg, IDS_NOSELECTION, MB_OK));
|
|
}
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: OnProcessCustomActionsContextMenu
|
|
//
|
|
// Synopsis: This function is called when the user right clicks on the list view
|
|
// control or invokes the context menu via the keyboard (shift+F10 or
|
|
// the context menu key). The function displays the context menu at
|
|
// the coordinates specified by the NMITEMACTIVATE structure and
|
|
// determines which context menu to display based on whether the
|
|
// NMITEMACTIVATE struct contains an item identifier (it will be
|
|
// -1 if the user doesn't click on an item specifically) and the
|
|
// position in the custom action list of the item selected. The
|
|
// function will also call the appropriate command function
|
|
// as necessary once the user has made a context menu selection.
|
|
//
|
|
// Arguments: HINSTANCE hInstance - instance handle to load string resources
|
|
// HWND hDlg - window handle of the dialog containing the type combo
|
|
// HWND hListView - window handle to the list view control
|
|
// NMITEMACTIVATE* pItemActivate - contains item and location information
|
|
// used to display the context menu.
|
|
// BOOL bUseTunneling - whether this profile uses tunneling or not
|
|
// UINT uComboCtrlId - control id of the combo box containing
|
|
// the custom action type selection
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
// History: quintinb Created Header 02/26/00
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
void OnProcessCustomActionsContextMenu(HINSTANCE hInstance, HWND hDlg, HWND hListView,
|
|
NMITEMACTIVATE* pItemActivate, BOOL bUseTunneling, UINT uComboCtrlId)
|
|
{
|
|
MYDBGASSERT(hInstance);
|
|
MYDBGASSERT(hDlg);
|
|
MYDBGASSERT(hListView);
|
|
MYDBGASSERT(pItemActivate);
|
|
MYDBGASSERT(g_pCustomActionList);
|
|
|
|
UINT uMenuIdToDisplay = IDM_CA_ADD_ONLY;
|
|
int iDisableMoveUp;
|
|
int iDisableMoveDown;
|
|
int iDisableDeleteAndEdit;
|
|
|
|
if (hInstance && hDlg && hListView && pItemActivate && g_pCustomActionList)
|
|
{
|
|
//
|
|
// If we aren't directly on an item, then we will only
|
|
// display the Add item, set as default. If we are on
|
|
// an item then we will display Edit (default), add, delete,
|
|
// and the appropriate choices for moveup and movedown (one,
|
|
// both, none). We will add a separator between the moveup/movedown
|
|
// choices and the regular options if we have moveup or movedown.
|
|
//
|
|
|
|
if (-1 == pItemActivate->iItem)
|
|
{
|
|
//
|
|
// Then the user right clicked in the area of the control and not on a specific item.
|
|
// Thus we only need to show a menu with Add as the default item.
|
|
//
|
|
uMenuIdToDisplay = IDM_CA_ADD_ONLY;
|
|
}
|
|
else if (0 == pItemActivate->ptAction.y)
|
|
{
|
|
//
|
|
// When the user clicks on the column headers we always get back a y value of zero and
|
|
// a really large (probably meant to be negative) x value. Since this throws off where
|
|
// the menu shows up, lets just disable the context menu here.
|
|
//
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The user actually right clicked on an item and we need to figure out which menu
|
|
// to display.
|
|
//
|
|
|
|
MYDBGASSERT(0 != ListView_GetItemCount(hListView));
|
|
|
|
//
|
|
// Get the description and type of the item
|
|
//
|
|
|
|
int iIndex = pItemActivate->iItem;
|
|
CustomActionListItem Item;
|
|
|
|
ZeroMemory(&Item, sizeof(Item));
|
|
HRESULT hr = GetDescriptionAndTypeOfItem(hInstance, hDlg, hListView, uComboCtrlId, &Item, &iIndex, bUseTunneling);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Note that GetListPositionAndBuiltInState returns either -1 (0xFFFFFFF) or 0, thus making the
|
|
// bitwise ANDs below work out to the correct index.
|
|
//
|
|
hr = g_pCustomActionList->GetListPositionAndBuiltInState(hInstance, &Item, &iDisableMoveUp, &iDisableMoveDown, &iDisableDeleteAndEdit);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
const UINT c_ArrayOfContextMenuIds[8] = {IDM_CA_FULL, IDM_CA_ADD_MOVEUPORDOWN, IDM_CA_NO_DOWN, IDM_CA_ADD_MOVEUP,
|
|
IDM_CA_NO_UP, IDM_CA_ADD_MOVEDOWN, IDM_CA_NO_MOVE, IDM_CA_ADD_ONLY};
|
|
|
|
DWORD dwIndex = (iDisableMoveUp & 0x4) + (iDisableMoveDown & 0x2) + (iDisableDeleteAndEdit & 0x1);
|
|
uMenuIdToDisplay = c_ArrayOfContextMenuIds[dwIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that we have figured out what menu to use, go add and display it
|
|
//
|
|
|
|
HMENU hLoadedMenu;
|
|
HMENU hContextMenu;
|
|
POINT ptClientToScreen;
|
|
|
|
hLoadedMenu = LoadMenu(hInstance, MAKEINTRESOURCE(uMenuIdToDisplay));
|
|
|
|
if (hLoadedMenu)
|
|
{
|
|
hContextMenu = GetSubMenu(hLoadedMenu, 0);
|
|
|
|
if (hContextMenu)
|
|
{
|
|
CopyMemory(&ptClientToScreen, &(pItemActivate->ptAction), sizeof(POINT));
|
|
|
|
if (ClientToScreen(hListView, &ptClientToScreen))
|
|
{
|
|
int iMenuSelection = TrackPopupMenu(hContextMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
|
|
ptClientToScreen.x , ptClientToScreen.y, 0, hDlg, NULL);
|
|
|
|
switch(iMenuSelection)
|
|
{
|
|
case IDM_CA_ADD:
|
|
OnProcessCustomActionsAdd(hInstance, hDlg, hListView, bUseTunneling);
|
|
break;
|
|
case IDM_CA_EDIT:
|
|
OnProcessCustomActionsEdit(hInstance, hDlg, hListView, bUseTunneling);
|
|
break;
|
|
case IDM_CA_DELETE:
|
|
OnProcessCustomActionsDelete(hInstance, hDlg, hListView, bUseTunneling);
|
|
break;
|
|
case IDM_CA_MOVE_UP:
|
|
OnProcessCustomActionsMoveUp(hInstance, hDlg, hListView, bUseTunneling);
|
|
break;
|
|
case IDM_CA_MOVE_DOWN:
|
|
OnProcessCustomActionsMoveDown(hInstance, hDlg, hListView, bUseTunneling);
|
|
break;
|
|
default:
|
|
//
|
|
// Do nothing the user canceled the menu or an error occurred.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DestroyMenu(hLoadedMenu);
|
|
}
|
|
}
|
|
}
|