// InPlaceList.cpp : implementation file
//
// Written by Chris Maunder <cmaunder@mail.com>
// Copyright (c) 1998-2000. All Rights Reserved.
//
// The code contained in this file is based on the original
// CInPlaceList from http://www.codeguru.com/listview
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included. If
// the source code in this file is used in any commercial application
// then acknowledgement must be made to the author of this file
// (in whatever form you wish).
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage/loss of business that
// this product may cause.
//
// Expect bugs!
//
// Please use and enjoy. Please let me know of any bugs/mods/improvements
// that you have found/implemented and I will fix/incorporate them into this
// file.
//
// 6 Aug 1998 - Added CComboEdit to subclass the edit control - code provided by
// Roelf Werkman <rdw@inn.nl>. Added nID to the constructor param list.
// 29 Nov 1998 - bug fix in onkeydown (Markus Irtenkauf)
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "InPlaceList.h"
#include "GridCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CComboEdit
CComboEdit::CComboEdit()
{
}
CComboEdit::~CComboEdit()
{
}
// Stoopid win95 accelerator key problem workaround - Matt Weagle.
BOOL CComboEdit::PreTranslateMessage(MSG* pMsg)
{
// Make sure that the keystrokes continue to the appropriate handlers
if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP)
{
::TranslateMessage(pMsg);
::DispatchMessage(pMsg);
return TRUE;
}
// Catch the Alt key so we don't choke if focus is going to an owner drawn button
if (pMsg->message == WM_SYSCHAR)
return TRUE;
return CEdit::PreTranslateMessage(pMsg);
}
BEGIN_MESSAGE_MAP(CComboEdit, CEdit)
//{{AFX_MSG_MAP(CComboEdit)
ON_WM_KILLFOCUS()
ON_WM_KEYDOWN()
ON_WM_KEYUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CComboEdit message handlers
void CComboEdit::OnKillFocus(CWnd* pNewWnd)
{
CEdit::OnKillFocus(pNewWnd);
CInPlaceList* pOwner = (CInPlaceList*) GetOwner(); // This MUST be a CInPlaceList
if (pOwner)
pOwner->EndEdit();
}
void CComboEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if ((nChar == VK_PRIOR || nChar == VK_NEXT ||
nChar == VK_DOWN || nChar == VK_UP ||
nChar == VK_RIGHT || nChar == VK_LEFT) &&
(GetKeyState(VK_CONTROL) < 0 && GetDlgCtrlID() == IDC_COMBOEDIT))
{
CWnd* pOwner = GetOwner();
if (pOwner)
pOwner->SendMessage(WM_KEYDOWN, nChar, nRepCnt+ (((DWORD)nFlags)<<16));
return;
}
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CComboEdit::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_ESCAPE)
{
CWnd* pOwner = GetOwner();
if (pOwner)
pOwner->SendMessage(WM_KEYUP, nChar, nRepCnt + (((DWORD)nFlags)<<16));
return;
}
if (nChar == VK_TAB || nChar == VK_RETURN || nChar == VK_ESCAPE)
{
CWnd* pOwner = GetOwner();
if (pOwner)
pOwner->SendMessage(WM_KEYUP, nChar, nRepCnt + (((DWORD)nFlags)<<16));
return;
}
CEdit::OnKeyUp(nChar, nRepCnt, nFlags);
}
/////////////////////////////////////////////////////////////////////////////
// CInPlaceList
CInPlaceList::CInPlaceList(CWnd* pParent, CRect& rect, DWORD dwStyle, UINT nID,
int nRow, int nColumn,
CStringArray& Items, CString sInitText,
UINT nFirstChar)
{
m_nNumLines = 4;
m_sInitText = sInitText;
m_nRow = nRow;
m_nCol = nColumn;
m_nLastChar = 0;
m_bExitOnArrows = FALSE; //(nFirstChar != VK_LBUTTON); // If mouse click brought us here,
// Create the combobox
DWORD dwComboStyle = WS_BORDER|WS_CHILD|WS_VISIBLE|WS_VSCROLL|
CBS_AUTOHSCROLL | dwStyle;
int nHeight = rect.Height();
rect.bottom = rect.bottom + m_nNumLines*nHeight + ::GetSystemMetrics(SM_CYHSCROLL);
if (!Create(dwComboStyle, rect, pParent, nID)) return;
// Add the strings
for (int i = 0; i < Items.GetSize(); i++)
AddString(Items[i]);
// Get the maximum width of the text strings
int nMaxLength = 0;
CClientDC dc(GetParent());
CFont* pOldFont = dc.SelectObject(pParent->GetFont());
for (i = 0; i < Items.GetSize(); i++)
nMaxLength = max(nMaxLength, dc.GetTextExtent(Items[i]).cx);
nMaxLength += (::GetSystemMetrics(SM_CXVSCROLL) + dc.GetTextExtent(_T(" ")).cx*2);
dc.SelectObject(pOldFont);
if (nMaxLength > rect.Width())
rect.right = rect.left + nMaxLength;
// Resize the edit window and the drop down window
MoveWindow(rect);
SetFont(pParent->GetFont());
SetItemHeight(-1, nHeight);
SetDroppedWidth(nMaxLength);
SetHorizontalExtent(0); // no horz scrolling
// Set the initial text to m_sInitText
if (SelectString(-1, m_sInitText) == CB_ERR)
SetWindowText(m_sInitText); // No text selected, so restore what was there before
// Subclass the combobox edit control if style includes CBS_DROPDOWN
if ((dwStyle & CBS_DROPDOWNLIST) != CBS_DROPDOWNLIST)
{
m_edit.SubclassDlgItem(IDC_COMBOEDIT, this);
SetFocus();
switch (nFirstChar)
{
case VK_LBUTTON:
case VK_RETURN: m_edit.SetSel((int)_tcslen(m_sInitText), -1); return;
case VK_BACK: m_edit.SetSel((int)_tcslen(m_sInitText), -1); break;
case VK_DOWN:
case VK_UP:
case VK_RIGHT:
case VK_LEFT:
case VK_NEXT:
case VK_PRIOR:
case VK_HOME:
case VK_END: m_edit.SetSel(0,-1); return;
default: m_edit.SetSel(0,-1);
}
SendMessage(WM_CHAR, nFirstChar);
}
else
SetFocus();
}
CInPlaceList::~CInPlaceList()
{
}
void CInPlaceList::EndEdit()
{
CString str;
GetWindowText(str);
// Send Notification to parent
GV_DISPINFO dispinfo;
dispinfo.hdr.hwndFrom = GetSafeHwnd();
dispinfo.hdr.idFrom = GetDlgCtrlID();
dispinfo.hdr.code = GVN_ENDLABELEDIT;
dispinfo.item.mask = LVIF_TEXT|LVIF_PARAM;
dispinfo.item.row = m_nRow;
dispinfo.item.col = m_nCol;
dispinfo.item.strText = str;
dispinfo.item.lParam = (LPARAM) m_nLastChar;
CWnd* pOwner = GetOwner();
if (IsWindow(pOwner->GetSafeHwnd()))
pOwner->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&dispinfo );
// Close this window (PostNcDestroy will delete this)
PostMessage(WM_CLOSE, 0, 0);
}
void CInPlaceList::PostNcDestroy()
{
CComboBox::PostNcDestroy();
delete this;
}
BEGIN_MESSAGE_MAP(CInPlaceList, CComboBox)
//{{AFX_MSG_MAP(CInPlaceList)
ON_WM_KILLFOCUS()
ON_WM_KEYDOWN()
ON_WM_KEYUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CInPlaceList message handlers
void CInPlaceList::OnKillFocus(CWnd* pNewWnd)
{
CComboBox::OnKillFocus(pNewWnd);
if (GetSafeHwnd() == pNewWnd->GetSafeHwnd())
return;
// Only end editing on change of focus if we're using the CBS_DROPDOWNLIST style
if ((GetStyle() & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST)
EndEdit();
}
// If an arrow key (or associated) is pressed, then exit if
// a) The Ctrl key was down, or
// b) m_bExitOnArrows == TRUE
void CInPlaceList::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if ((nChar == VK_PRIOR || nChar == VK_NEXT ||
nChar == VK_DOWN || nChar == VK_UP ||
nChar == VK_RIGHT || nChar == VK_LEFT) &&
(m_bExitOnArrows || GetKeyState(VK_CONTROL) < 0))
{
m_nLastChar = nChar;
GetParent()->SetFocus();
return;
}
CComboBox::OnKeyDown(nChar, nRepCnt, nFlags);
}
// Need to keep a lookout for Tabs, Esc and Returns.
void CInPlaceList::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_ESCAPE)
SetWindowText(m_sInitText); // restore previous text
if (nChar == VK_TAB || nChar == VK_RETURN || nChar == VK_ESCAPE)
{
m_nLastChar = nChar;
GetParent()->SetFocus(); // This will destroy this window
return;
}
CComboBox::OnKeyUp(nChar, nRepCnt, nFlags);
}