pjs/cmd/winfe/button.cpp

681 строка
18 KiB
C++
Executable File

/* -*- 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.
*/
// button.cpp : implementation file
//
#include "stdafx.h"
#include "fmabstra.h"
#include "button.h"
#include "libevent.h"
#include <windowsx.h>
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
#ifndef _AFXDLL
#define new DEBUG_NEW // MSVC Debugging new...goes to regular new in release mode
#endif
#ifndef _WIN32
#define GET_WM_COMMAND_CMD(wp, lp) ((UINT)HIWORD(lp))
#endif
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CNetscapeButton dialog
IMPLEMENT_DYNAMIC(CNetscapeButton, CButton)
CNetscapeButton::CNetscapeButton(MWContext * context, LO_FormElementStruct * form, CWnd* pParent)
{
m_Context = context;
m_Form = form;
m_bDepressed = FALSE;
m_pwndEdit = NULL;
m_pPaneCX = PANECX(m_Context);
m_callBase = FALSE;
}
BEGIN_MESSAGE_MAP(CNetscapeButton, CButton)
ON_WM_CHAR()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_LBUTTONDBLCLK()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_RBUTTONDBLCLK()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CNetscapeButton message handlers
/////////////////////////////////////////////////////////////////////////////
//
// Called when the 'submit' event has been processed
//
static void
button_submit_closure(MWContext * pContext, LO_Element * pEle, int32 event,
void * pObj, ETEventStatus status)
{
if(status != EVENT_OK || !pEle || !pContext)
return;
FE_SubmitInputElement(pContext, pEle);
}
//
// Called when the 'click' event has been processed
//
static void
button_click_closure(MWContext * pContext, LO_Element * pEle, int32 event,
void * pObj, ETEventStatus status)
{
// make sure every thing is OK
if(status == EVENT_PANIC || !pEle || !pContext)
return;
CNetscapeButton * pButton = (CNetscapeButton *) pObj;
LO_FormElementStruct * pForm = (LO_FormElementStruct *) pEle;
lo_FormElementMinimalData * min_data;
lo_FormElementToggleData * tog_data;
min_data = (lo_FormElementMinimalData *) pForm->element_data;
if(!min_data)
return;
if(status == EVENT_OK) {
switch(min_data->type) {
case FORM_TYPE_SUBMIT:
// send the event to libmocha --- do any further processing
// in our closure routine
JSEvent *event;
event = XP_NEW_ZAP(JSEvent);
event->type = EVENT_SUBMIT;
event->layer_id = pForm->layer_id;
ET_SendEvent(pContext, pEle, event,
button_submit_closure, pObj);
break;
case FORM_TYPE_RESET:
// libmocha has said it was OK to allow the reset to happen
// tell layout to go forward with the operation
// the world may not be safe after this so return
LO_ResetForm(pContext, pForm);
return;
default:
break;
}
}
else {
// Undo state operations.
switch(min_data->type) {
case FORM_TYPE_RADIO:
if(pButton->m_pLastSelectedRadio) {
tog_data = (lo_FormElementToggleData *)
pButton->m_pLastSelectedRadio->element_data;
if(!tog_data->toggled)
LO_FormRadioSet(ABSTRACTCX(pContext)->GetDocumentContext(),
pButton->m_pLastSelectedRadio);
CFormElement *pRadioButton = CFormElement::GetFormElement(ABSTRACTCX(pContext),
pButton->m_pLastSelectedRadio);
if(pRadioButton) {
pRadioButton->SetFormElementToggle(tog_data->toggled);
}
}
break;
case FORM_TYPE_CHECKBOX:
tog_data = (lo_FormElementToggleData *) pForm->element_data;
if(tog_data->toggled)
tog_data->toggled = FALSE;
else
tog_data->toggled = TRUE;
if((pButton->GetCheck() ? TRUE : FALSE) != tog_data->toggled)
pButton->SetCheck(tog_data->toggled);
break;
default:
break;
}
}
}
// NFlags as per OnLButtonUp
//
//
// for FORM_TYPE_CHECKBOX, there are 3 places in this file call Click().
// 1. OnChar for space-key
// 2. OnButtonEvent, initiated by java script?
// 3. OnChildNotify maybe not necesary to handle this msg.
//
// We taggled state for 1 and 2.We do NOT taggle for 3. It always comes
// with 1 or 2. It is not clear whether item 3 is redundent, so it is
// not removed.
//
// Click() function is still called for all 3 cases.
//
// Messages interfare with debug, Trace statements are left for debuging.
//
// When change this code, please make sure radio-button and check-box
// work for both Tab-Space keys and mouse click(with out debug).
//
// Arthur 3/12/97, fix for #49682
//
void CNetscapeButton::Click(UINT nFlags, BOOL bNotify, BOOL &bReturnImmediately)
{
if(!m_Form || !m_Context)
return;
lo_FormElementMinimalData * min_data;
lo_FormElementToggleData * tog_data;
min_data = (lo_FormElementMinimalData *) m_Form->element_data;
char * pFileName;
if(!min_data)
return;
m_pLastSelectedRadio = NULL;
switch(min_data->type) {
case FORM_TYPE_RADIO:
tog_data = (lo_FormElementToggleData *) m_Form->element_data;
if(!tog_data->toggled) {
m_pLastSelectedRadio = LO_FormRadioSet(ABSTRACTCX(m_Context)->GetDocumentContext(), m_Form); // Turn off everyone else
}
if(GetCheck() != tog_data->toggled) {
SetCheck(tog_data->toggled);
#ifdef DEBUG_aliu
TRACE0("Set\n");
#endif // DEBUG_aliu
}
break;
case FORM_TYPE_CHECKBOX:
// Make sure we don't already reflect that state, and don't do this
// if via OnChar, as MFC/Windows will do it for us.
if(bNotify == FALSE) {
tog_data = (lo_FormElementToggleData *) m_Form->element_data;
if(tog_data->toggled) {
#ifdef DEBUG_aliu
TRACE0("Off ");
#endif // DEBUG_aliu
tog_data->toggled = FALSE;
} else {
tog_data->toggled = TRUE;
#ifdef DEBUG_aliu
TRACE0("On ");
#endif // DEBUG_aliu
}
if((GetCheck() ? TRUE : FALSE) != tog_data->toggled) {
#ifdef DEBUG_aliu
TRACE0("Set\n");
#endif // DEBUG_aliu
SetCheck(tog_data->toggled);
}
}
break;
case FORM_TYPE_BUTTON:
break;
case FORM_TYPE_RESET:
case FORM_TYPE_SUBMIT:
// don't do anything yet --- send the OnClick to libmocha first
// Sigh don't make double submits
if (bNotify == TRUE) return;
break;
case FORM_TYPE_FILE:
pFileName = wfe_GetExistingFileName(m_hWnd,
szLoadString(IDS_FILE_UPLOAD),
HTM,
TRUE);
/* update both the widget display and the lo-datastructure */
if(pFileName && m_pwndEdit) {
m_pwndEdit->SetWindowText(pFileName);
if (m_Form->element_data->ele_text.current_text)
XP_FREE(m_Form->element_data->ele_text.current_text);
m_Form->element_data->ele_text.current_text = (PA_Block) pFileName;
}
break;
default:
break;
}
// send the event to libmocha --- do any further processing
// in our closure routine
JSEvent *event;
event = XP_NEW_ZAP(JSEvent);
event->type = EVENT_CLICK;
event->layer_id = m_Form->layer_id;
event->which = 1;
event->modifiers = (nFlags & MK_SHIFT ? EVENT_SHIFT_MASK : 0)
| (nFlags & MK_CONTROL ? EVENT_CONTROL_MASK : 0)
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
ET_SendEvent(m_Context, (LO_Element *)m_Form, event,
button_click_closure, this);
}
void CNetscapeButton::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// CButton::OnChar( nChar, nRepCnt, nFlags);
if(( nChar == VK_SPACE || nChar == VK_RETURN) && !(nFlags>>14 & 1)) {
// buttons need a push.
BOOL bReturnImmediately = FALSE;
#ifdef DEBUG_aliu
TRACE0("onCharClick ");
#endif // DEBUG_aliu
Click(0, FALSE, bReturnImmediately); //#49682 TRUE
if(bReturnImmediately) {
return;
}
}
}
void CNetscapeButton::OnSetFocus(CWnd * pWnd)
{
CButton::OnSetFocus(pWnd); //must have this for fixing #51086, ref. #49682
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 );
// 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);
}
}
void CNetscapeButton::OnKillFocus(CWnd * pWnd)
{
CButton::OnKillFocus(pWnd);
if(m_Context && m_Form) {
// send the event to libmocha --- do any further processing
// in our closure routine
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 CNetscapeButton::OnLButtonDblClk(UINT uFlags, CPoint cpPoint) {
if (m_callBase) {
CButton::OnLButtonDblClk(uFlags, cpPoint);
return;
}
// Don't continue if this context is destroyed.
if(!m_Context || ABSTRACTCX(m_Context)->IsDestroyed()) {
return;
}
MapWindowPoints(GetParent(), &cpPoint, 1);
// Convert the point to something we understand.
XY Point;
m_pPaneCX->ResolvePoint(Point, cpPoint);
if (m_Context->compositor) {
CL_Event event;
fe_EventStruct fe_event;
fe_event.uFlags = uFlags;
fe_event.x = cpPoint.x;
fe_event.y = cpPoint.y;
event.type = CL_EVENT_MOUSE_BUTTON_MULTI_CLICK;
event.fe_event = (void *)&fe_event;
event.fe_event_size = sizeof(fe_EventStruct);
event.x = Point.x;
event.y = Point.y;
event.which = 1;
event.data = 2;
event.modifiers = (uFlags & MK_SHIFT ? EVENT_SHIFT_MASK : 0)
| (uFlags & MK_CONTROL ? EVENT_CONTROL_MASK : 0)
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
CL_DispatchEvent(m_Context->compositor,
&event);
}
else
CButton::OnLButtonDblClk(uFlags, cpPoint);
return;
}
void CNetscapeButton::OnLButtonDown(UINT uFlags, CPoint cpPoint) {
if (m_callBase) {
CButton::OnLButtonDown(uFlags, cpPoint);
return;
}
// Don't continue if this context is destroyed.
if(!m_Context) {
return;
}
// Start capturing all mouse events.
if(m_hWnd) {
::SetCapture(m_hWnd);
}
MapWindowPoints(GetParent(), &cpPoint, 1);
XY Point;
m_pPaneCX->ResolvePoint(Point, cpPoint);
if (m_Context->compositor) {
CL_Event event;
fe_EventStruct fe_event;
fe_event.uFlags = uFlags;
fe_event.x = cpPoint.x;
fe_event.y = cpPoint.y;
event.type = CL_EVENT_MOUSE_BUTTON_DOWN;
event.fe_event = (void *)&fe_event;
event.fe_event_size = sizeof(fe_EventStruct);
event.x = Point.x;
event.y = Point.y;
event.which = 1;
event.modifiers = (uFlags & MK_SHIFT ? EVENT_SHIFT_MASK : 0)
| (uFlags & MK_CONTROL ? EVENT_CONTROL_MASK : 0)
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
CL_DispatchEvent(m_Context->compositor,
&event);
}
else
CButton::OnLButtonDown(uFlags, cpPoint);
return;
}
void CNetscapeButton::OnLButtonUp(UINT uFlags, CPoint cpPoint) {
if (m_callBase) {
CButton::OnLButtonUp(uFlags, cpPoint);
return;
}
// Don't continue if this context is destroyed.
if(!m_Context) {
return;
}
// Start capturing all mouse events.
if(m_hWnd) {
::ReleaseCapture();
}
// translate mouse position to screen
CPoint cpScreenPoint(cpPoint);
ClientToScreen(&cpScreenPoint);
// Don't send the event on to JS if we're out of the button area.
if (::WindowFromPoint(cpScreenPoint) != m_hWnd) {
CButton::OnLButtonUp(uFlags, cpPoint);
m_bDepressed = FALSE;
return;
}
MapWindowPoints(GetParent(), &cpPoint, 1);
XY Point;
m_pPaneCX->ResolvePoint(Point, cpPoint);
if (m_Context->compositor) {
CL_Event event;
fe_EventStruct fe_event;
fe_event.uFlags = uFlags;
fe_event.x = cpPoint.x;
fe_event.y = cpPoint.y;
event.type = CL_EVENT_MOUSE_BUTTON_UP;
event.fe_event = (void *)&fe_event;
event.fe_event_size = sizeof(fe_EventStruct);
event.x = Point.x;
event.y = Point.y;
event.which = 1;
event.modifiers = (uFlags & MK_SHIFT ? EVENT_SHIFT_MASK : 0)
| (uFlags & MK_CONTROL ? EVENT_CONTROL_MASK : 0)
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
CL_DispatchEvent(m_Context->compositor,
&event);
}
else
CButton::OnLButtonUp(uFlags, cpPoint);
return;
}
void CNetscapeButton::OnRButtonDblClk(UINT uFlags, CPoint cpPoint) {
if (m_callBase) {
CButton::OnRButtonDblClk(uFlags, cpPoint);
return;
}
// Don't continue if this context is destroyed.
if(!m_Context) {
return;
}
MapWindowPoints(GetParent(), &cpPoint, 1);
XY Point;
m_pPaneCX->ResolvePoint(Point, cpPoint);
if (m_Context->compositor) {
CL_Event event;
fe_EventStruct fe_event;
fe_event.uFlags = uFlags;
fe_event.x = cpPoint.x;
fe_event.y = cpPoint.y;
event.type = CL_EVENT_MOUSE_BUTTON_MULTI_CLICK;
event.fe_event = (void *)&fe_event;
event.fe_event_size = sizeof(fe_EventStruct);
event.x = Point.x;
event.y = Point.y;
event.which = 3;
event.data = 2;
event.modifiers = (uFlags & MK_SHIFT ? EVENT_SHIFT_MASK : 0)
| (uFlags & MK_CONTROL ? EVENT_CONTROL_MASK : 0)
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
CL_DispatchEvent(m_Context->compositor,
&event);
}
else
CButton::OnRButtonDblClk(uFlags, cpPoint);
return;
}
void CNetscapeButton::OnRButtonDown(UINT uFlags, CPoint cpPoint) {
if (m_callBase) {
CButton::OnRButtonDown(uFlags, cpPoint);
return;
}
// Don't continue if this context is destroyed.
if(!m_Context) {
return;
}
// Start capturing all mouse events.
if(m_hWnd) {
::SetCapture(m_hWnd);
}
MapWindowPoints(GetParent(), &cpPoint, 1);
XY Point;
m_pPaneCX->ResolvePoint(Point, cpPoint);
if (m_Context->compositor) {
CL_Event event;
fe_EventStruct fe_event;
fe_event.uFlags = uFlags;
fe_event.x = cpPoint.x;
fe_event.y = cpPoint.y;
event.type = CL_EVENT_MOUSE_BUTTON_DOWN;
event.fe_event = (void *)&fe_event;
event.fe_event_size = sizeof(fe_EventStruct);
event.x = Point.x;
event.y = Point.y;
event.which = 3;
event.modifiers = (uFlags & MK_SHIFT ? EVENT_SHIFT_MASK : 0)
| (uFlags & MK_CONTROL ? EVENT_CONTROL_MASK : 0)
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
CL_DispatchEvent(m_Context->compositor,
&event);
}
else
CButton::OnRButtonDown(uFlags, cpPoint);
return;
}
void CNetscapeButton::OnRButtonUp(UINT uFlags, CPoint cpPoint) {
if (m_callBase) {
CButton::OnRButtonUp(uFlags, cpPoint);
return;
}
// Don't continue if this context is destroyed.
if(!m_Context) {
return;
}
// Start capturing all mouse events.
if(m_hWnd) {
::ReleaseCapture();
}
MapWindowPoints(GetParent(), &cpPoint, 1);
XY Point;
m_pPaneCX->ResolvePoint(Point, cpPoint);
if (m_Context->compositor) {
CL_Event event;
fe_EventStruct fe_event;
fe_event.uFlags = uFlags;
fe_event.x = cpPoint.x;
fe_event.y = cpPoint.y;
event.type = CL_EVENT_MOUSE_BUTTON_UP;
event.fe_event = (void *)&fe_event;
event.fe_event_size = sizeof(fe_EventStruct);
event.x = Point.x;
event.y = Point.y;
event.which = 3;
event.modifiers = (uFlags & MK_SHIFT ? EVENT_SHIFT_MASK : 0)
| (uFlags & MK_CONTROL ? EVENT_CONTROL_MASK : 0)
| (GetKeyState(VK_MENU) < 0 ? EVENT_ALT_MASK : 0);
CL_DispatchEvent(m_Context->compositor,
&event);
}
else
CButton::OnRButtonUp(uFlags, cpPoint);
return;
}
void
CNetscapeButton::OnButtonEvent(CL_EventType type, UINT nFlags, CPoint point) {
BOOL bReturnImmediately;
m_callBase = TRUE;
switch (type) {
case CL_EVENT_MOUSE_BUTTON_DOWN:
SendMessage(WM_LBUTTONDOWN, nFlags, MAKELONG(point.x, point.y));
// Hack since Windows stubbornly refuses to accept current state.
SetState(FALSE);
SetState(TRUE);
m_bDepressed = TRUE;
break;
case CL_EVENT_MOUSE_BUTTON_UP:
// Only send click event if button is already down.
if (m_bDepressed) {
SendMessage(WM_LBUTTONUP, nFlags, MAKELONG(point.x, point.y));
#ifdef DEBUG_aliu
TRACE0("ButtonEventClick ");
#endif // DEBUG_aliu
Click(nFlags, FALSE, bReturnImmediately);
m_bDepressed = FALSE;
}
break;
case CL_EVENT_MOUSE_BUTTON_MULTI_CLICK:
SendMessage(WM_LBUTTONDBLCLK, nFlags, MAKELONG(point.x, point.y));
break;
}
m_callBase = FALSE;
}