Legacy Mac OS X Reference Library Apple Developer Connection

/HICustomPushButton.c

View Source Code:

Download Sample Code (“HICustomPushButton.zip”, 32.9K)



/*
*    File:        HICustomPushButton.c of HICustomPushButton
* 
*    Contains:    Demonstrates creating a simple custom push button using the HIView APIs.
*
*    Version:    1.1
* 
*    Created:    11/5/04
*
*    Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
*                ("Apple") in consideration of your agreement to the following terms, and your
*                use, installation, modification or redistribution of this Apple software
*                constitutes acceptance of these terms.  If you do not agree with these terms,
*                please do not use, install, modify or redistribute this Apple software.
*
*                In consideration of your agreement to abide by the following terms, and subject
*                to these terms, Apple grants you a personal, non-exclusive license, under Appleƕs
*                copyrights in this original Apple software (the "Apple Software"), to use,
*                reproduce, modify and redistribute the Apple Software, with or without
*                modifications, in source and/or binary forms; provided that if you redistribute
*                the Apple Software in its entirety and without modifications, you must retain
*                this notice and the following text and disclaimers in all such redistributions of
*                the Apple Software.  Neither the name, trademarks, service marks or logos of
*                Apple Computer, Inc. may be used to endorse or promote products derived from the
*                Apple Software without specific prior written permission from Apple.  Except as
*                expressly stated in this notice, no other rights or licenses, express or implied,
*                are granted by Apple herein, including but not limited to any patent rights that
*                may be infringed by your derivative works or by other works in which the Apple
*                Software may be incorporated.
*
*                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
*                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
*                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
*                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
*                COMBINATION WITH YOUR PRODUCTS.
*
*                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
*                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
*                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
*                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
*                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
*                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
*                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*    Copyright:  Copyright \xC2(C) 2005 Apple Computer, Inc, All Rights Reserved
*/
//****************************************************
#pragma mark * compilation directives *

//****************************************************
#pragma mark -
#pragma mark * includes & imports *

#include "HICustomPushButton.h"

//****************************************************
#pragma mark -
#pragma mark * typedef's, struct's, enums, defines, etc. *

// structure in which we hold our custom push button's data
typedef struct
    {
    HIViewRef        view;                // the HIViewRef for our button
    /* MoreStuff    someMoreStuff; */    // More stuff if we need it,
                                        // we don't need anything for this custom push button
                                        // but this sample skeleton might be used for
                                        // something more ambitious.
    }
CustomPushButtonData;

#define kCustomPushButtonClassID    CFSTR("com.apple.sample.dts.HICustomPushButton")

//****************************************************
#pragma mark -
#pragma mark * local (static) function prototypes *

static pascal OSStatus Internal_HICustomPushButtonHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon);

//****************************************************
#pragma mark -
#pragma mark * exported globals *

//****************************************************
#pragma mark -
#pragma mark * local (static) globals *

static UInt32 gWindowCount = 0;

//****************************************************
#pragma mark -
#pragma mark * exported function implementations *

/*****************************************************
*
* Do_NewWindow() 
*
* Purpose:  called to create a new window, each other window will be created from APIs and the other one from Interface Builder
*
* Notes:    called by Handle_CommandProcess() ("File/New" menu item), Handle_OpenApplication(). Handle_ReopenApplication()
*
* Inputs:   none
*
* Returns:  OSStatus    - error code (0 == no error) 
*/
OSStatus Do_NewWindow(void)
{
    WindowRef aWindowRef = NULL;
    CFStringRef theTitle = NULL;
    OSStatus status;
    
    // Create a window
    Rect bounds = {0, 0, 360, 480};
    status = CreateNewWindow(kDocumentWindowClass, kWindowStandardFloatingAttributes | kWindowStandardHandlerAttribute | kWindowCompositingAttribute | kWindowMetalAttribute, &bounds, &aWindowRef);
    require_noerr(status, CreateNewWindow);
    require(aWindowRef != NULL, CreateNewWindow);
    
    status = RepositionWindow(aWindowRef, NULL, kWindowCascadeOnMainScreen);
    require_noerr(status, RepositionWindow);
    
    HIViewRef contentView;
    status = HIViewFindByID(HIViewGetRoot(aWindowRef), kHIViewWindowContentID, &contentView);
    require_noerr(status, HIViewFindByID);

    // create the custom push button view
    HIViewRef customPushButton;
    status = HICustomPushButtonCreate(&customPushButton);
    require_noerr(status, HICustomPushButtonCreate);
    
    // place the view into the Window content view
    status = HIViewAddSubview(contentView, customPushButton);
    require_noerr(status, HIViewAddSubview);
    
    // position the view
    HIRect frame = { {110.0, 130.0}, {200.0, 40.0} };
    HIViewSetFrame(customPushButton, &frame);
    
    // views are initially invisible, so make it visible
    HIViewSetVisible(customPushButton, true);
    
    // give the button a command so that it does something when pressed
    SetControlCommandID(customPushButton, kHICommandAbout);
    
    theTitle = CFStringCreateWithFormat(NULL, NULL, CFSTR("HICustomPushButton Window #%ld"), ++gWindowCount);
    require(theTitle != NULL, CFStringCreateWithFormat);
    
    status = SetWindowTitleWithCFString(aWindowRef, theTitle);
    require_noerr(status, SetWindowTitleWithCFString);
        
    // The window was created hidden so show it
    ShowWindow(aWindowRef);
    
    SetWindowModified(aWindowRef, false);
    
HICustomPushButtonCreate:
SetWindowTitleWithCFString:
CFStringCreateWithFormat:

    if (theTitle != NULL)
        CFRelease(theTitle);

SetWindowDefaultButton:
SetControlCommandID:
CreatePushButtonControl:
HIViewAddSubview:
CreateStaticTextControl:
HIViewFindByID:
RepositionWindow:
CreateNewWindow:
    
    return status;
}   // Do_NewWindow

/*****************************************************
*
* HICustomPushButtonCreate(outView) 
*
* Purpose:  registers a HIView custom class and installs the event handlers for that class
*
* Inputs:   outView     - returns the newly created HIView if successful
*
* Returns:  OSStatus    - error code (0 == no error) 
*/
OSStatus HICustomPushButtonCreate(HIViewRef *outView)
{
    OSStatus status;

    status = HIObjectCreate(GetCustomPushButtonClass(), 0, (HIObjectRef *)outView);
    require_noerr(status, HIObjectCreate);

HIObjectCreate:

    return status;
}   // HICustomPushButtonCreate

/*****************************************************
*
* GetCustomPushButtonClass() 
*
* Purpose:  registers a HIView custom class and installs the event handlers for that class
*
* Inputs:   none
*
* Returns:  CFStringRef - the class name
*/
CFStringRef GetCustomPushButtonClass(void)
{
    
    // following code is pretty much boiler plate.
    
    static HIObjectClassRef    theClass;
    
    if (theClass == NULL)
    {
        static EventTypeSpec kFactoryEvents[] =
        {
            // the next 2 messages are required

            { kEventClassHIObject, kEventHIObjectConstruct },
            { kEventClassHIObject, kEventHIObjectDestruct },
                    
            // the next 3 messages are the actual minimum messages you need to
            // implement a simple custom push button:
            //
            // kEventControlHitTest has to be implemented so that you can
            //      verify that the point passed in parameter is indeed in
            //      an active part of your control.
            //      Note: contrary to what you might think and what the name suggests
            //            (HitTest), this message can be sent even when the button is
            //            not down. Do not assume that you just got a click.
            //            The Control Manager is just asking you to verify if a point
            //            is in a part of your control, nothing more.
            //
            // kEventControlHiliteChanged, you get this message if the user just clicked
            //      in your control, or has left the scope of your control while the
            //      button is still down. In each case, that means a change of hilite.
            //      most of the time, you should just react by asking for a redraw.
            //
            // kEventControlDraw, you need to draw your control (or part of it),
            //      according to its state.
            //
            // and, for a simple custom push button, that's IT!
            //
            // You do not need to implement kEventControlHit since, for a push button,
            // it makes more sense to attach a command to it which will automatically
            // be invoked if the user releases the button while inside the control.
            //
            // You do not need to implement kEventControlClick since you implement
            // kEventControlHitTest which has to be implemented. That would be
            // redundant and, for a simple push button control, you don't need it.
            //
            // You do not need to implement kEventControlTrack since the tracking will
            // be done for you by the Control Manager which will repeatedly call your
            // kEventControlHitTest implementation to know what it's supposed to do.
            // You only need to implement kEventControlTrack if you want to do something
            // special while the user is tracking, like displaying a page number near the
            // thumb of the scroll bar that he is moving.
                    
            { kEventClassControl, kEventControlHitTest },
            { kEventClassControl, kEventControlHiliteChanged },
            { kEventClassControl, kEventControlDraw }
        };
        
        HIObjectRegisterSubclass(kCustomPushButtonClassID, kHIViewClassID, 0, Internal_HICustomPushButtonHandler,
                                  GetEventTypeCount(kFactoryEvents), kFactoryEvents, 0, &theClass);
    }
    
    return kCustomPushButtonClassID;
}   // GetCustomPushButtonClass

//****************************************************
#pragma mark -
#pragma mark * local (static) function implementations *

/*----------------------------------------------------------------------------------------------------------*/
//    * ViewHandler
//    Event handler that implements our custom push button.
/*----------------------------------------------------------------------------------------------------------*/
static pascal OSStatus Internal_HICustomPushButtonHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon)
{
    OSStatus status = eventNotHandledErr;
    CustomPushButtonData * myData = (CustomPushButtonData*)inRefcon;

    switch (GetEventClass(inEvent))
    {
        case kEventClassHIObject:
            switch (GetEventKind(inEvent))
            {
                case kEventHIObjectConstruct:
                {
                    // allocate some instance data
                    myData = (CustomPushButtonData*) calloc(1, sizeof(CustomPushButtonData));
                    require(myData != NULL, CantAllocateData);
                    
                    // get our superclass instance
                    HIViewRef epView;
                    status = GetEventParameter(inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof(epView), NULL, &epView);
                    require_noerr(status, GetEventParameter);
                    
                    // remember our superclass in our instance data
                    myData->view = epView;
                    
                    // store our instance data into the event
                    status = SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof(myData), &myData);
                    require_noerr(status, SetEventParameter);
                    
                    break;
                }
                    
                case kEventHIObjectDestruct:
                {
                    if (myData != NULL) free(myData);
                    status = noErr;
                    break;
                }
                
                default:
                    break;
            }
            break;
            
        case kEventClassControl:
            switch (GetEventKind(inEvent))
            {

                //    Draw the view.

                case kEventControlDraw:
                {
                    CGContextRef    context;
                    HIRect            bounds;
                    
                    status = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(context), NULL, &context);
                    require_noerr(status, GetEventParameter);
                    
                    HIViewGetBounds(myData->view, &bounds);

                    if ((!IsControlHilited(myData->view)) || (!IsControlActive(myData->view)))
                        CGContextSetGrayFillColor(context, 0.1, 0.3);
                    else
                        CGContextSetRGBFillColor(context, 0.1, 0.1, 1.0, 0.3);

                    CGContextFillRect(context, bounds);
                    status = noErr;
                    break;
                }

                //    Determine if a point is in the view.

                case kEventControlHitTest:
                {
                    HIPoint    pt;
                    HIRect    bounds;
                    
                    // the point parameter is in view-local coords.
                    status = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(pt), NULL, &pt);
                    require_noerr(status, GetEventParameter);

                    HIViewGetBounds(myData->view, &bounds);
                    
                    if (CGRectContainsPoint(bounds, pt))
                    {
                        ControlPartCode part = kControlButtonPart;
                        status = SetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, sizeof(part), &part);
                        require_noerr(status, SetEventParameter);
                    }
                    break;
                }

                //    React to hilite changes by invalidating the view so that it will be redrawn.

                case kEventControlHiliteChanged:
                    HIViewSetNeedsDisplay(myData->view, true);
                    break;
                
                default:
                    break;
            }
            break;
            
        default:
            break;
    }

SetEventParameter:
GetEventParameter:
CantAllocateData:

    return status;
}
Did this document help you? Yes It's good, but... Not helpful...