Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
THexEditorView.cp
/* |
File: THexEditorView.cp |
Version: 1.0 |
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 © 2002 Apple Computer, Inc., All Rights Reserved |
*/ |
#include "THexEditorView.h" |
#include "THexEditorScrollableView.h" |
// This is the class ID that we've assigned to the HexEditorView |
#define kHexEditorClassID CFSTR("com.apple.sample.hexeditorview") |
//-------------------------------------------------------------------- |
// CONSTANTS |
//-------------------------------------------------------------------- |
// const data has internal linkage, so these will not pollute the |
// global namespace |
// |
const ControlKind kHexEditorViewKind = { 'Samp', 'HxVw' }; |
const short kHeaderTextOffset = 8; |
const float kHeaderHeight = 20.0; |
// View defined Carbon Events that are used internally to this view |
enum |
{ |
kEventParamDataBuffer = 'Data', // typeCFDataRef |
typeCFDataRef = 'CFDt' |
}; |
// Events that this view is interested in |
const EventTypeSpec kEvents[] = |
{ |
{ kEventClassScrollableHexEditorView, kEventHexEditorSelectionChanged } |
}; |
//-------------------------------------------------------------------- |
// THexEditorView |
//-------------------------------------------------------------------- |
// |
THexEditorView::THexEditorView( ControlRef inControl ) |
: |
TView( inControl ), |
fScrollView( NULL ), |
fScrollableHexView( NULL ), |
fEventHandler( NULL ) |
{ |
} |
//-------------------------------------------------------------------- |
// THexEditorView::THexEditorView |
//-------------------------------------------------------------------- |
// Clean up |
// |
THexEditorView::~THexEditorView() |
{ |
if ( fScrollableHexView != NULL ) |
CFRelease( fScrollableHexView ); |
if ( fScrollView != NULL ) |
CFRelease( fScrollView ); |
if ( fEventHandler != NULL ) |
RemoveEventHandler( fEventHandler ); |
} |
//-------------------------------------------------------------------- |
// Create API |
//-------------------------------------------------------------------- |
// This is the main creation API to create an instance of this |
// class. |
// |
OSStatus |
THexEditorView::Create( |
CFDataRef inData, |
HIViewRef* outControl ) |
{ |
OSStatus err; |
EventRef event = CreateInitializationEvent(); |
// Register this class with the HIView system |
RegisterClass(); |
verify_noerr( SetEventParameter( |
event, |
kEventParamDataBuffer, |
typeCFDataRef, |
sizeof( CFDataRef ), |
&inData ) ); |
err = _HIObjectCreate( kHexEditorClassID, event, (HIObjectRef*)outControl ); |
ReleaseEvent( event ); |
return err; |
} |
//-------------------------------------------------------------------- |
// SetCFData API |
//-------------------------------------------------------------------- |
// Update the hex editor view to display this new chunk of data |
// |
OSStatus |
THexEditorView::SetCFData( |
HIViewRef inView, |
CFDataRef inData ) |
{ |
OSStatus err; |
THexEditorView* view; |
// verify that we have valid parameters |
require_action( inView != NULL, SetData_BadParam_NoView, err = paramErr ); |
// cast the given view to an instance of this class so we can call its |
// methods |
view = (THexEditorView*)::HIObjectDynamicCast( |
(HIObjectRef)inView, |
kHexEditorClassID ); |
require_action( view != NULL, SetData_NoView, err = paramErr ); |
err = view->SetCFData( inData ); |
SetData_NoView: |
SetData_BadParam_NoView: |
return err; |
} |
//-------------------------------------------------------------------- |
// GetCFData API |
//-------------------------------------------------------------------- |
// This is the API to get the CFData from this view |
// |
OSStatus |
THexEditorView::GetCFData( |
HIViewRef inView, |
CFDataRef* outData ) |
{ |
OSStatus err; |
THexEditorView* view; |
// verify that we have valid parameters |
require_action( inView != NULL, GetData_BadParam_NoView, err = paramErr ); |
require_action( outData != NULL, GetData_BadParam_NoOutData, err = paramErr ); |
// cast the given view to an instance of this class so we can call its |
// methods |
view = (THexEditorView*)::HIObjectDynamicCast( |
(HIObjectRef)inView, |
kHexEditorClassID ); |
require_action( view != NULL, GetData_CantGetView, err = paramErr ); |
err = view->GetCFData( outData ); |
GetData_CantGetView: |
GetData_BadParam_NoOutData: |
GetData_BadParam_NoView: |
return err; |
} |
//-------------------------------------------------------------------- |
// RegisterClass |
//-------------------------------------------------------------------- |
// |
void |
THexEditorView::RegisterClass() |
{ |
static Boolean sRegistered; |
if ( not sRegistered ) |
{ |
TView::RegisterSubclass( kHexEditorClassID, Construct ); |
sRegistered = true; |
} |
} |
//-------------------------------------------------------------------- |
// SetCFData |
//-------------------------------------------------------------------- |
// |
OSStatus |
THexEditorView::SetCFData( CFDataRef inData ) |
{ |
OSStatus err; |
require_action( fScrollableHexView != NULL, SetData_NoScrollableView, err = paramErr ); |
err = THexEditorScrollableView::SetCFData( fScrollableHexView, inData ); |
SetData_NoScrollableView: |
return err; |
} |
//-------------------------------------------------------------------- |
// GetCFData |
//-------------------------------------------------------------------- |
// |
OSStatus |
THexEditorView::GetCFData( CFDataRef* outData ) |
{ |
OSStatus err; |
check( outData != NULL ); |
require_action( fScrollableHexView != NULL, GetData_NoScrollableView, err = paramErr ); |
err = THexEditorScrollableView::GetCFData( fScrollableHexView, outData ); |
GetData_NoScrollableView: |
return err; |
} |
//-------------------------------------------------------------------- |
// Initialize |
//-------------------------------------------------------------------- |
// Create the scrollable sub view and embed it in this view |
// |
OSStatus |
THexEditorView::Initialize( TCarbonEvent& inEvent ) |
{ |
OSStatus err = noErr; |
CFDataRef data = NULL; |
// Install a handler for the events that this view is interested |
// in |
verify_noerr( InstallEventHandler( |
GetEventTarget(), |
EventHandler, |
GetEventTypeCount( kEvents ), |
kEvents, |
this, |
&fEventHandler ) ); |
// It's okay if this parameter doesn't exist, it will leave data untouched |
inEvent.GetParameter( kEventParamDataBuffer, typeCFDataRef, sizeof( CFDataRef ), &data ); |
// Now create a scroll view that we will embed the scrollable view in |
err = HIScrollViewCreate( kHIScrollViewOptionsVertScroll, &fScrollView ); |
require_noerr( err, Initialize_CantCreateScrollView ); |
// Embed the scroll view in our view |
verify_noerr( HIViewAddSubview( GetViewRef(), fScrollView ) ); |
// This class doesn't actually deal with the data, it just passes it along to |
// the subordinate scroller class, which we create here |
err = THexEditorScrollableView::Create( data, &fScrollableHexView ); |
require_noerr( err, Initialize_CantCreateScrollableHexView ); |
// Embed the scrollable view in the scroll view and make it visible |
verify_noerr( HIViewAddSubview( fScrollView, fScrollableHexView ) ); |
HIViewSetVisible( fScrollableHexView, true ); |
HIViewSetVisible( fScrollView, true ); |
Initialize_CantCreateScrollableHexView: |
Initialize_CantCreateScrollView: |
return err; |
} |
//-------------------------------------------------------------------- |
// Draw |
//-------------------------------------------------------------------- |
// |
void |
THexEditorView::Draw( |
RgnHandle limitRgn, |
CGContextRef inContext ) |
{ |
TRect bounds = Bounds(); |
Rect textBox = bounds; |
CFStringRef dataSizeString; |
CFStringRef dataSelectionString; |
Point textBounds; |
SInt16 textBaseline; |
#pragma unused( limitRgn ) |
check( inContext != NULL ); |
DrawHeaderBackground( inContext ); |
// get the strings for the data size and selection |
dataSizeString = CreateDataSizeString(); |
require( dataSizeString != NULL, Draw_CantGetDataString ); |
dataSelectionString = CreateDataSelectionString(); |
require( dataSelectionString != NULL, Draw_CantGetSelectionString ); |
// get the dimensions of the data size string |
verify_noerr( GetThemeTextDimensions( |
dataSizeString, |
kThemeSmallSystemFont, |
kThemeStateActive, |
false, |
&textBounds, |
&textBaseline ) ); |
textBox.left += kHeaderTextOffset; |
// draw the dataSize string into the header area |
verify_noerr( DrawThemeTextBox( |
dataSizeString, |
kThemeSmallSystemFont, |
kThemeStateActive, |
false, |
&textBox, |
teFlushDefault, |
inContext ) ); |
// move the box past the end of the data size string (plus a little padding) |
textBox.left += (textBounds.h + kHeaderTextOffset); |
// now draw the data selection string into this box |
verify_noerr( DrawThemeTextBox( |
dataSelectionString, |
kThemeSmallSystemFont, |
kThemeStateActive, |
false, |
&textBox, |
teFlushDefault, |
inContext ) ); |
// release our references to these strings, we're done with them |
CFRelease( dataSelectionString ); |
Draw_CantGetSelectionString: |
CFRelease( dataSizeString ); |
Draw_CantGetDataString: |
// Draw a rectangle around the content (or scrollable view) |
CGContextMoveToPoint( inContext, bounds.MinX(), bounds.MinY() ); |
CGContextAddLineToPoint( inContext, bounds.MinX(), bounds.MaxY() ); |
CGContextAddLineToPoint( inContext, bounds.MaxX(), bounds.MaxY() ); |
CGContextAddLineToPoint( inContext, bounds.MaxX(), bounds.MinY() ); |
CGContextAddLineToPoint( inContext, bounds.MinX(), bounds.MinY() ); |
CGContextMoveToPoint( inContext, bounds.MinX(), bounds.MinY() + GetHeaderHeight() ); |
CGContextAddLineToPoint( inContext, bounds.MaxX(), bounds.MinY() + GetHeaderHeight() ); |
CGContextStrokePath( inContext ); |
} |
//-------------------------------------------------------------------- |
// HitTest |
//-------------------------------------------------------------------- |
// |
ControlPartCode |
THexEditorView::HitTest( const HIPoint& where ) |
{ |
if ( CGRectContainsPoint( Bounds(), where ) ) |
return 1; |
else |
return kControlNoPart; |
} |
//-------------------------------------------------------------------- |
// GetBehaviors |
//-------------------------------------------------------------------- |
// We must respond to this and indicate that this control supports |
// embedding because we want to embed the scroll view within this view |
// |
UInt32 |
THexEditorView::GetBehaviors() |
{ |
return TView::GetBehaviors() | kControlSupportsEmbedding; |
} |
#pragma mark --- HANDLERS --- |
//-------------------------------------------------------------------- |
// BoundsChanged |
//-------------------------------------------------------------------- |
// Called when the view's bounds have changed |
// |
OSStatus |
THexEditorView::BoundsChanged( |
UInt32 inOptions, |
const HIRect& inOriginalBounds, |
const HIRect& inCurrentBounds, |
RgnHandle inInvalRgn ) |
{ |
OSStatus err; |
TRect bounds; |
float headerHeight = GetHeaderHeight(); |
#pragma unused( inOptions, inOriginalBounds, inInvalRgn ) |
check( fScrollView != NULL ); |
// leave enough room for our header |
bounds.Set( |
inCurrentBounds.origin.x, |
inCurrentBounds.origin.y + headerHeight, |
inCurrentBounds.size.width, |
inCurrentBounds.size.height - headerHeight ); |
// embed the scroll view _within_ this view. Inset by a pixel. |
bounds.Inset( 1.0, 1.0 ); |
// set the scroll view to be this "content" rect |
err = HIViewSetFrame( fScrollView, &bounds ); |
require_noerr( err, BoundsChanged_CantSetFrame ); |
BoundsChanged_CantSetFrame: |
return err; |
} |
//-------------------------------------------------------------------- |
// GetKind |
//-------------------------------------------------------------------- |
// |
ControlKind |
THexEditorView::GetKind() |
{ |
return kHexEditorViewKind; |
} |
#pragma mark --- PRIVATE METHODS --- |
//-------------------------------------------------------------------- |
// GetHeaderHeight |
//-------------------------------------------------------------------- |
// |
float |
THexEditorView::GetHeaderHeight() |
{ |
return kHeaderHeight; |
} |
//-------------------------------------------------------------------- |
// CreateDataSizeString |
//-------------------------------------------------------------------- |
// Create a string that contains the length of the contained data |
// |
CFStringRef |
THexEditorView::CreateDataSizeString() |
{ |
OSStatus err; |
CFStringRef sizeString = NULL; |
long dataSize; |
require_quiet( fScrollableHexView != NULL, CreateDataSizeString_NoScrollableView ); |
// get the data size from the scrollable hex view instance |
err = THexEditorScrollableView::GetDataSize( |
fScrollableHexView, |
&dataSize ); |
require_noerr( err, CreateDataSizeString_CantGetSize ); |
// create a string that contains this information |
sizeString = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("Data size: %ld"), dataSize ); |
CreateDataSizeString_CantGetSize: |
CreateDataSizeString_NoScrollableView: |
return sizeString; |
} |
//-------------------------------------------------------------------- |
// CreateDataSelectionString |
//-------------------------------------------------------------------- |
// Create a string that specifies the current selection |
// |
CFStringRef |
THexEditorView::CreateDataSelectionString() |
{ |
OSStatus err; |
CFStringRef selectionString = NULL; |
long selectionStart; |
long selectionEnd; |
require_quiet( fScrollableHexView != NULL, CreateSelectionString_NoScrollableView ); |
// get the selection from the scrollable hex view instance |
err = THexEditorScrollableView::GetDataSelection( |
fScrollableHexView, |
&selectionStart, |
&selectionEnd ); |
require_noerr( err, CreateSelectionString_CantGetSelection ); |
// if it is an empty selection indicate that the cursor is set up |
// for insertion, otherwise indicate what the selection is |
if ( selectionStart == selectionEnd ) |
{ |
selectionString = CFStringCreateWithFormat( |
kCFAllocatorDefault, |
NULL, |
CFSTR("Insertion: %ld"), |
selectionStart ); |
} |
else |
{ |
selectionString = CFStringCreateWithFormat( |
kCFAllocatorDefault, |
NULL, |
CFSTR("Selection: [ %ld, %ld ) %ld bytes"), |
selectionStart, |
selectionEnd, |
selectionEnd - selectionStart ); |
} |
CreateSelectionString_CantGetSelection: |
CreateSelectionString_NoScrollableView: |
return selectionString; |
} |
//-------------------------------------------------------------------- |
// DrawHeaderBackground |
//-------------------------------------------------------------------- |
// |
void |
THexEditorView::DrawHeaderBackground( CGContextRef inContext ) |
{ |
TRect headerBounds = Bounds(); |
headerBounds.SetHeight( GetHeaderHeight() ); |
// Fill in with gray |
::CGContextSetRGBFillColor( |
inContext, |
0.9, |
0.9, |
0.9, |
1.0 ); |
::CGContextFillRect( inContext, headerBounds ); |
// Restore with black |
::CGContextSetRGBFillColor( |
inContext, |
0.0, |
0.0, |
0.0, |
1.0 ); |
} |
#pragma mark --- CLASS HANDLERS --- |
//-------------------------------------------------------------------- |
// Construct |
//-------------------------------------------------------------------- |
// |
OSStatus |
THexEditorView::Construct( |
ControlRef inControl, |
TView** outView ) |
{ |
*outView = new THexEditorView( inControl ); |
if ( *outView != NULL ) |
return noErr; |
else |
return memFullErr; // could be a lie |
} |
//-------------------------------------------------------------------- |
// EventHandler |
//-------------------------------------------------------------------- |
// |
pascal OSStatus |
THexEditorView::EventHandler( |
EventHandlerCallRef inCallRef, |
EventRef inEvent, |
void* inUserData ) |
{ |
OSStatus err = eventNotHandledErr; |
THexEditorView* view = (THexEditorView*)inUserData; |
#pragma unused( inCallRef ) |
// The only event registered for the hex editor scrollable class |
// is the notification that the selection has changed. This requires |
// an update of the view. Just invalidate. We can be smarter if |
// we wanted to be. |
if ( GetEventClass( inEvent ) == kEventClassScrollableHexEditorView ) |
{ |
view->Invalidate(); |
err = noErr; |
} |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30