/***************************************************** * * UserPaneHandler(inHandlerCallRef, inEvent, inUserData) * * Purpose: called to handle the UserPane used as an embedder in the HIScrollView * * Inputs: inHandlerCallRef - reference to the current handler call chain * inEvent - the event * inUserData - app-specified data you passed in the call to InstallEventHandler * * Returns: OSStatus - noErr indicates the event was handled * eventNotHandledErr indicates the event was not handled and the Toolbox should take over */ static pascal OSStatus UserPaneHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void * inUserData) { OSStatus status = eventNotHandledErr; HIViewRef userPane = (HIViewRef)inUserData; HIRect bounds; HIViewGetBounds(userPane, &bounds); // the following is a very straightforward simple yet complete implementation of the // kEventClassScrollable protocol which is enough to make the User Pane scrolls along its embedded content switch (GetEventKind(inEvent)) { #pragma mark * kEventScrollableGetInfo case kEventScrollableGetInfo: { // we're being asked to return information about the scrolled view that we set as Event Parameters HISize lineSize = { 1, 1 }, imageSize = {3000, 2000}; SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof(bounds.size), &bounds.size); SetEventParameter(inEvent, kEventParamImageSize, typeHISize, sizeof(imageSize), &imageSize); SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof(lineSize), &lineSize); SetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, sizeof(bounds.origin), &bounds.origin); status = noErr; break; } #pragma mark * kEventScrollableScrollTo case kEventScrollableScrollTo: { // we're being asked to scroll, we just do a sanity check and ask for a redraw if the location is different HIPoint where; GetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, NULL, sizeof(where), NULL, &where); if ((bounds.origin.y != where.y) || (bounds.origin.x != where.x)) { HIViewSetBoundsOrigin(userPane, where.x, where.y); HIViewSetNeedsDisplay(userPane, true); } status = noErr; break; } } } // UserPaneHandler
bool AUCarbonViewBase::HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event) { #if !__LP64__ UInt32 eclass = GetEventClass(event); UInt32 ekind = GetEventKind(event); ControlRef control; switch (eclass) { case kEventClassControl: { switch (ekind) { case kEventControlClick: GetEventParameter(event, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control); if (control == mCarbonPane) { ClearKeyboardFocus(mCarbonWindow); return true; } } } break; case kEventClassScrollable: { switch (ekind) { case kEventScrollableGetInfo: { // [1/4] /* <-- kEventParamImageSize (out, typeHISize) * On exit, contains the size of the entire scrollable view. */ HISize originalSize = { mBottomRight.h, mBottomRight.v }; verify_noerr(SetEventParameter(event, kEventParamImageSize, typeHISize, sizeof(HISize), &originalSize)); // [2/4] /* <-- kEventParamViewSize (out, typeHISize) * On exit, contains the amount of the scrollable view that is * visible. */ HIViewRef parentView = HIViewGetSuperview(mCarbonPane); HIRect parentBounds; verify_noerr(HIViewGetBounds(parentView, &parentBounds)); //HISize windowSize = { float(windowBounds.right - windowBounds.left), // float(windowBounds.bottom - windowBounds.top) }; verify_noerr(SetEventParameter(event, kEventParamViewSize, typeHISize, sizeof(HISize), &(parentBounds.size))); // [3/4] /* <-- kEventParamLineSize (out, typeHISize) * On exit, contains the amount that should be scrolled in * response to a single click on a scrollbar arrow. */ HISize scrollIncrementSize = { 16.0f, float(20) }; verify_noerr(SetEventParameter(event, kEventParamLineSize, typeHISize, sizeof(HISize), &scrollIncrementSize)); // [4/4] /* <-- kEventParamOrigin (out, typeHIPoint) * On exit, contains the scrollable viewÕs current origin (the * view-relative coordinate that is drawn at the top left * corner of its frame). These coordinates should always be * greater than or equal to zero. They should be less than or * equal to the viewÕs image size minus its view size. */ verify_noerr(SetEventParameter(event, kEventParamOrigin, typeHIPoint, sizeof(HIPoint), &mCurrentScrollPoint)); } return true; case kEventScrollableScrollTo: { /* * kEventClassScrollable / kEventScrollableScrollTo * * Summary: * Requests that an HIScrollViewÕs scrollable view should scroll to * a particular origin. */ /* --> kEventParamOrigin (in, typeHIPoint) * The new origin for the scrollable view. The origin * coordinates will vary from (0,0) to scrollable viewÕs image * size minus its view size. */ HIPoint pointToScrollTo; verify_noerr(GetEventParameter(event, kEventParamOrigin, typeHIPoint, NULL, sizeof(HIPoint), NULL, &pointToScrollTo)); float xDelta = mCurrentScrollPoint.x - pointToScrollTo.x; float yDelta = mCurrentScrollPoint.y - pointToScrollTo.y; // move visible portion the appropriate amount verify_noerr(HIViewScrollRect(mCarbonPane, NULL, xDelta, yDelta)); // set new content to be drawn verify_noerr(HIViewSetBoundsOrigin(mCarbonPane, pointToScrollTo.x, pointToScrollTo.y)); mCurrentScrollPoint = pointToScrollTo; } return true; default: break; } } break; default: break; } #endif return false; }
/*----------------------------------------------------------------------------------------------------------*/ pascal OSStatus ScrollingTextViewHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) { OSStatus result = eventNotHandledErr; ScrollingTextBoxData* myData = (ScrollingTextBoxData*)inRefcon; switch (GetEventClass(inEvent)) { case kEventClassHIObject: switch (GetEventKind(inEvent)) { case kEventHIObjectConstruct: { // allocate some instance data myData = (ScrollingTextBoxData*) calloc(1, sizeof(ScrollingTextBoxData)); // get our superclass instance HIViewRef epView; GetEventParameter(inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof(epView), NULL, &epView); // remember our superclass in our instance data and initialize other fields myData->view = epView; // set the control ID so that we can find it later with HIViewFindByID result = SetControlID(myData->view, &kScrollingTextBoxViewID); // store our instance data into the event result = SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof(myData), &myData); break; } case kEventHIObjectDestruct: { if (myData->theTimer != NULL) RemoveEventLoopTimer(myData->theTimer); CFRelease(myData->theText); free(myData); result = noErr; break; } case kEventHIObjectInitialize: { // always begin kEventHIObjectInitialize by calling through to the previous handler result = CallNextEventHandler(inCaller, inEvent); // if that succeeded, do our own initialization if (result == noErr) { GetEventParameter(inEvent, kEventParamScrollingText, typeCFStringRef, NULL, sizeof(myData->theText), NULL, &myData->theText); CFRetain(myData->theText); GetEventParameter(inEvent, kEventParamAutoScroll, typeBoolean, NULL, sizeof(myData->autoScroll), NULL, &myData->autoScroll); GetEventParameter(inEvent, kEventParamDelayBeforeAutoScroll, typeUInt32, NULL, sizeof(myData->delayBeforeAutoScroll), NULL, &myData->delayBeforeAutoScroll); GetEventParameter(inEvent, kEventParamDelayBetweenAutoScroll, typeUInt32, NULL, sizeof(myData->delayBetweenAutoScroll), NULL, &myData->delayBetweenAutoScroll); GetEventParameter(inEvent, kEventParamAutoScrollAmount, typeSInt16, NULL, sizeof(myData->autoScrollAmount), NULL, &myData->autoScrollAmount); myData->theTimer = NULL; } break; } default: break; } break; case kEventClassScrollable: switch (GetEventKind(inEvent)) { case kEventScrollableGetInfo: { // we're being asked to return information about the scrolled view that we set as Event Parameters HISize imageSize = {50.0, myData->height}; SetEventParameter(inEvent, kEventParamImageSize, typeHISize, sizeof(imageSize), &imageSize); HISize lineSize = {50.0, 20.0}; SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof(lineSize), &lineSize); HIRect bounds; HIViewGetBounds(myData->view, &bounds); SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof(bounds.size), &bounds.size); SetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, sizeof(myData->originPoint), &myData->originPoint); result = noErr; break; } case kEventScrollableScrollTo: { // we're being asked to scroll, we just do a sanity check and ask for a redraw HIPoint where; GetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, NULL, sizeof(where), NULL, &where); HIViewSetNeedsDisplay(myData->view, true); myData->originPoint.y = (where.y < 0.0)?0.0:where.y; HIViewSetBoundsOrigin(myData->view, 0, myData->originPoint.y); break; } default: break; } break; case kEventClassControl: switch (GetEventKind(inEvent)) { // sets the feature of the view. case kEventControlInitialize: { result = CallNextEventHandler(inCaller, inEvent); if (result != noErr) break; UInt32 features = 0; result = GetEventParameter(inEvent, kEventParamControlFeatures, typeUInt32, NULL, sizeof(features), NULL, &features); if (result == noErr) features |= kControlSupportsEmbedding; else features = kControlSupportsEmbedding; result = SetEventParameter(inEvent, kEventParamControlFeatures, typeUInt32, sizeof features, &features); break; } // Our parent view just changed dimensions, so we determined our new height. case kEventControlSetData: CFRelease(myData->theText); CFStringRef *p; GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, NULL, sizeof(p), NULL, &p); myData->theText = *p; CFRetain(myData->theText); // fallthrough case kEventControlBoundsChanged: { HIRect bounds; HIViewGetBounds(myData->view, &bounds); // // If we're building on Panther (or later) then HIThemeGetTextDimensions is available, else we use GetThemeTextDimensions // #if PANTHER_BUILD // // Furthermore, if we're running on Panther then we can call HIThemeGetTextDimensions else we call GetThemeTextDimensions // if (GetHIToolboxVersion() >= Panther_HIToolbox_Version) { HIThemeTextInfo textInfo = {0, kThemeStateActive, kScrollingTextBoxFontID, kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, kHIThemeTextBoxOptionStronglyVertical, kHIThemeTextTruncationNone, 0, false}; HIThemeGetTextDimensions(myData->theText, bounds.size.width - kMargin - kMargin, &textInfo, NULL, &myData->height, NULL); } else #endif { Point pointBounds; pointBounds.h = (int)(bounds.size.width - kMargin - kMargin); GetThemeTextDimensions(myData->theText, kScrollingTextBoxFontID, kThemeStateActive, true, &pointBounds, NULL); myData->height = pointBounds.v; } myData->height += 2.0 * kMargin; HIViewSetNeedsDisplay(myData->view, true); result = eventNotHandledErr; break; } // Draw the view. case kEventControlDraw: { CGContextRef context; result = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(context), NULL, &context); HIRect bounds; HIViewGetBounds(myData->view, &bounds); CGContextSaveGState(context); CGAffineTransform transform = CGAffineTransformIdentity; // adjust the transform so the text doesn't draw upside down transform = CGAffineTransformScale(transform, 1, -1); CGContextSetTextMatrix(context, transform); // now that the proper parameters and configurations have been dealt with, let's draw result = ScrollingTextBoxDraw(context, &bounds, myData); CGContextRestoreGState(context); if (myData->autoScroll) CGContextStrokeRect(context, bounds); // we postpone starting the autoscroll timer until after we do our first drawing if ( (myData->autoScroll) && (myData->theTimer == NULL) ) InstallEventLoopTimer(GetCurrentEventLoop(), TicksToEventTime(myData->delayBeforeAutoScroll), TicksToEventTime(myData->delayBetweenAutoScroll), myScrollingTextTimeProc, myData, &myData->theTimer); result = noErr; break; } default: break; } break; default: break; } return result; }