From 0951733a49a8b3ab9ab1a1d59b669a9cab039220 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Thu, 8 Jul 2010 11:55:43 +0000 Subject: [PATCH] MGR: Fix bugs in accessibility code for Notices tab svn path=/trunk/boinc/; revision=21880 --- checkin_notes | 8 + clientgui/NoticeListCtrl.cpp | 67 +++-- clientgui/NoticeListCtrl.h | 4 +- clientgui/mac/MacAccessiblity.cpp | 477 +++++++++++++++++++++++++++++- 4 files changed, 528 insertions(+), 28 deletions(-) diff --git a/checkin_notes b/checkin_notes index 9bb65738eb..84f74ff6d3 100644 --- a/checkin_notes +++ b/checkin_notes @@ -4954,3 +4954,11 @@ David 6 Jul 2010 html/inc/ translation.inc + +Charlie 8 Jul 2010 + - MGR: Fix bugs in accessibility code for Notices tab. + + clientgui/ + NoticeListCtrl.cpp, .h + mac/ + MacAccessiblity.cpp diff --git a/clientgui/NoticeListCtrl.cpp b/clientgui/NoticeListCtrl.cpp index c678608908..dffd95f435 100644 --- a/clientgui/NoticeListCtrl.cpp +++ b/clientgui/NoticeListCtrl.cpp @@ -60,7 +60,8 @@ wxAccStatus CNoticeListCtrlAccessible::GetName(int childId, wxString* name) if (pDoc) { - strBuffer = wxString(process_client_message(pDoc->notice(childId-1)->title), wxConvUTF8); + strBuffer = wxString(process_client_message(pDoc->notice(childId-1)->title), wxConvUTF8); + strBuffer = StripHTMLTags(strBuffer); *name = strBuffer.c_str(); } } @@ -99,25 +100,21 @@ wxAccStatus CNoticeListCtrlAccessible::GetLocation(wxRect& rect, int elementId) { // List item wxSize cCtrlSize = pCtrl->GetClientSize(); - int iItemWidth = cCtrlSize.GetWidth(); - int iItemHeight = pCtrl->GetTotalClientHeight() / (int)pCtrl->GetItemCount(); // Set the initial control postition to the absolute coords of the upper // left hand position of the control rect.SetPosition(pCtrl->GetScreenPosition()); - rect.width = iItemWidth - 1; - rect.height = iItemHeight - 1; + rect.width = cCtrlSize.GetWidth() - 1; + rect.height = pCtrl->GetItemHeight(elementId - 1) - 1; - if (1 == elementId) - { - // First child - } - else - { - // Other children - rect.SetTop(rect.GetTop() + ((elementId - 1) * iItemHeight) + 1); - rect.height -= 1; + // Items can have different heights + int firstVisibleItem = (int)pCtrl->GetFirstVisibleLine(); + int yOffset = 0; + for (int i=firstVisibleItem; i<(elementId - 1); ++i) { + yOffset += pCtrl->GetItemHeight((size_t)i); } + rect.SetTop(rect.GetTop() + yOffset); + rect.height -= 1; return wxACC_OK; } // Let the framework handle the other cases. @@ -181,14 +178,29 @@ wxAccStatus CNoticeListCtrlAccessible::GetDescription(int childId, wxString* des { CMainDocument* pDoc = wxGetApp().GetDocument(); static wxString strBuffer; + wxDateTime dtBuffer; + wxString strDescription; + wxString strProjectName = wxEmptyString; + wxString strArrivalTime = wxEmptyString; if (pDoc && (childId != wxACC_SELF)) { strBuffer = wxEmptyString; if (pDoc) { - strBuffer = wxString(process_client_message(pDoc->notice(childId-1)->description.c_str()), wxConvUTF8); + strDescription = wxString(process_client_message(pDoc->notice(childId-1)->description.c_str()), wxConvUTF8); + strProjectName = wxString(pDoc->notice(childId-1)->project_name, wxConvUTF8); + dtBuffer.Set((time_t)pDoc->notice(childId-1)->arrival_time); + strArrivalTime = dtBuffer.Format(); + if (strProjectName.IsEmpty()) { + strBuffer.Printf(_("%s; received on %s"), strDescription.c_str(), strArrivalTime.c_str()); + } else { + strBuffer.Printf(_("%s; received from %s; on %s"), strDescription.c_str(), strProjectName.c_str(), strArrivalTime.c_str()); + } + + strBuffer = StripHTMLTags(strBuffer); *description = strBuffer.c_str(); + return wxACC_OK; } } @@ -198,6 +210,20 @@ wxAccStatus CNoticeListCtrlAccessible::GetDescription(int childId, wxString* des } +wxString CNoticeListCtrlAccessible::StripHTMLTags(wxString inBuf) { + wxString outBuf = wxEmptyString; + wxString tempBuf = inBuf; + + while (!tempBuf.IsEmpty()) { + outBuf += tempBuf.BeforeFirst(wxT('<')); + tempBuf = tempBuf.AfterFirst(wxT('<')); + if (tempBuf.IsEmpty()) break; + tempBuf = tempBuf.AfterFirst(wxT('>')); + } + + return outBuf; +} + #ifndef __WXMAC__ // Navigates from fromId to toId/toObject. @@ -569,14 +595,3 @@ bool CNoticeListCtrl::UpdateUI() } return true; } - - -/*! - * Return the total height of all the client items. - */ - -wxCoord CNoticeListCtrl::GetTotalClientHeight() -{ - return EstimateTotalHeight(); -} - diff --git a/clientgui/NoticeListCtrl.h b/clientgui/NoticeListCtrl.h index 509eeada2e..e095c69b74 100644 --- a/clientgui/NoticeListCtrl.h +++ b/clientgui/NoticeListCtrl.h @@ -56,6 +56,8 @@ public: virtual wxAccStatus GetChildCount(int* childCount); virtual wxAccStatus DoDefaultAction(int childId); virtual wxAccStatus GetDescription(int childId, wxString* description); + wxString StripHTMLTags(wxString inBuf); + #ifndef __WXMAC__ virtual wxAccStatus Navigate(wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject); virtual wxAccStatus GetDefaultAction(int childId, wxString* actionName); @@ -110,7 +112,7 @@ public: bool UpdateUI(); - wxCoord GetTotalClientHeight(); + int GetItemHeight(size_t i) { return (int)OnMeasureItem(i); } private: #ifdef __WXMAC__ diff --git a/clientgui/mac/MacAccessiblity.cpp b/clientgui/mac/MacAccessiblity.cpp index 241024f41c..dfaf88730e 100755 --- a/clientgui/mac/MacAccessiblity.cpp +++ b/clientgui/mac/MacAccessiblity.cpp @@ -74,6 +74,8 @@ pascal OSStatus BOINCListAccessibilityEventHandler( EventHandlerCallRef inHandle pascal OSStatus AttachListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData); +pascal OSStatus NoticeListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, void* pData); static EventTypeSpec myAccessibilityEvents[] = { { kEventClassAccessibility, kEventAccessibleGetChildAtPoint }, @@ -224,7 +226,7 @@ void CNoticeListCtrlAccessible::SetupMacAccessibilitySupport() { m_listView = (HIViewRef)pCtrl->GetHandle(); err = HIViewSetEnabled(m_listView, true); - err = InstallHIObjectEventHandler((HIObjectRef)m_listView, NewEventHandlerUPP(AttachListAccessibilityEventHandler), + err = InstallHIObjectEventHandler((HIObjectRef)m_listView, NewEventHandlerUPP(NoticeListAccessibilityEventHandler), sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents, this, &m_plistAccessibilityEventHandlerRef); } else { @@ -1719,6 +1721,479 @@ pascal OSStatus AttachListAccessibilityEventHandler( EventHandlerCallRef inHandl return eventNotHandledErr; } + +pascal OSStatus NoticeListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, void* pData) { + const UInt32 eventClass = GetEventClass(inEvent); + const UInt32 eventKind = GetEventKind(inEvent); + OSStatus err; + + CProjectListCtrlAccessible* pAccessible = (CProjectListCtrlAccessible*)pData; + if (pAccessible == NULL) { + return eventNotHandledErr; + } + + CNoticeListCtrl* pCtrl = wxDynamicCast(pAccessible->GetWindow(), CNoticeListCtrl); + if (pCtrl == NULL) { + return eventNotHandledErr; + } + + if (eventClass != kEventClassAccessibility) { + return eventNotHandledErr; + } + + AXUIElementRef element; + UInt64 inIdentifier = 0; + UInt64 outIdentifier = 0; + SInt32 row = 0; + HIObjectRef obj = NULL; + + err = GetEventParameter (inEvent, kEventParamAccessibleObject, + typeCFTypeRef, NULL, sizeof(typeCFTypeRef), NULL, &element); + if (err) return err; + + AXUIElementGetIdentifier( element, &inIdentifier ); + obj = AXUIElementGetHIObject(element); + + row = inIdentifier; + + switch (eventKind) { +#pragma mark kEventAccessibleGetChildAtPoint + case kEventAccessibleGetChildAtPoint: + { + CFTypeRef child = NULL; + HIPoint where; + int hitRow; + + // Only the whole view can be tested since the parts don't have sub-parts. + if (inIdentifier != 0) { + return noErr; + } + + err = GetEventParameter (inEvent, kEventParamMouseLocation, + typeHIPoint, NULL, sizeof(HIPoint), NULL, &where); + if (err) return err; + + wxPoint p((int)where.x, (int)where.y); + pCtrl->ScreenToClient(&p.x, &p.y); + + err = pAccessible->HitTest(p, &hitRow, NULL); + + if (err) { + return eventNotHandledErr; + } + + if (hitRow >= 0) { + outIdentifier = hitRow + 1; + child = AXUIElementCreateWithHIObjectAndIdentifier(obj, outIdentifier ); + if (child == NULL) { + return eventNotHandledErr; + } + + err = SetEventParameter (inEvent, kEventParamAccessibleChild, typeCFTypeRef, + sizeof(typeCFTypeRef), &child); + if (err) { + return eventNotHandledErr; + } + } + + return noErr; + } + break; + +#pragma mark kEventAccessibleGetFocusedChild + case kEventAccessibleGetFocusedChild: + return noErr; + break; + +#pragma mark kEventAccessibleGetAllAttributeNames + case kEventAccessibleGetAllAttributeNames: + { + CFMutableArrayRef namesArray; + + err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames, + typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray); + if (err) + return err; + + CallNextEventHandler( inHandlerCallRef, inEvent ); + + if ( inIdentifier == 0 ) + { + // Identifier 0 means "the whole view". + // Let accessibility know that this view has children and can + // return a list of them. + CFArrayAppendValue( namesArray, kAXChildrenAttribute ); + } else { + // Let accessibility know that this view's children can return description, + // size, position, parent window, top level element and isFocused attributes. + CFArrayAppendValue( namesArray, kAXWindowAttribute ); + CFArrayAppendValue( namesArray, kAXTopLevelUIElementAttribute ); + CFArrayAppendValue( namesArray, kAXDescriptionAttribute ); + CFArrayAppendValue( namesArray, kAXSizeAttribute ); + CFArrayAppendValue( namesArray, kAXPositionAttribute ); + CFArrayAppendValue( namesArray, kAXTitleAttribute ); + CFArrayAppendValue( namesArray, kAXEnabledAttribute ); + } + + CFArrayAppendValue( namesArray, kAXFocusedAttribute ); + CFArrayAppendValue( namesArray, kAXRoleAttribute ); + CFArrayAppendValue( namesArray, kAXRoleDescriptionAttribute ); + CFArrayAppendValue( namesArray, kAXParentAttribute ); + + return noErr; + } + break; + +#pragma mark kEventAccessibleGetAllParameterizedAttributeNames + case kEventAccessibleGetAllParameterizedAttributeNames: + { + CFMutableArrayRef namesArray; + + err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames, + typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray); + if (err) return err; + + return noErr; + } + break; + +#pragma mark kEventAccessibleGetNamedAttribute + case kEventAccessibleGetNamedAttribute: + { + CFStringRef attribute; + + err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, + typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); + if (err) return err; + + if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { + // Return whether or not this part is focused. +//TODO: Add kAXFocusedAttribute support? + Boolean focused = false; + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused ); + return noErr; + } else if ( CFStringCompare( attribute, kAXSizeAttribute, 0 ) == kCFCompareEqualTo ) { + wxRect r; + HISize theSize; + + err = pAccessible->GetLocation(r, row); + if (err) { + return eventNotHandledErr; + } + + theSize.width = r.width; + theSize.height = r.height; + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &theSize ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) { + wxRect r; + HIPoint pt; + int x, y; + + err = pAccessible->GetLocation(r, row); + if (err) { + return eventNotHandledErr; + } + + x = r.x; + y = r.y; + + // Now convert to global coordinates +// pCtrl->ClientToScreen(&x, &y); + pt.x = x; + pt.y = y; + + SetEventParameter(inEvent, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(HIPoint), &pt); + return noErr; + } + + if ( inIdentifier == 0 ) { + // String compare the incoming attribute name and return the appropriate accessibility + // information as an event parameter. + + if ( CFStringCompare( attribute, kAXChildrenAttribute, 0 ) == kCFCompareEqualTo ) { + // Create and return an array of AXUIElements describing the children of this view. + CFMutableArrayRef children; + AXUIElementRef child; + int i, n; + + err = pAccessible->GetChildCount(&n); + children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks ); + + for ( i = 0; i < n; i++ ) { + outIdentifier = i+1; + child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier ); + CFArrayAppendValue( children, child ); + CFRelease( child ); + } + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children ); + CFRelease( children ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { + // Return a string indicating the role of this view. Using the table role doesn't work. + CFStringRef role = kAXListRole; + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { + // Return a string indicating the role of this part. +//TODO: specify whether projects or account managers + wxString str; + + str = _("list of projects or account managers"); + CFStringRef roleDesc = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc ); + CFRelease( roleDesc ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) { + AXUIElementRef parent; + HIViewRef parentView; + + parentView = HIViewGetSuperview(pAccessible->m_listView); + parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0); + if (parent == NULL) { + return eventNotHandledErr; + } + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent ); + CFRelease( parent ); + return noErr; + + } else { + return CallNextEventHandler( inHandlerCallRef, inEvent ); + + } + + } else { // End if ( inIdentifier == 0 ) + + if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { + wxString str, buf; + int n; + + err = pAccessible->GetChildCount(&n); + if (err) { + return eventNotHandledErr; + } + + if (pCtrl->IsSelected(row - 1)) { + str.Printf(_("selected row %d of %d; "), row, n); + } else { + str.Printf(_("row %d of %d; "), row, n); + } + + err = pAccessible->GetDescription(row, &buf); + if (err) { + return eventNotHandledErr; + } + str += buf; + + CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description ); + CFRelease( description ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) { + AXUIElementRef parent; + HIViewRef parentView; + + parentView = pAccessible->m_listView; + parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0); + if (parent == NULL) { + return eventNotHandledErr; + } + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent ); + CFRelease( parent ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXSubroleAttribute, 0 ) == kCFCompareEqualTo ) { + return eventNotHandledErr; + + } else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) { + // Return a string indicating the role of this part. The parts of the view behave like + // buttons, so use that system role. + + CFStringRef role = kAXStaticTextRole; // kAXRowRole; + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) { + // Return a string describing the role of this part. Use the system description. + CFStringRef roleDesc = HICopyAccessibilityRoleDescription( kAXRowRole, NULL ); + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc ); + CFRelease( roleDesc ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXWindowAttribute, 0 ) == kCFCompareEqualTo + || CFStringCompare( attribute, kAXTopLevelUIElementAttribute, 0 ) == kCFCompareEqualTo ) { + // Return the window or top level ui element for this part. They are both the same so re-use the code. + AXUIElementRef windOrTopUI; + + WindowRef win = GetControlOwner((HIViewRef)obj); + if (win == NULL) { + return eventNotHandledErr; + } + + windOrTopUI = AXUIElementCreateWithHIObjectAndIdentifier( (HIObjectRef)win, 0 ); + if (windOrTopUI == NULL) { + return eventNotHandledErr; + } + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( windOrTopUI ), &windOrTopUI ); + CFRelease( windOrTopUI ); + return noErr; + + } else if ( CFStringCompare( attribute, kAXEnabledAttribute, 0 ) == kCFCompareEqualTo ) { + Boolean enabled = true; + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( enabled ), &enabled ); + return noErr; + +//TODO: Add kAXFocusedAttribute support? +#if 0 + } else if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) { + // Return whether or not this part is focused. + Boolean focused = false; + + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused ); + return noErr; +#endif + + } else if ( CFStringCompare( attribute, kAXTitleAttribute, 0 ) == kCFCompareEqualTo ) { + // Return the item's text + wxString str; + wxListItem headerItem; + + pAccessible->GetName(row, &str); + + CFStringRef title = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8); + SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( title ), &title ); + CFRelease( title ); + return noErr; + + } else { + return eventNotHandledErr; + } + + } // End if ( inIdentifier != 0 ) + break; + } // End case kEventAccessibleGetNamedAttribute: + +#pragma mark kEventAccessibleIsNamedAttributeSettable + case kEventAccessibleIsNamedAttributeSettable: + { + CFStringRef attribute; + Boolean isSettable = false; + + err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName, + typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute); + if (err) return err; + + // The focused attribute is the only settable attribute for this view, + // and it can only be set on part (or subelements), not the whole view. + if (inIdentifier != 0) + { + if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) + { + isSettable = true; + } + } + SetEventParameter( inEvent, kEventParamAccessibleAttributeSettable, typeBoolean, sizeof( Boolean ), &isSettable ); + return noErr; + } + break; + +#pragma mark kEventAccessibleSetNamedAttribute + case kEventAccessibleSetNamedAttribute: + { + return eventNotHandledErr; + } + break; + +#pragma mark kEventAccessibleGetAllActionNames + case kEventAccessibleGetAllActionNames: + { + CFMutableArrayRef array; + + err = GetEventParameter (inEvent, kEventParamAccessibleActionNames, + typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &array); + if (err) return err; + + if (inIdentifier != 0) { + CFArrayAppendValue( array, kAXPressAction ); + } + return noErr; + } + break; + +#pragma mark kEventAccessibleGetNamedActionDescription + case kEventAccessibleGetNamedActionDescription: + { + CFStringRef action; + CFMutableStringRef desc; + CFStringRef selfDesc = NULL; + + if (inIdentifier == 0) { + return eventNotHandledErr; + } + + err = GetEventParameter (inEvent, kEventParamAccessibleActionName, + typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action); + if (err) return err; + + err = GetEventParameter (inEvent, kEventParamAccessibleActionDescription, + typeCFMutableStringRef, NULL, sizeof(typeCFMutableStringRef), NULL, &desc); + if (err) return err; + + selfDesc = HICopyAccessibilityActionDescription( action ); + + CFStringReplaceAll( desc, selfDesc ); + CFRelease( selfDesc ); + return noErr; + } + break; + +#pragma mark kEventAccessiblePerformNamedAction + case kEventAccessiblePerformNamedAction: + { + CFStringRef action; + + if (inIdentifier == 0) { + return eventNotHandledErr; + } + + err = GetEventParameter (inEvent, kEventParamAccessibleActionName, + typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action); + if (err) return err; + + if ( CFStringCompare( action, kAXPressAction, 0 ) != kCFCompareEqualTo ) { + return eventNotHandledErr; + } + err = pAccessible->DoDefaultAction(inIdentifier); + if (err) { + return eventNotHandledErr; + } + + return noErr; + } + break; + + default: + return eventNotHandledErr; + } // End switch(eventKind) + + return eventNotHandledErr; +} + + pascal OSStatus SimpleAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* pData) { const UInt32 eventClass = GetEventClass(inEvent);