mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
827 lines
22 KiB
C++
827 lines
22 KiB
C++
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||
|
*
|
||
|
* The contents of this file are subject to the Netscape Public License
|
||
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
||
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
||
|
* http://www.mozilla.org/NPL/
|
||
|
*
|
||
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* NPL.
|
||
|
*
|
||
|
* The Initial Developer of this code under the NPL is Netscape
|
||
|
* Communications Corporation. Portions created by Netscape are
|
||
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||
|
* Reserved.
|
||
|
*/
|
||
|
|
||
|
// medit.cpp : implementation file
|
||
|
//
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
|
||
|
#include "medit.h"
|
||
|
#include "fmabstra.h"
|
||
|
#include "libevent.h"
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#undef THIS_FILE
|
||
|
static char BASED_CODE THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
#define EDIT_ID 25000
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CNetscapeEdit, CGenericEdit)
|
||
|
//{{AFX_MSG_MAP(CNetscapeEdit)
|
||
|
ON_WM_CHAR()
|
||
|
ON_WM_KEYDOWN()
|
||
|
ON_WM_KEYUP()
|
||
|
ON_WM_GETDLGCODE()
|
||
|
ON_WM_SETFOCUS()
|
||
|
ON_WM_KILLFOCUS()
|
||
|
ON_WM_CREATE()
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
IMPLEMENT_DYNAMIC(CNetscapeEdit, CGenericEdit)
|
||
|
|
||
|
|
||
|
#ifndef _AFXDLL
|
||
|
#define new DEBUG_NEW // MSVC Debugging new...goes to regular new in release mode
|
||
|
#endif
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
CNetscapeEdit::CNetscapeEdit()
|
||
|
{
|
||
|
m_Context = NULL;
|
||
|
m_Form = NULL;
|
||
|
m_Submit = FALSE;
|
||
|
m_callBase = FALSE;
|
||
|
#ifdef DEBUG
|
||
|
m_bOnCreateCalled = FALSE;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int CNetscapeEdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
m_bOnCreateCalled = TRUE;
|
||
|
#endif
|
||
|
|
||
|
if (CGenericEdit::OnCreate(lpCreateStruct) == -1)
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure we have a valid context
|
||
|
// We might nto be in a form so don't barf if we don't have a form
|
||
|
//
|
||
|
BOOL CNetscapeEdit::SetContext(MWContext * context, LO_FormElementStruct * form)
|
||
|
{
|
||
|
|
||
|
if(!context)
|
||
|
return(FALSE);
|
||
|
|
||
|
m_Context = context;
|
||
|
m_Form = form;
|
||
|
|
||
|
return(TRUE);
|
||
|
|
||
|
}
|
||
|
|
||
|
LO_FormElementStruct *CNetscapeEdit::GetFormElement()
|
||
|
{
|
||
|
return(m_Form);
|
||
|
}
|
||
|
|
||
|
|
||
|
UINT CNetscapeEdit::OnGetDlgCode()
|
||
|
{
|
||
|
return CEdit::OnGetDlgCode() | DLGC_WANTARROWS | DLGC_WANTALLKEYS;
|
||
|
}
|
||
|
|
||
|
#ifdef XP_WIN16
|
||
|
//
|
||
|
// The creator of this form element should have created a segment for
|
||
|
// us to live in before we got here. Tell Windows to use that
|
||
|
// segment rather than the application's so that we don't run out of
|
||
|
// DGROUP space
|
||
|
//
|
||
|
BOOL CNetscapeEdit::PreCreateWindow( CREATESTRUCT& cs )
|
||
|
{
|
||
|
ASSERT(m_hInstance);
|
||
|
|
||
|
if(!m_hInstance)
|
||
|
return(TRUE);
|
||
|
|
||
|
cs.hInstance = m_hInstance;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Called when the 'submit' event has been processed
|
||
|
//
|
||
|
static void
|
||
|
edit_submit_closure(MWContext * pContext, LO_Element * pEle, int32 event,
|
||
|
void * pObj, ETEventStatus status)
|
||
|
{
|
||
|
if(status != EVENT_OK || !pEle || !pContext)
|
||
|
return;
|
||
|
FE_SubmitInputElement(pContext, pEle);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the user has hit return load the url
|
||
|
// Otherwise pass the key on to the base library
|
||
|
//
|
||
|
void CNetscapeEdit::OnChar(UINT nChar, UINT nCnt, UINT nFlags)
|
||
|
{
|
||
|
BOOL bJsWantsEvent = m_Form->event_handler_present ||
|
||
|
LM_EventCaptureCheck(m_Context, EVENT_KEYDOWN | EVENT_KEYUP | EVENT_KEYPRESS);
|
||
|
|
||
|
// Check if we generated this event. If so, the base. If not, call JS
|
||
|
if (m_callBase || !bJsWantsEvent) {
|
||
|
|
||
|
if(nChar == '\r') { // the user hit return
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
if(!m_bOnCreateCalled) {
|
||
|
TRACE("Bug 88020 still present\n");
|
||
|
#if defined (DEBUG_blythe) || defined(DEBUG_ftang) || defined(DEBUG_bstell) || defined(DEBUG_nhotta) || defined(DEBUG_jliu)
|
||
|
ASSERT(0);
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if(m_Submit) { // if we are the only element, return == submit
|
||
|
|
||
|
// if the text is different throw out the old text and add the
|
||
|
// new text into the layout structure
|
||
|
UpdateAndCheckForChange();
|
||
|
|
||
|
// we are about to submit so tell libmocha about it first
|
||
|
// do the submit in our closure if libmocha says its OK
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_SUBMIT;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *)m_Form, event,
|
||
|
edit_submit_closure, this);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
// we aren't auto-submitting, but the user still hit return
|
||
|
// they prolly mean that we should check to see if we should
|
||
|
// send a change event
|
||
|
if(!(GetStyle() & ES_MULTILINE))
|
||
|
UpdateAndCheckForChange();
|
||
|
|
||
|
}
|
||
|
|
||
|
} // wasn't the return key
|
||
|
|
||
|
//#ifdef NO_TAB_NAVIGATION
|
||
|
/*
|
||
|
// let the CGenericView to handle the Tab, to step through Form elements
|
||
|
// and links, even links inside a form.
|
||
|
if(nChar == VK_TAB) {
|
||
|
// if the high order bit is set (i.e. its negative) the shift
|
||
|
// key is being held down
|
||
|
BOOL bShift = GetKeyState(VK_SHIFT) < 0 ? TRUE : FALSE;
|
||
|
|
||
|
// Determine our form element.
|
||
|
if(m_Context) {
|
||
|
CFormElement *pMe = CFormElement::GetFormElement(ABSTRACTCX(m_Context), m_Form);
|
||
|
if(pMe) {
|
||
|
// We're handling tabbing between form elements.
|
||
|
pMe->Tab(bShift);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
CEdit::OnChar(nChar, nCnt, nFlags);
|
||
|
*/
|
||
|
//#endif /* NO_TAB_NAVIGATION */
|
||
|
|
||
|
CEdit::OnChar(nChar, nCnt, nFlags);
|
||
|
//If m_callBase is set, this event is a callback from a JS event and
|
||
|
//we have to update the value of the edit field to reflect new values.
|
||
|
if (bJsWantsEvent) {
|
||
|
UpdateEditField();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Give the event to JS. They'll call the base when they get back.
|
||
|
//
|
||
|
// Grab key events for this layer's parent.
|
||
|
CL_GrabKeyEvents(m_Context->compositor, CL_GetLayerParent(m_Form->layer));
|
||
|
|
||
|
/*
|
||
|
* If there's a compositor and someone has keyboard focus.
|
||
|
* Note that if noone has event focus, we don't really need
|
||
|
* to dispatch the event.
|
||
|
*/
|
||
|
if (m_Context->compositor &&
|
||
|
!CL_IsKeyEventGrabber(m_Context->compositor, NULL)) {
|
||
|
CL_Event event;
|
||
|
fe_EventStruct fe_event;
|
||
|
|
||
|
fe_event.fe_modifiers = nCnt;
|
||
|
fe_event.nChar = nChar;
|
||
|
fe_event.uFlags = nFlags;
|
||
|
fe_event.x = 1;
|
||
|
|
||
|
event.type = CL_EVENT_KEY_DOWN;
|
||
|
event.which = nChar;
|
||
|
event.fe_event = (void *)&fe_event;
|
||
|
event.fe_event_size = sizeof(fe_EventStruct);
|
||
|
event.modifiers = (GetKeyState(VK_SHIFT) < 0 ? EVENT_SHIFT_MASK : 0)
|
||
|
| (GetKeyState(VK_CONTROL) < 0 ? EVENT_CONTROL_MASK : 0)
|
||
|
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
|
||
|
event.x = CL_GetLayerXOrigin(m_Form->layer);
|
||
|
event.y = CL_GetLayerYOrigin(m_Form->layer);
|
||
|
event.data = nFlags>>14 & 1;//Bit represeting key repetition
|
||
|
|
||
|
CL_DispatchEvent(m_Context->compositor, &event);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We just got focus --- maybe libmocha cares
|
||
|
//
|
||
|
void CNetscapeEdit::OnSetFocus(CWnd * pWnd)
|
||
|
{
|
||
|
CEdit::OnSetFocus(pWnd);
|
||
|
if(m_Context && m_Form) {
|
||
|
|
||
|
// set tab_focus to this element
|
||
|
CWinCX *pWinCX = WINCX(m_Context);
|
||
|
|
||
|
// if ((LO_Element *)m_Form == pWinCX->getLastTabFocusElement())
|
||
|
// return;
|
||
|
|
||
|
pWinCX->setFormElementTabFocus( (LO_Element *)m_Form );
|
||
|
CL_GrabKeyEvents(m_Context->compositor, CL_GetLayerParent(m_Form->layer));
|
||
|
|
||
|
// send the event to libmocha --- do any further processing
|
||
|
// in our closure routine
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_FOCUS;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *)m_Form, event,
|
||
|
NULL, this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
lo_FormElementTextData *CNetscapeEdit::GetTextData() {
|
||
|
lo_FormElementTextData *pRetval = NULL;
|
||
|
|
||
|
if(m_Form && m_Form->element_data) {
|
||
|
switch(m_Form->element_data->type) {
|
||
|
case FORM_TYPE_READONLY:
|
||
|
break;
|
||
|
default:
|
||
|
pRetval = &(m_Form->element_data->ele_text);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(pRetval);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// We just lost focus --- maybe libmocha cares
|
||
|
//
|
||
|
// If the current value is different from the value that we have previously
|
||
|
// stored call SendOnChange() for this element before calling BlurElement();
|
||
|
//
|
||
|
void CNetscapeEdit::OnKillFocus(CWnd * pWnd)
|
||
|
{
|
||
|
// Call base class and be done with the message processing first
|
||
|
CEdit::OnKillFocus(pWnd);
|
||
|
|
||
|
// if the text is different throw out the old text and add the
|
||
|
// new text into the layout structure
|
||
|
UpdateAndCheckForChange();
|
||
|
|
||
|
// Send the blur message.
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_BLUR;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event,
|
||
|
NULL, this);
|
||
|
|
||
|
}
|
||
|
|
||
|
void CNetscapeEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
|
{
|
||
|
if(m_Form->event_handler_present ||
|
||
|
LM_EventCaptureCheck(m_Context, EVENT_KEYDOWN | EVENT_KEYUP | EVENT_KEYPRESS)) {
|
||
|
UpdateEditField();
|
||
|
}
|
||
|
|
||
|
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
|
||
|
}
|
||
|
|
||
|
void CNetscapeEdit::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
|
{
|
||
|
BOOL bJsWantsEvent = m_Form->event_handler_present ||
|
||
|
LM_EventCaptureCheck(m_Context, EVENT_KEYDOWN | EVENT_KEYUP | EVENT_KEYPRESS);
|
||
|
|
||
|
// Check if we generated this event. If so, the base. If not, call JS
|
||
|
if (m_callBase || !bJsWantsEvent) {
|
||
|
CEdit::OnKeyUp(nChar, nRepCnt, nFlags);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Don't continue if this context is destroyed.
|
||
|
if(!m_Context || ABSTRACTCX(m_Context)->IsDestroyed()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Grab key events for this layer's parent.
|
||
|
CL_GrabKeyEvents(m_Context->compositor, CL_GetLayerParent(m_Form->layer));
|
||
|
|
||
|
/*
|
||
|
* If there's a compositor and someone has keyboard focus.
|
||
|
* Note that if noone has event focus, we don't really need
|
||
|
* to dispatch the event.
|
||
|
*/
|
||
|
if (m_Context->compositor &&
|
||
|
!CL_IsKeyEventGrabber(m_Context->compositor, NULL)) {
|
||
|
CL_Event event;
|
||
|
fe_EventStruct fe_event;
|
||
|
BYTE kbstate[256];
|
||
|
WORD asciiChar = 0;
|
||
|
|
||
|
GetKeyboardState(kbstate);
|
||
|
#ifdef WIN32
|
||
|
ToAscii(nChar, nFlags & 0xff, kbstate, &asciiChar, 0);
|
||
|
#else
|
||
|
ToAscii(nChar, nFlags & 0xff, kbstate, (DWORD*)&asciiChar, 0);
|
||
|
#endif
|
||
|
|
||
|
fe_event.fe_modifiers = nRepCnt;
|
||
|
fe_event.nChar = nChar;
|
||
|
fe_event.uFlags = nFlags;
|
||
|
fe_event.x = 1;
|
||
|
|
||
|
event.type = CL_EVENT_KEY_UP;
|
||
|
event.fe_event = (void *)&fe_event;
|
||
|
event.fe_event_size = sizeof(fe_EventStruct);
|
||
|
event.which = asciiChar;
|
||
|
event.modifiers = (GetKeyState(VK_SHIFT) < 0 ? EVENT_SHIFT_MASK : 0)
|
||
|
| (GetKeyState(VK_CONTROL) < 0 ? EVENT_CONTROL_MASK : 0)
|
||
|
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
|
||
|
event.x = CL_GetLayerXOrigin(m_Form->layer);
|
||
|
event.y = CL_GetLayerYOrigin(m_Form->layer);
|
||
|
|
||
|
CL_DispatchEvent(m_Context->compositor, &event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This function returns true if it has to update the value of the field
|
||
|
// and false if it does not.
|
||
|
XP_Bool CNetscapeEdit::UpdateEditField()
|
||
|
{
|
||
|
// find out what the current text is
|
||
|
CString csText;
|
||
|
GetWindowText(csText);
|
||
|
|
||
|
// if the text is different throw out the old text and add the
|
||
|
// new text into the layout structure
|
||
|
lo_FormElementTextData * textData = GetTextData();
|
||
|
|
||
|
if(!textData)
|
||
|
return FALSE;
|
||
|
|
||
|
//
|
||
|
// This if() statement has two parts. If there used to not be
|
||
|
// any text and there is some now we will send a change message
|
||
|
// Or, if there already was text but it was different we also
|
||
|
// want to send a change event
|
||
|
if((!textData->current_text && !csText.IsEmpty()) ||
|
||
|
(textData->current_text && csText != (char *) textData->current_text))
|
||
|
{
|
||
|
LO_UpdateTextData(textData, (const char *)csText);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// See if our value has changed since the last time we checked.
|
||
|
// If it has, send a change event to Mocha
|
||
|
//
|
||
|
void CNetscapeEdit::UpdateAndCheckForChange()
|
||
|
{
|
||
|
|
||
|
if(UpdateEditField())
|
||
|
{
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_CHANGE;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event,
|
||
|
NULL, this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CNetscapeEdit::OnEditKeyEvent(CL_EventType type, UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||
|
m_callBase = TRUE;
|
||
|
if (type == CL_EVENT_KEY_DOWN)
|
||
|
SendMessage(WM_CHAR, nChar, MAKELONG(nRepCnt, nFlags));
|
||
|
else if (type == CL_EVENT_KEY_UP)
|
||
|
SendMessage(WM_KEYUP, nChar, MAKELONG(nRepCnt, nFlags));
|
||
|
m_callBase = FALSE;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CMochaListBox, CListBox)
|
||
|
//{{AFX_MSG_MAP(CMochaListBox)
|
||
|
ON_WM_SETFOCUS()
|
||
|
ON_WM_KILLFOCUS()
|
||
|
ON_WM_CHAR()
|
||
|
#if defined(MSVC4)
|
||
|
ON_CONTROL_REFLECT(LBN_SELCHANGE, OnSelChange)
|
||
|
#endif // MSVC4
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
void CMochaListBox::SetContext(MWContext * context, LO_FormElementStruct * form)
|
||
|
{
|
||
|
m_Context = context;
|
||
|
m_Form = form;
|
||
|
}
|
||
|
|
||
|
void CMochaListBox::OnSetFocus(CWnd * pWnd)
|
||
|
{
|
||
|
CListBox::OnSetFocus(pWnd);
|
||
|
|
||
|
// set tab_focus to this element
|
||
|
CWinCX *pWinCX = WINCX(m_Context);
|
||
|
// if ((LO_Element *)m_Form == pWinCX->getLastTabFocusElement())
|
||
|
// return;
|
||
|
pWinCX->setFormElementTabFocus( (LO_Element *)m_Form );
|
||
|
CL_GrabKeyEvents(m_Context->compositor, CL_GetLayerParent(m_Form->layer));
|
||
|
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_FOCUS;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event,
|
||
|
NULL, this);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the current value is different from the value that we have previously
|
||
|
// stored call SendOnChange() for this element before calling BlurElement();
|
||
|
//
|
||
|
void CMochaListBox::CheckForChange()
|
||
|
{
|
||
|
|
||
|
// get the base form element
|
||
|
lo_FormElementSelectData * pSelectData = NULL;
|
||
|
pSelectData = (lo_FormElementSelectData *) m_Form->element_data;
|
||
|
if(!pSelectData)
|
||
|
return;
|
||
|
|
||
|
// get the option state
|
||
|
lo_FormElementOptionData * pOptionData = NULL;
|
||
|
pOptionData = (lo_FormElementOptionData *) pSelectData->options;
|
||
|
if(!pOptionData)
|
||
|
return;
|
||
|
|
||
|
BOOL bChanged = FALSE;
|
||
|
for(int i = 0; i < pSelectData->option_cnt; i++) {
|
||
|
|
||
|
int iIsSelected = GetSel(i);
|
||
|
|
||
|
// see if we have found a selection which is not the current
|
||
|
// selection
|
||
|
if(pOptionData[i].selected && (iIsSelected < 1)) {
|
||
|
pOptionData[i].selected = FALSE;
|
||
|
bChanged = TRUE;
|
||
|
}
|
||
|
|
||
|
// see if item #i has gone from being unselected to being selected
|
||
|
if(!pOptionData[i].selected && (iIsSelected > 0)) {
|
||
|
pOptionData[i].selected = TRUE;
|
||
|
bChanged = TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// only send change message if things have changed
|
||
|
if(bChanged) {
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_CHANGE;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event,
|
||
|
NULL, this);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We just lost focus --- maybe libmocha cares
|
||
|
//
|
||
|
void CMochaListBox::OnKillFocus(CWnd * pWnd)
|
||
|
{
|
||
|
// let MFC do its thing
|
||
|
CListBox::OnKillFocus(pWnd);
|
||
|
|
||
|
// see if we have changed and fire a change event if necessary
|
||
|
CheckForChange();
|
||
|
|
||
|
// we have lost focus so whether the value changed or not we
|
||
|
// should tell the mocha library
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_BLUR;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event, NULL, this);
|
||
|
}
|
||
|
|
||
|
#if defined(MSVC4)
|
||
|
void CMochaListBox::OnSelChange()
|
||
|
{
|
||
|
// The OnKillFocus() handler takes care of leaving a control, but Mocha
|
||
|
// also wants to know about a selection change in a combobox or listbox.
|
||
|
CheckForChange();
|
||
|
}
|
||
|
#else
|
||
|
// In Win16 and MFC prior to 4.0, only the parent of a control could get
|
||
|
// notification messages.
|
||
|
BOOL CMochaListBox::OnChildNotify(UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||
|
{
|
||
|
if (nMsg == WM_COMMAND) {
|
||
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE) {
|
||
|
CheckForChange();
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return CListBox::OnChildNotify(nMsg, wParam, lParam, pResult);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CMochaComboBox, CComboBox)
|
||
|
//{{AFX_MSG_MAP(CMochaComboBox)
|
||
|
ON_WM_SETFOCUS()
|
||
|
ON_WM_KILLFOCUS()
|
||
|
ON_WM_CHAR()
|
||
|
#if defined(MSVC4)
|
||
|
ON_CONTROL_REFLECT(CBN_SELCHANGE, OnSelChange)
|
||
|
#endif // MSVC4
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
void CMochaComboBox::SetContext(MWContext * context, LO_FormElementStruct * form)
|
||
|
{
|
||
|
m_Context = context;
|
||
|
m_Form = form;
|
||
|
}
|
||
|
|
||
|
void CMochaComboBox::OnSetFocus(CWnd * pWnd)
|
||
|
{
|
||
|
CComboBox::OnSetFocus(pWnd);
|
||
|
|
||
|
// set tab_focus to this element
|
||
|
CWinCX *pWinCX = WINCX(m_Context);
|
||
|
// if ((LO_Element *)m_Form == pWinCX->getLastTabFocusElement())
|
||
|
// return;
|
||
|
pWinCX->setFormElementTabFocus( (LO_Element *)m_Form );
|
||
|
CL_GrabKeyEvents(m_Context->compositor, CL_GetLayerParent(m_Form->layer));
|
||
|
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_FOCUS;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event, NULL, this);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the current value is different from the value that we have previously
|
||
|
// stored call SendOnChange() for this element before calling BlurElement();
|
||
|
//
|
||
|
void CMochaComboBox::CheckForChange()
|
||
|
{
|
||
|
|
||
|
// get the base form element
|
||
|
lo_FormElementSelectData * pSelectData = NULL;
|
||
|
pSelectData = (lo_FormElementSelectData *) m_Form->element_data;
|
||
|
if(!pSelectData)
|
||
|
return;
|
||
|
|
||
|
// get the option state
|
||
|
lo_FormElementOptionData * pOptionData = NULL;
|
||
|
pOptionData = (lo_FormElementOptionData *) pSelectData->options;
|
||
|
if(!pOptionData)
|
||
|
return;
|
||
|
|
||
|
// index of single selection
|
||
|
int iCurSelection = GetCurSel();
|
||
|
if(iCurSelection == LB_ERR)
|
||
|
return;
|
||
|
|
||
|
BOOL bChanged = FALSE;
|
||
|
for(int i = 0; i < pSelectData->option_cnt; i++) {
|
||
|
|
||
|
// see if we have found a selection which is not the current
|
||
|
// selection
|
||
|
if(pOptionData[i].selected && i != iCurSelection) {
|
||
|
pOptionData[i].selected = FALSE;
|
||
|
bChanged = TRUE;
|
||
|
}
|
||
|
|
||
|
// see if item #i has gone from being unselected to being selected
|
||
|
if(!pOptionData[i].selected && i == iCurSelection) {
|
||
|
pOptionData[i].selected = TRUE;
|
||
|
bChanged = TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// only send change message if things have changed
|
||
|
if(bChanged) {
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_CHANGE;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event,
|
||
|
NULL, this);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We just lost focus --- maybe libmocha cares
|
||
|
//
|
||
|
void CMochaComboBox::OnKillFocus(CWnd * pWnd)
|
||
|
{
|
||
|
CComboBox::OnKillFocus(pWnd);
|
||
|
|
||
|
CheckForChange();
|
||
|
|
||
|
// we have lost focus so whether the value changed or not we should tell the
|
||
|
// mocha library
|
||
|
JSEvent *event;
|
||
|
event = XP_NEW_ZAP(JSEvent);
|
||
|
event->type = EVENT_BLUR;
|
||
|
event->layer_id = m_Form->layer_id;
|
||
|
|
||
|
ET_SendEvent(m_Context, (LO_Element *) m_Form, event, NULL, this);
|
||
|
}
|
||
|
|
||
|
#if defined(MSVC4)
|
||
|
void CMochaComboBox::OnSelChange()
|
||
|
{
|
||
|
// The OnKillFocus() handler takes care of leaving a control, but Mocha
|
||
|
// also wants to know about a selection change in a combobox or listbox.
|
||
|
CheckForChange();
|
||
|
}
|
||
|
#else
|
||
|
// In Win16 and MFC prior to 4.0, only the parent of a control could get
|
||
|
// notification messages.
|
||
|
BOOL CMochaComboBox::OnChildNotify(UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
||
|
{
|
||
|
if (nMsg == WM_COMMAND) {
|
||
|
if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) {
|
||
|
CheckForChange();
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return CComboBox::OnChildNotify(nMsg, wParam, lParam, pResult);
|
||
|
}
|
||
|
#endif // MSVC4
|
||
|
|
||
|
|
||
|
BOOL FirstCharMatch( char * searchFor, lo_FormElementSelectData *pSelectData, int oldSel, int *newSel )
|
||
|
{
|
||
|
char *pCurrent = NULL;
|
||
|
int ii;
|
||
|
|
||
|
if( searchFor == NULL )
|
||
|
return( FALSE);
|
||
|
|
||
|
|
||
|
// get the option state
|
||
|
lo_FormElementOptionData * pOptionData = NULL;
|
||
|
pOptionData = (lo_FormElementOptionData *) pSelectData->options;
|
||
|
if(!pOptionData)
|
||
|
return(FALSE);
|
||
|
|
||
|
// index of single selection
|
||
|
BOOL bChanged = FALSE;
|
||
|
ii = oldSel + 1; // start with current selection.
|
||
|
if( ii == pSelectData->option_cnt )
|
||
|
ii = 0; // fold back to the begining of the list.
|
||
|
while( ! bChanged && ii != oldSel ) {
|
||
|
|
||
|
pCurrent = (char *)pOptionData[ii].text_value;
|
||
|
if( pCurrent && (toupper(*pCurrent) == toupper(* searchFor)) ) { // todo multi byte
|
||
|
bChanged = TRUE;
|
||
|
*newSel = ii;
|
||
|
break; // ii is the index to select.
|
||
|
}
|
||
|
|
||
|
ii++;
|
||
|
if( ii == pSelectData->option_cnt )
|
||
|
ii = 0; // fold back to the begining of the list.
|
||
|
}
|
||
|
|
||
|
return( bChanged );
|
||
|
}
|
||
|
|
||
|
void CMochaComboBox::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
|
{
|
||
|
CComboBox::OnChar(nChar, nRepCnt, nFlags); // call base class
|
||
|
|
||
|
// refer to void CFormSelectOne::CreateWidget() in fmdelone.cpp
|
||
|
// combo boxes are created with CBS_OWNERDRAWFIXED, for i18n support.
|
||
|
// First-char-searching in list doesn't work any more.
|
||
|
// Need to do it here.
|
||
|
|
||
|
int newSel;
|
||
|
char searchFor[2];
|
||
|
searchFor[0] = nChar;
|
||
|
searchFor[1] = '\0';
|
||
|
|
||
|
// get the base form element
|
||
|
lo_FormElementSelectData * pSelectData = NULL;
|
||
|
pSelectData = (lo_FormElementSelectData *) m_Form->element_data;
|
||
|
if(!pSelectData)
|
||
|
return;
|
||
|
|
||
|
int iCurSelection = GetCurSel();
|
||
|
if(iCurSelection == LB_ERR)
|
||
|
return;
|
||
|
|
||
|
BOOL changed = FirstCharMatch( searchFor, pSelectData, iCurSelection, &newSel );
|
||
|
if( changed )
|
||
|
CComboBox::SetCurSel( newSel );
|
||
|
|
||
|
}
|
||
|
|
||
|
void CMochaListBox::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
|
{
|
||
|
|
||
|
CListBox::OnChar(nChar, nRepCnt, nFlags);
|
||
|
int newSel;
|
||
|
char searchFor[2];
|
||
|
searchFor[0] = nChar;
|
||
|
searchFor[1] = '\0';
|
||
|
|
||
|
// get the base form element
|
||
|
lo_FormElementSelectData * pSelectData = NULL;
|
||
|
pSelectData = (lo_FormElementSelectData *) m_Form->element_data;
|
||
|
if(!pSelectData)
|
||
|
return;
|
||
|
|
||
|
int iCurSelection = GetCurSel();
|
||
|
if(iCurSelection == LB_ERR)
|
||
|
return;
|
||
|
|
||
|
BOOL changed = FirstCharMatch( searchFor, pSelectData, iCurSelection, &newSel );
|
||
|
if( changed ) {
|
||
|
CListBox::SetSel( -1, FALSE ); // clear all selections.
|
||
|
CListBox::SetSel( newSel );
|
||
|
}
|
||
|
|
||
|
}
|