/*
* 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 © 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;
}
|