mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-26 02:01:03 +00:00
Create the NSEdgeInsets struct in NSGeometry, introduced in OSX 10.7. Add helper functions for creating these insets. This struct is not used in Base. Rather, it is used as part of newer APIs in GUI.
512 lines
14 KiB
Objective-C
512 lines
14 KiB
Objective-C
/** NSGeometry.m - geometry functions
|
|
* Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
|
|
*
|
|
* Written by: Adam Fedor <fedor@boulder.colorado.edu>
|
|
* Date: Mar 1995
|
|
*
|
|
* This file is part of the GNUstep Base Library.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02111 USA.
|
|
|
|
<title>NSGeometry class reference</title>
|
|
$Date$ $Revision$
|
|
*/
|
|
|
|
/*
|
|
* Define IN_NSGEOMETRY_M so that the Foundation/NSGeometry.h header can
|
|
* provide non-inline versions of the function implementations for us.
|
|
*/
|
|
#define IN_NSGEOMETRY_M
|
|
|
|
|
|
/**** Included Headers *******************************************************/
|
|
|
|
#import "common.h"
|
|
#include <math.h>
|
|
#import "Foundation/NSGeometry.h"
|
|
#import "Foundation/NSScanner.h"
|
|
#import "Foundation/NSNotification.h"
|
|
#import "GSPrivate.h"
|
|
|
|
static Class NSStringClass = 0;
|
|
static Class NSScannerClass = 0;
|
|
static SEL scanFloatSel;
|
|
static SEL scanStringSel;
|
|
static SEL scannerSel;
|
|
static BOOL (*scanFloatImp)(NSScanner*, SEL, CGFloat*);
|
|
static BOOL (*scanStringImp)(NSScanner*, SEL, NSString*, NSString**);
|
|
static id (*scannerImp)(Class, SEL, NSString*);
|
|
|
|
static inline void
|
|
setupCache(void)
|
|
{
|
|
if (NSStringClass == 0)
|
|
{
|
|
NSStringClass = [NSString class];
|
|
NSScannerClass = [NSScanner class];
|
|
if (sizeof(CGFloat) == sizeof(double))
|
|
{
|
|
scanFloatSel = @selector(scanDouble:);
|
|
}
|
|
else
|
|
{
|
|
scanFloatSel = @selector(scanFloat:);
|
|
}
|
|
scanStringSel = @selector(scanString:intoString:);
|
|
scannerSel = @selector(scannerWithString:);
|
|
scanFloatImp = (BOOL (*)(NSScanner*, SEL, CGFloat*))
|
|
[NSScannerClass instanceMethodForSelector: scanFloatSel];
|
|
scanStringImp = (BOOL (*)(NSScanner*, SEL, NSString*, NSString**))
|
|
[NSScannerClass instanceMethodForSelector: scanStringSel];
|
|
scannerImp = (id (*)(Class, SEL, NSString*))
|
|
[NSScannerClass methodForSelector: scannerSel];
|
|
}
|
|
}
|
|
|
|
static BOOL GSMacOSXCompatibleGeometry(void)
|
|
{
|
|
if (GSPrivateDefaultsFlag(GSOldStyleGeometry) == YES)
|
|
return NO;
|
|
return GSPrivateDefaultsFlag(GSMacOSXCompatible);
|
|
}
|
|
|
|
/**** Function Implementations ***********************************************/
|
|
/* Most of these are implemented in the header file as inline functkions */
|
|
|
|
NSRect
|
|
NSIntegralRect(NSRect aRect)
|
|
{
|
|
NSRect rect;
|
|
|
|
if (NSIsEmptyRect(aRect))
|
|
return NSMakeRect(0, 0, 0, 0);
|
|
|
|
rect.origin.x = floor(NSMinX(aRect));
|
|
rect.origin.y = floor(NSMinY(aRect));
|
|
rect.size.width = ceil(NSMaxX(aRect)) - rect.origin.x;
|
|
rect.size.height = ceil(NSMaxY(aRect)) - rect.origin.y;
|
|
return rect;
|
|
}
|
|
|
|
void
|
|
NSDivideRect(NSRect aRect,
|
|
NSRect *slice,
|
|
NSRect *remainder,
|
|
CGFloat amount,
|
|
NSRectEdge edge)
|
|
{
|
|
static NSRect sRect;
|
|
static NSRect rRect;
|
|
|
|
if (!slice)
|
|
slice = &sRect;
|
|
if (!remainder)
|
|
remainder = &rRect;
|
|
|
|
if (NSIsEmptyRect(aRect))
|
|
{
|
|
*slice = NSMakeRect(0,0,0,0);
|
|
*remainder = NSMakeRect(0,0,0,0);
|
|
return;
|
|
}
|
|
|
|
switch (edge)
|
|
{
|
|
case NSMinXEdge:
|
|
if (amount > aRect.size.width)
|
|
{
|
|
*slice = aRect;
|
|
*remainder = NSMakeRect(NSMaxX(aRect),
|
|
aRect.origin.y,
|
|
0,
|
|
aRect.size.height);
|
|
}
|
|
else
|
|
{
|
|
*slice = NSMakeRect(aRect.origin.x,
|
|
aRect.origin.y,
|
|
amount,
|
|
aRect.size.height);
|
|
*remainder = NSMakeRect(NSMaxX(*slice),
|
|
aRect.origin.y,
|
|
NSMaxX(aRect) - NSMaxX(*slice),
|
|
aRect.size.height);
|
|
}
|
|
break;
|
|
case NSMinYEdge:
|
|
if (amount > aRect.size.height)
|
|
{
|
|
*slice = aRect;
|
|
*remainder = NSMakeRect(aRect.origin.x,
|
|
NSMaxY(aRect),
|
|
aRect.size.width, 0);
|
|
}
|
|
else
|
|
{
|
|
*slice = NSMakeRect(aRect.origin.x,
|
|
aRect.origin.y,
|
|
aRect.size.width,
|
|
amount);
|
|
*remainder = NSMakeRect(aRect.origin.x,
|
|
NSMaxY(*slice),
|
|
aRect.size.width,
|
|
NSMaxY(aRect) - NSMaxY(*slice));
|
|
}
|
|
break;
|
|
case (NSMaxXEdge):
|
|
if (amount > aRect.size.width)
|
|
{
|
|
*slice = aRect;
|
|
*remainder = NSMakeRect(aRect.origin.x,
|
|
aRect.origin.y,
|
|
0,
|
|
aRect.size.height);
|
|
}
|
|
else
|
|
{
|
|
*slice = NSMakeRect(NSMaxX(aRect) - amount,
|
|
aRect.origin.y,
|
|
amount,
|
|
aRect.size.height);
|
|
*remainder = NSMakeRect(aRect.origin.x,
|
|
aRect.origin.y,
|
|
NSMinX(*slice) - aRect.origin.x,
|
|
aRect.size.height);
|
|
}
|
|
break;
|
|
case NSMaxYEdge:
|
|
if (amount > aRect.size.height)
|
|
{
|
|
*slice = aRect;
|
|
*remainder = NSMakeRect(aRect.origin.x,
|
|
aRect.origin.y,
|
|
aRect.size.width,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
*slice = NSMakeRect(aRect.origin.x,
|
|
NSMaxY(aRect) - amount,
|
|
aRect.size.width,
|
|
amount);
|
|
*remainder = NSMakeRect(aRect.origin.x,
|
|
aRect.origin.y,
|
|
aRect.size.width,
|
|
NSMinY(*slice) - aRect.origin.y);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/** Get a String Representation... **/
|
|
/* NOTE: Spaces around '=' so that old OpenStep implementations can
|
|
read our strings (Both GNUstep and Mac OS X can read these as well). */
|
|
|
|
NSString*
|
|
NSStringFromPoint(NSPoint aPoint)
|
|
{
|
|
setupCache();
|
|
if (GSMacOSXCompatibleGeometry() == YES)
|
|
return [NSStringClass stringWithFormat:
|
|
@"{%g, %g}", aPoint.x, aPoint.y];
|
|
else
|
|
return [NSStringClass stringWithFormat:
|
|
@"{x = %g; y = %g}", aPoint.x, aPoint.y];
|
|
}
|
|
|
|
NSString*
|
|
NSStringFromRect(NSRect aRect)
|
|
{
|
|
setupCache();
|
|
if (GSMacOSXCompatibleGeometry() == YES)
|
|
return [NSStringClass stringWithFormat:
|
|
@"{{%g, %g}, {%g, %g}}",
|
|
aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height];
|
|
else
|
|
return [NSStringClass stringWithFormat:
|
|
@"{x = %g; y = %g; width = %g; height = %g}",
|
|
aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height];
|
|
}
|
|
|
|
NSString*
|
|
NSStringFromSize(NSSize aSize)
|
|
{
|
|
setupCache();
|
|
if (GSMacOSXCompatibleGeometry() == YES)
|
|
return [NSStringClass stringWithFormat:
|
|
@"{%g, %g}", aSize.width, aSize.height];
|
|
else
|
|
return [NSStringClass stringWithFormat:
|
|
@"{width = %g; height = %g}", aSize.width, aSize.height];
|
|
}
|
|
|
|
NSPoint
|
|
NSPointFromString(NSString* string)
|
|
{
|
|
NSScanner *scanner;
|
|
NSPoint point;
|
|
|
|
setupCache();
|
|
scanner = (*scannerImp)(NSScannerClass, scannerSel, string);
|
|
if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"x", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &point.x)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @";", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"y", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &point.y)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL))
|
|
{
|
|
return point;
|
|
}
|
|
else
|
|
{
|
|
[scanner setScanLocation: 0];
|
|
if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &point.x)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @",", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &point.y)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL))
|
|
{
|
|
return point;
|
|
}
|
|
else
|
|
{
|
|
return NSMakePoint(0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
NSSize
|
|
NSSizeFromString(NSString* string)
|
|
{
|
|
NSScanner *scanner;
|
|
NSSize size;
|
|
|
|
setupCache();
|
|
scanner = (*scannerImp)(NSScannerClass, scannerSel, string);
|
|
if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"width", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &size.width)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @";", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"height", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &size.height)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL))
|
|
{
|
|
return size;
|
|
}
|
|
else
|
|
{
|
|
[scanner setScanLocation: 0];
|
|
if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &size.width)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @",", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &size.height)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL))
|
|
{
|
|
return size;
|
|
}
|
|
else
|
|
{
|
|
return NSMakeSize(0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
NSRect
|
|
NSRectFromString(NSString* string)
|
|
{
|
|
NSScanner *scanner;
|
|
NSRect rect;
|
|
|
|
setupCache();
|
|
scanner = (*scannerImp)(NSScannerClass, scannerSel, string);
|
|
if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"x", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.x)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @";", NULL)
|
|
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"y", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.y)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @";", NULL)
|
|
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"width", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.size.width)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @";", NULL)
|
|
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"height", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"=", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.size.height)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL))
|
|
{
|
|
return rect;
|
|
}
|
|
else
|
|
{
|
|
[scanner setScanLocation: 0];
|
|
if ((*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.x)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @",", NULL)
|
|
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.origin.y)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @",", NULL)
|
|
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"{", NULL)
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.size.width)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @",", NULL)
|
|
|
|
&& (*scanFloatImp)(scanner, scanFloatSel, &rect.size.height)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL)
|
|
&& (*scanStringImp)(scanner, scanStringSel, @"}", NULL))
|
|
{
|
|
return rect;
|
|
}
|
|
else
|
|
{
|
|
return NSMakeRect(0, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Tests for equality of floats/doubles.
|
|
* WARNING assumes the values are in the standard IEEE format ...
|
|
* this may not be true on all systems, though afaik it is the case
|
|
* on all systems we target.
|
|
*
|
|
* We use integer arithmetic for speed, assigning the float/double
|
|
* to an integer of the same size and then converting any negative
|
|
* values to twos-complement integer values so that a simple integer
|
|
* comparison can be done.
|
|
*
|
|
* MAX_ULP specified the number of Units in the Last Place by which
|
|
* the two values may differ and still be considered equal. A value
|
|
* of zero means that the two numbers must be identical.
|
|
*
|
|
* The way that infinity is represented means that it will be considered
|
|
* equal to MAX_FLT (or MAX_DBL) unless we are doing an exact comparison
|
|
* with MAX_ULP set to zero.
|
|
*
|
|
* The implementation will also treat two NaN values as being equal, which
|
|
* is technically wrong ... but is it worth adding a check for that?
|
|
*/
|
|
#define MAX_ULP 0
|
|
static inline BOOL
|
|
almostEqual(CGFloat A, CGFloat B)
|
|
{
|
|
#if MAX_ULP == 0
|
|
return (A == B) ? YES : NO;
|
|
#else /* MAX_UPL == 0 */
|
|
#if defined(CGFLOAT_IS_DBL)
|
|
union {int64_t i; double d;} valA, valB;
|
|
|
|
valA.d = A;
|
|
valB.d = B;
|
|
#if GS_SIZEOF_LONG == 8
|
|
if (valA.i < 0)
|
|
{
|
|
valA.i = 0x8000000000000000L - valA.i;
|
|
}
|
|
if (valB.i < 0)
|
|
{
|
|
valB.i = 0x8000000000000000L - valB.i;
|
|
}
|
|
if (labs(valA.i - valB.i) <= MAX_ULP)
|
|
{
|
|
return YES;
|
|
}
|
|
#else /* GS_SIZEOF_LONG == 8 */
|
|
if (valA.i < 0)
|
|
{
|
|
valA.i = 0x8000000000000000LL - valA.i;
|
|
}
|
|
if (valB.i < 0)
|
|
{
|
|
valB.i = 0x8000000000000000LL - valB.i;
|
|
}
|
|
if (llabs(valA.i - valB.i) <= MAX_ULP)
|
|
{
|
|
return YES;
|
|
}
|
|
#endif /* GS_SIZEOF_LONG == 8 */
|
|
return NO;
|
|
#else /* DEFINED(CGFLOAT_IS_DBL) */
|
|
union {int32_t i; float f;} valA, valB;
|
|
|
|
valA.f = A;
|
|
if (valA.i < 0)
|
|
{
|
|
valA.i = 0x80000000 - valA.i;
|
|
}
|
|
valB.f = B;
|
|
if (valB.i < 0)
|
|
{
|
|
valB.i = 0x80000000 - valB.i;
|
|
}
|
|
if (abs(valA.i - valB.i) <= MAX_ULP)
|
|
{
|
|
return YES;
|
|
}
|
|
return NO;
|
|
#endif /* DEFINED(CGFLOAT_IS_DBL) */
|
|
#endif /* MAX_UPL == 0 */
|
|
}
|
|
|
|
BOOL
|
|
NSEqualRects(NSRect aRect, NSRect bRect)
|
|
{
|
|
return (almostEqual(NSMinX(aRect), NSMinX(bRect))
|
|
&& almostEqual(NSMinY(aRect), NSMinY(bRect))
|
|
&& almostEqual(NSWidth(aRect), NSWidth(bRect))
|
|
&& almostEqual(NSHeight(aRect), NSHeight(bRect))) ? YES : NO;
|
|
}
|
|
|
|
BOOL
|
|
NSEqualSizes(NSSize aSize, NSSize bSize)
|
|
{
|
|
return (almostEqual(aSize.width, bSize.width)
|
|
&& almostEqual(aSize.height, bSize.height)) ? YES : NO;
|
|
}
|
|
|
|
BOOL
|
|
NSEqualPoints(NSPoint aPoint, NSPoint bPoint)
|
|
{
|
|
return (almostEqual(aPoint.x, bPoint.x)
|
|
&& almostEqual(aPoint.y, bPoint.y)) ? YES : NO;
|
|
}
|
|
|
|
BOOL
|
|
NSEdgeInsetsEqual(NSEdgeInsets e1, NSEdgeInsets e2)
|
|
{
|
|
return (
|
|
almostEqual(e1.top, e2.top)
|
|
&& almostEqual(e1.left, e2.left)
|
|
&& almostEqual(e1.bottom, e2.bottom)
|
|
&& almostEqual(e1.right, e2.right)
|
|
);
|
|
}
|
|
|