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.
Source/CSkObjects.c
/* |
File: CSkObjects.c |
Contains: Definition of (opaque) CSkObject structure, and various related routines. |
Refers to CSkShape for geometric description, and contains additional attributes. |
CSkObjets form a doubly-linked list to deal with z-ordering. |
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 © 2004 Apple Computer, Inc., All Rights Reserved |
*/ |
#include <Carbon/Carbon.h> |
#include <ApplicationServices/ApplicationServices.h> |
#include "CSkObjects.h" |
// also includes "CSkShapes.h" |
#include "CSkConstants.h" |
#include "CSkToolPalette.h" |
// CSkObjects (or "DrawObjects" as they were called in the first stages of development) are stored |
// in a double-linked list. They contain a CSkShapePtr to the geometry definition, and |
// specifications needed for drawing. |
struct CSkObject |
{ |
CSkShapePtr shape; |
float lineWidth; |
CGLineCap lineCap; |
CGLineJoin lineJoin; |
int lineStyle; |
CGrgba strokeColor; |
CGrgba fillColor; |
Boolean selected; |
CSkObjectPtr nextObj; |
CSkObjectPtr prevObj; |
}; |
//--------------------------------------------------------------------------------------------- |
// Return the specified line attributes lineWidth, lineCap, lineJoin, lineStyle. |
// No NULL-checks in the out-parameters because |
// a) the function is only used with parameters &lineWidth, &lineCap, &lineJoin, &lineStyle |
// b) I want to keep the code short |
void GetLineAttributes(const CSkObject* obj, float* width, CGLineCap* cap, CGLineJoin* join, int* style) |
{ |
if (obj != NULL) |
{ |
*width = obj->lineWidth; |
*cap = obj->lineCap; |
*join = obj->lineJoin; |
*style = obj->lineStyle; |
} |
else |
{ |
fprintf(stderr, "GetLineAttributes: NULL obj parameter\n"); |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Allocate new drawObject with attributes from the current settings in the CSkToolPalette. |
// Bounds are empty. |
CSkObjectPtr CreateDrawObj(WindowRef toolPalette, int shapeType) |
{ |
CSkObjectPtr obj = (CSkObjectPtr)NewPtrClear(sizeof(CSkObject)); |
if (obj != NULL) |
{ |
SetDrawObjAttributesFromToolPalette(obj, toolPalette); |
obj->shape = CSkShapeCreate(shapeType); |
} |
return obj; |
} |
//--------------------------------------------------------------------------------------------- |
// After mouse-tracking a selected CSkObject, we need an independent (unlinked) copy of it |
// to draw during dragging. |
CSkObjectPtr CopyDrawObject(const CSkObject* obj) |
{ |
CSkObjectPtr newObj = (CSkObjectPtr)NewPtrClear(sizeof(CSkObject)); |
if (newObj) |
{ |
memcpy(newObj, obj, sizeof(CSkObject)); |
newObj->shape = CSkShapeCreate(kUndefined); |
memcpy(newObj->shape, obj->shape, CSkShapeSize()); |
newObj->nextObj = NULL; |
newObj->prevObj = NULL; |
} |
return newObj; |
} |
//--------------------------------------------------------------------------------------------- |
void ReleaseDrawObj(CSkObjectPtr obj) |
{ |
CSkShapeRelease(obj->shape); |
DisposePtr((Ptr)obj); |
} |
void ReleaseDrawObjList(DrawObjListPtr objList) |
{ |
CSkObjectPtr obj = objList->firstItem; |
while ( obj != NULL ) |
{ |
CSkObjectPtr deleteThis = obj; |
obj = obj->nextObj; |
ReleaseDrawObj(deleteThis); |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Some obvious accessors |
int GetDrawObjShapeType( const CSkObject* drawObj ) |
{ |
return CSkShapeGetType(drawObj->shape); |
} |
CSkShape* GetCSkObjectShape( const CSkObject* drawObj ) |
{ |
return drawObj->shape; |
} |
float GetFillAlpha( const CSkObject* drawObj ) |
{ |
return drawObj->fillColor.a; |
} |
float GetStrokeAlpha( const CSkObject* drawObj ) |
{ |
return drawObj->strokeColor.a; |
} |
void SetDrawObjSelectState( CSkObjectPtr drawObj, Boolean selected ) |
{ |
drawObj->selected = selected; |
} |
Boolean IsDrawObjSelected( const CSkObject* drawObj ) |
{ |
return drawObj->selected; |
} |
//---------------------------------------------------------------------------- |
void SetDrawObjAttributesFromToolPalette(CSkObjectPtr obj, WindowRef toolPalette) |
{ |
obj->lineWidth = CSkToolPaletteGetLineWidth(toolPalette); |
obj->lineCap = CSkToolPaletteGetLineCap(toolPalette); |
obj->lineJoin = CSkToolPaletteGetLineJoin(toolPalette); |
obj->lineStyle = CSkToolPaletteGetLineStyle(toolPalette); |
CSkToolPaletteGetStrokeColor(toolPalette, &obj->strokeColor); |
CSkToolPaletteGetFillColor(toolPalette, &obj->fillColor); |
} |
void SetSelectedDrawObjAttributesFromToolPalette(DrawObjListPtr objListP, WindowRef toolPalette) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
SetDrawObjAttributesFromToolPalette(obj, toolPalette); |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetLineWidthOfSelecteds(DrawObjListPtr objListP, float lineWidth) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
{ |
if (lineWidth == kMakeItThinner) |
{ |
if (obj->lineWidth >= 2.0) |
obj->lineWidth -= 1.0; |
} |
else if (lineWidth == kMakeItThicker) |
obj->lineWidth += 1.0; |
else |
obj->lineWidth = lineWidth; |
} |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetLineCapOfSelecteds(DrawObjListPtr objListP, CGLineCap lineCap) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
obj->lineCap = lineCap; |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetLineJoinOfSelecteds(DrawObjListPtr objListP, CGLineJoin lineJoin) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
obj->lineJoin = lineJoin; |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetLineStyleOfSelecteds(DrawObjListPtr objListP, int lineStyle) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
obj->lineStyle = lineStyle; |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetStrokeColorOfSelecteds(DrawObjListPtr objListP, CGrgba* color) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
obj->strokeColor = *color; |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetStrokeAlphaOfSelecteds(DrawObjListPtr objListP, float alpha) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
obj->strokeColor.a = alpha; |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetFillColorOfSelecteds(DrawObjListPtr objListP, CGrgba* color) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
obj->fillColor = *color; |
obj = obj->nextObj; |
} |
} |
//---------------------------------------------------------------------------- |
void SetFillAlphaOfSelecteds(DrawObjListPtr objListP, float alpha) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
obj->fillColor.a = alpha; |
obj = obj->nextObj; |
} |
} |
//-------------------------------------------------------- |
CSkObjectPtr FirstSelectedObject(const DrawObjList* objListP) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
return obj; |
obj = obj->nextObj; |
} |
return NULL; |
} |
//------------------------------------------------------------------------ |
// Needed for dragselection of several objects |
void CSkObjListSelectWithinRect(DrawObjList* objListP, CGRect selectionRect) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
CGRect bounds = CSkShapeGetBounds(obj->shape); |
if ( CGRectContainsRect(selectionRect, bounds) ) |
obj->selected = true; |
obj = obj->nextObj; |
} |
} |
//------------------------------------------- |
///////////// Drawing Routines ////////////// |
// |
// We don't distinguish between "filled" and "not filled"; the result |
// "stroke only" is achieved by setting the fillColor alpha to fully |
// transparent. Similarly, if we want "filled only", we have to set |
// the alpha of the strokeColor to 0. |
// Consequently, we always pass kCGPathFillStroke to CGContextDrawPath. |
//------------------------------------------------------------------------- |
static void DrawRect(CGContextRef ctx, CGRect cgRect) |
{ |
CGContextBeginPath(ctx); // reset current path to empty |
CGContextAddRect(ctx, cgRect); // current path now represents cgRect |
CGContextDrawPath(ctx, kCGPathFillStroke); |
} |
//------------------------------------------------------------------------- |
static void DrawOval(CGContextRef ctx, CGRect cgRect) |
{ |
const float TWOPI = 6.283185307; |
float halfWidth = 0.5 * CGRectGetWidth(cgRect); |
float halfHeight = 0.5 * CGRectGetHeight(cgRect); |
float centerX, centerY, radius; |
float scaleX, scaleY; |
if (halfWidth < halfHeight) |
{ |
radius = halfWidth; |
scaleX = 1.0; |
scaleY = halfHeight / halfWidth; |
centerX = cgRect.origin.x + halfWidth; |
centerY = (cgRect.origin.y + halfHeight) / scaleY; |
} |
else |
{ |
radius = halfHeight; |
scaleX = halfWidth / halfHeight; |
scaleY = 1.0; |
centerX = (cgRect.origin.x + halfWidth) / scaleX; |
centerY = cgRect.origin.y + halfHeight; |
} |
CGContextSaveGState(ctx); // because we temporarily change the CTM |
CGContextScaleCTM(ctx, scaleX, scaleY); // so the full-circle arc will appear as oval |
CGContextBeginPath(ctx); |
CGContextAddArc(ctx, centerX, centerY, radius, 0.0, TWOPI, 0); |
CGContextClosePath(ctx); |
CGContextRestoreGState(ctx); // back to the previous CTM |
CGContextDrawPath(ctx, kCGPathFillStroke); |
} |
//-------------------------------------------------------------------------------------- |
static void DrawRRect(CGContextRef ctx, CGRect cgRect, float rX, float rY) |
{ |
CGContextBeginPath(ctx); |
if ((rX > 0) && (rY > 0)) |
{ |
float width = CGRectGetWidth(cgRect); |
float height = CGRectGetHeight(cgRect); |
float fw = width / rX; |
float fh = height / rY; |
if (fw < 2) |
{ |
rX = width / 2; |
fw = 2; |
} |
if (fh < 2) |
{ |
rY = height / 2; |
fh = 2; |
} |
CGContextSaveGState(ctx); |
CGContextTranslateCTM(ctx, CGRectGetMinX(cgRect), CGRectGetMinY(cgRect)); |
CGContextScaleCTM(ctx, rX, rY); |
CGContextMoveToPoint(ctx, fw, fh/2); |
CGContextAddArcToPoint(ctx, fw, fh, fw/2, fh, 1); |
CGContextAddArcToPoint(ctx, 0, fh, 0, fh/2, 1); |
CGContextAddArcToPoint(ctx, 0, 0, fw/2, 0, 1); |
CGContextAddArcToPoint(ctx, fw, 0, fw, fh/2, 1); |
CGContextRestoreGState(ctx); |
} |
else |
{ |
CGContextAddRect(ctx, cgRect); |
} |
CGContextClosePath(ctx); |
CGContextDrawPath(ctx, kCGPathFillStroke); |
} |
//-------------------------------------------------------------------------------------- |
static void DrawCGLine(CGContextRef ctx, CGPoint a, CGPoint b) |
{ |
CGContextMoveToPoint( ctx, a.x, a.y ); |
CGContextAddLineToPoint( ctx, b.x, b.y ); |
CGContextStrokePath( ctx ); |
} |
//-------------------------------------------------------------------------------------- |
// Keep this separate from RenderCSkObject; this way, RenderCSkObject can |
// be reused from within the mousetracking loops when drawing into an overlay window. |
void SetContextStateForDrawObject(CGContextRef ctx, const CSkObject* obj) |
{ |
CGContextSetLineWidth(ctx, obj->lineWidth); |
CGContextSetLineCap(ctx, obj->lineCap); |
CGContextSetLineJoin(ctx, obj->lineJoin); |
if (obj->lineStyle == kStyleDashed) |
{ |
float dashLengths[2] = { obj->lineWidth + 4, obj->lineWidth + 4 }; |
CGContextSetLineDash(ctx, 1.0, dashLengths, 2); |
} |
CGContextSetStrokeColor( ctx, (float *)&(obj->strokeColor)); |
CGContextSetFillColor( ctx, (float *)&(obj->fillColor)); |
} |
//-------------------------------------------------------------------------------------- |
// RenderCSkObject is being called from RenderDrawObjList, and also during MouseTracking |
// (see CSkWindow.c). |
void RenderCSkObject(CGContextRef ctx, const CSkObject* obj, Boolean drawSelection) |
{ |
int shapeType = CSkShapeGetType(obj->shape); |
CGRect shapeBounds = CSkShapeGetBounds(obj->shape); |
CGPoint a, b; |
if ( shapeType == kLineShape ) |
{ |
CSkShapeGetLine(obj->shape, &a, &b); |
DrawCGLine(ctx, a, b); |
} |
else switch (shapeType) |
{ |
case kRectShape: |
DrawRect(ctx, shapeBounds); |
break; |
case kOvalShape: |
DrawOval(ctx, shapeBounds); |
break; |
case kRRectShape: |
{ |
float rX, rY; |
CSkShapeGetRRectRadii(obj->shape, &rX, &rY); |
DrawRRect(ctx, shapeBounds, rX, rY); |
} |
break; |
} |
if (drawSelection && obj->selected) // draw little "grabber" squares |
{ |
CGRect grabRect; |
UInt32 grabNum = 0; |
CGContextSaveGState(ctx); // because we are changing colors and line width |
CGContextSetRGBFillColor(ctx, 0.9, 0.9, 0.9, 0.7); // ltGray, a little transparent |
CGContextSetRGBStrokeColor(ctx, 0, 0, 0, 1.0); // black |
CGContextSetLineWidth(ctx, 1.0); |
while (NextGrabberRect(obj->shape, &grabNum, &grabRect)) |
{ |
DrawRect(ctx, grabRect); |
// Alternatively: |
// CGContextStrokeRect(ctx, grabRect); |
// CGContextFillRect(ctx, grabRect); |
} |
CGContextRestoreGState(ctx); |
} |
} // RenderCSkObject |
//--------------------------------------------------------------------------------------------- |
// Draw the CSkObjects in the linked list from back to front. For each object, the GState needs |
// to be saved and restored. |
void RenderDrawObjList(CGContextRef ctx, const DrawObjList* objListP, Boolean drawSelection) |
{ |
CSkObjectPtr obj = objListP->lastItem; // draw from back to front |
while (obj != NULL) |
{ |
CGContextSaveGState(ctx); // because SetContextStateForDrawObject is doing what it says it will |
SetContextStateForDrawObject(ctx, obj); |
RenderCSkObject(ctx, obj, drawSelection); |
CGContextRestoreGState(ctx); // undo the changes for the specific obj drawing |
obj = obj->prevObj; |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Multiply in a transparency factor. Used for tracking feedback when resizing an object. |
void MakeDrawObjTransparent(CSkObject* obj, float alpha) |
{ |
obj->strokeColor.a *= alpha; |
obj->fillColor.a *= alpha; |
} |
//--------------------------------------------------------------------------------------------- |
// The following is used during moving selected objects around (ctx is overlayWindowContext). |
// Draw the selected objects only, and with an additional alpha multiplied in for more transparency. |
void RenderSelectedDrawObjs(CGContextRef ctx, const DrawObjList* objListP, float offsetX, float offsetY, float alpha) |
{ |
CSkObjectPtr obj = objListP->lastItem; // draw from back to front |
CGContextSaveGState(ctx); |
CGContextTranslateCTM(ctx, offsetX, offsetY); |
while (obj != NULL) |
{ |
if (obj->selected) |
{ |
CGrgba saveStrokeColor = obj->strokeColor; |
CGrgba saveFillColor = obj->fillColor; |
MakeDrawObjTransparent(obj, alpha); |
SetContextStateForDrawObject(ctx, obj); |
RenderCSkObject(ctx, obj, true); |
obj->strokeColor = saveStrokeColor; |
obj->fillColor = saveFillColor; |
} |
obj = obj->prevObj; |
} |
CGContextRestoreGState(ctx); |
} |
//--------------------------------------------------------------------------------------------- |
// Called at the end of mouse tracking when selected objects have been moved |
void MoveSelectedDrawObjs(DrawObjList* objListP, float offsetX, float offsetY) |
{ |
CSkObjectPtr obj = objListP->firstItem; |
while (obj != NULL) |
{ |
if (obj->selected) |
{ |
CSkShapeOffset(obj->shape, offsetX, offsetY); |
} |
obj = obj->nextObj; |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Select all, or deselect all |
void CSkObjListSetSelectState(DrawObjListPtr objList, Boolean state) |
{ |
CSkObjectPtr obj = objList->firstItem; |
while (obj != NULL) |
{ |
obj->selected = state; |
obj = obj->nextObj; |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Called when reording objects in the list |
static CSkObjectPtr GetFirstSelectedDrawObj(const DrawObjList* objList) |
{ |
CSkObjectPtr obj = objList->firstItem; |
CSkObjectPtr selectedObject = NULL; |
while (obj != NULL) |
{ |
if (obj->selected == true) |
{ |
selectedObject = obj; |
break; |
} |
obj = obj->nextObj; |
} |
return selectedObject; |
} |
//---------------------------------------------------------------------------- |
// Some obvious linked list management routines |
void AddDrawObjToList(DrawObjListPtr objList, CSkObjectPtr obj) |
{ |
CSkObjectPtr firstObj = objList->firstItem; |
obj->prevObj = NULL; // always put it in front of the list |
obj->nextObj = firstObj; |
objList->firstItem = obj; |
if (firstObj == NULL) |
{ |
objList->lastItem = obj; |
} |
else |
{ |
firstObj->prevObj = obj; |
} |
} |
//---------------------------------------------------------------------- |
static void RemoveDrawObjFromList(DrawObjListPtr objList, const CSkObject* obj) |
{ |
CSkObjectPtr prevObj = obj->prevObj; |
CSkObjectPtr nextObj = obj->nextObj; |
if (prevObj != NULL) |
prevObj->nextObj = nextObj; |
if (nextObj != NULL) |
nextObj->prevObj = prevObj; |
if (objList->firstItem == obj) |
objList->firstItem = obj->nextObj; |
if (objList->lastItem == obj) |
objList->lastItem = obj->prevObj; |
} |
//---------------------------------------------------------------------- |
void RemoveSelectedDrawObjs(DrawObjListPtr objList) |
{ |
CSkObjectPtr obj = objList->firstItem; |
while (obj != NULL) |
{ |
CSkObjectPtr nextObj = obj->nextObj; |
if (obj->selected) |
{ |
RemoveDrawObjFromList(objList, obj); |
} |
obj = nextObj; |
} |
} |
//---------------------------------------------------------------------- |
static void InsertDrawObjBefore(DrawObjListPtr objList, CSkObjectPtr obj, CSkObjectPtr beforeObj) |
{ |
CSkObjectPtr prevObj = beforeObj->prevObj; |
if (prevObj != NULL) |
prevObj->nextObj = obj; |
obj->prevObj = prevObj; |
obj->nextObj = beforeObj; |
beforeObj->prevObj = obj; |
if (objList->firstItem == beforeObj) |
objList->firstItem = obj; |
} |
//---------------------------------------------------------------------- |
static void InsertDrawObjAfter(DrawObjListPtr objList, CSkObjectPtr obj, CSkObjectPtr afterObj) |
{ |
CSkObjectPtr nextObj = afterObj->nextObj; |
if (nextObj != NULL) |
nextObj->prevObj = obj; |
obj->nextObj = nextObj; |
obj->prevObj = afterObj; |
afterObj->nextObj = obj; |
if (objList->lastItem == afterObj) |
objList->lastItem = obj; |
} |
//---------------------------------------------------------------------- |
static void DuplicateDrawObj(DrawObjListPtr objList, CSkObjectPtr obj, CGPoint offset) |
{ |
CSkObjectPtr tempObj = CopyDrawObject(obj); |
// Always deselect the original and select the new |
obj->selected = false; |
tempObj->selected = true; |
// tempObj->bounds = CGRectOffset(tempObj->bounds, offset.x, offset.y); -- this normalizes the rect, which is not what we want for lines |
CSkShapeOffset(tempObj->shape, offset.x, offset.y); |
InsertDrawObjBefore(objList, tempObj, obj); |
} |
//---------------------------------------------------------------------- |
void DuplicateSelectedDrawObjs(DrawObjListPtr objList, CGPoint offset) |
{ |
CSkObjectPtr obj = objList->firstItem; |
while (obj != NULL) |
{ |
CSkObjectPtr nextObj = obj->nextObj; |
if (obj->selected) |
{ |
DuplicateDrawObj(objList, obj, offset); |
} |
obj = nextObj; |
} |
} |
//---------------------------------------------------------------------- |
void MoveObjectForward(DrawObjListPtr objList) |
{ |
CSkObjectPtr obj = GetFirstSelectedDrawObj(objList); |
if (obj != NULL) |
{ |
// remember where we were |
CSkObjectPtr prevObj = obj->prevObj; |
// only remove and insert if there is a previous item |
if (prevObj != NULL) |
{ |
RemoveDrawObjFromList(objList, obj); // pull obj out of list |
InsertDrawObjBefore(objList, obj, prevObj); |
} |
} |
} |
//---------------------------------------------------------------------- |
void MoveObjectBackward(DrawObjListPtr objList) |
{ |
CSkObjectPtr obj = GetFirstSelectedDrawObj(objList); |
if (obj != NULL) |
{ |
// remember where we were |
CSkObjectPtr nextObj = obj->nextObj; |
// only remove and insert if there is a next |
if (nextObj != NULL) |
{ |
RemoveDrawObjFromList(objList, obj); // pull obj out of list |
InsertDrawObjAfter(objList, obj, nextObj); |
} |
} |
} |
//---------------------------------------------------------------------- |
void MoveObjectToFront(DrawObjListPtr objList) |
{ |
CSkObjectPtr obj = GetFirstSelectedDrawObj(objList); |
if ((obj != NULL) && (obj != objList->firstItem)) |
{ |
RemoveDrawObjFromList(objList, obj); // pull obj out of list |
AddDrawObjToList(objList, obj); // add it in front |
} |
} |
//---------------------------------------------------------------------- |
void MoveObjectToBack(DrawObjListPtr objList) |
{ |
CSkObjectPtr obj = GetFirstSelectedDrawObj(objList); |
if ((obj != NULL) && (obj != objList->lastItem)) |
{ |
RemoveDrawObjFromList(objList, obj); // pull obj out of list |
obj->prevObj = objList->lastItem; // hook it in at the end |
obj->prevObj->nextObj = obj; |
objList->lastItem = obj; |
obj->nextObj = NULL; |
} |
} |
//-------------------------------------------------------------------------------------- |
// Return the first (front-to-back) object hit by docPt, or NULL. |
// If hit, *outFlags contains which "grabber" handle has been hit, if any (numbered 1..nGrabbers). |
// Note the use of a 1x1-pixel CGBitmapContext bmCtx for hit-testing. We keep it around and pass it in; |
// but you could also create it on the spot. |
CSkObjectPtr DrawObjListHitTesting(DrawObjListPtr objList, CGContextRef bmCtx, CGAffineTransform m, CGPoint windowCtxPt, CGPoint docPt, UInt32* grabNum) |
{ |
UInt32* baseAddr = (UInt32*)CGBitmapContextGetData(bmCtx); // Assume 4 bytes per pixel! |
CSkObjectPtr obj = objList->firstItem; // traverse front to back |
Boolean hit = false; |
CGContextSaveGState(bmCtx); // because we are temporarily changing the CTM |
CGContextTranslateCTM( bmCtx, -windowCtxPt.x, -windowCtxPt.y ); // move 1x1 bitmap context to "windowCtxPt" |
CGContextConcatCTM(bmCtx, m); // apply document transform for drawing |
while ((obj != NULL) && !hit) |
{ |
int shapeType = CSkShapeGetType(obj->shape); |
float d = 0.5 * obj->lineWidth; |
CGRect cgR = CSkShapeGetBounds(obj->shape); |
cgR = CGRectInset(cgR, -d, -d); |
if (CGRectContainsPoint(cgR, docPt)) |
{ |
cgR = CGRectInset(cgR, d, d); // restore original shape bounds |
// If the obj is selected, check for a hit in the grabbers first since they overlap the obj |
if (obj->selected && (grabNum != NULL)) |
{ |
*grabNum = FindGrabberHit(obj->shape, docPt); |
if (*grabNum > 0) |
{ |
fprintf(stderr, "0x%x: grabber %d hit\n", (uint)obj, (int)*grabNum); |
hit = true; |
break; // done - return obj |
} |
} |
if (shapeType == kRectShape) |
{ |
// fprintf(stderr, "0x%x: Rectangle hit\n", (uint)obj); |
hit = true; |
break; // done - return obj |
} |
// Horizontal or vertical line? Hit if CGRectContainsPoint |
if ( (shapeType == kLineShape) |
&& ((cgR.size.height == 0) || (cgR.size.width == 0)) |
) |
{ |
// fprintf(stderr, "0x%x: Horz/Vert Line hit\n", (uint)obj); |
hit = true; |
break; // done - return obj |
} |
// If none of the above, draw the object into the bitmapContext, and check whether this changed the point |
cgR = CGRectInset(cgR, -d, -d); // outset once more |
*baseAddr = 0; // clear the pixel in bmCtx |
SetContextStateForDrawObject(bmCtx, obj); |
RenderCSkObject(bmCtx, obj, true); |
if (*baseAddr != 0) // got it! |
{ |
hit = true; |
// fprintf(stderr, "0x%x: BitmapContext hit\n", (uint)obj); |
break; // done - return obj |
} |
else |
{ |
// fprintf(stderr, "no BitmapContext hit for (%f, %f) in (%f, %f, %f, %f)\n", |
// docPt.x, docPt.y, |
// cgR.origin.x, cgR.origin.y, cgR.size.width, cgR.size.height); |
} |
} |
if (!hit) |
obj = obj->nextObj; |
} |
// obj == NULL if there was no hit |
CGContextRestoreGState(bmCtx); |
return obj; |
} |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-03-23