view mac/dw.m @ 2909:3fe7641f027c

WARNING: Fix an API inconsistency in dw_notebook_page_destroy/set() These two APIs incorrectly referenced the page ID as an int not long. This change may cause ABI problems with programs that use these on some platforms. Recompile your apps that use these functions for safety.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Tue, 27 Dec 2022 00:58:58 +0000
parents 761b7a12b079
children 7d3276f80132
line wrap: on
line source

/*
 * Dynamic Windows:
 *          A GTK like implementation of the MacOS GUI using Cocoa
 *
 * (C) 2011-2022 Brian Smith <brian@dbsoft.org>
 * (C) 2011-2021 Mark Hessling <mark@rexx.org>
 *
 * Requires 10.5 or later.
 * clang -std=c99 -g -o dwtest -D__MAC__ -I. dwtest.c mac/dw.m -framework Cocoa -framework WebKit
 */
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#include "dw.h"
#include <sys/utsname.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <math.h>

/* Create a define to let us know to include Snow Leopard specific features */
#if defined(MAC_OS_X_VERSION_10_6) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define DWPasteboardTypeString NSPasteboardTypeString
#define BUILDING_FOR_SNOW_LEOPARD
#else
#define DWPasteboardTypeString NSStringPboardType
#endif

/* Create a define to let us know to include Lion specific features */
#if defined(MAC_OS_X_VERSION_10_7) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define BUILDING_FOR_LION
#define DW_USE_NSVIEW
#endif

/* Create a define to let us know to include Mountain Lion specific features */
#if defined(MAC_OS_X_VERSION_10_8) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define BUILDING_FOR_MOUNTAIN_LION
#endif

/* Macros to handle local auto-release pools */
#define DW_LOCAL_POOL_IN NSAutoreleasePool *localpool = nil; \
        if(DWThread != (DWTID)-1 && pthread_self() != DWThread) \
            localpool = [[NSAutoreleasePool alloc] init];
#define DW_LOCAL_POOL_OUT if(localpool) [localpool drain];

/* Handle deprecation of several constants in 10.10...
 * the replacements are not available in earlier versions.
 */
#if defined(MAC_OS_X_VERSION_10_10) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define DWModalResponseOK NSModalResponseOK
#define DWModalResponseCancel NSModalResponseCancel
#define DWPaperOrientationPortrait NSPaperOrientationPortrait
#define DWCalendarUnitDay NSCalendarUnitDay
#define DWCalendarUnitMonth NSCalendarUnitMonth
#define DWCalendarUnitYear NSCalendarUnitYear
#define DWCalendarIdentifierGregorian NSCalendarIdentifierGregorian
#define BUILDING_FOR_YOSEMITE
#else
#define DWModalResponseOK NSOKButton
#define DWModalResponseCancel NSCancelButton
#define DWPaperOrientationPortrait NSPortraitOrientation
#define DWCalendarUnitDay NSDayCalendarUnit
#define DWCalendarUnitMonth NSMonthCalendarUnit
#define DWCalendarUnitYear NSYearCalendarUnit
#define DWCalendarIdentifierGregorian (NSString *)kCFGregorianCalendar
#endif

/* Handle deprecation of several constants in 10.12...
 * the replacements are not available in earlier versions.
 */
#if defined(MAC_OS_X_VERSION_10_12) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define DWButtonTypeSwitch NSButtonTypeSwitch
#define DWButtonTypeRadio NSButtonTypeRadio
#define DWButtonTypeMomentaryPushIn NSButtonTypeMomentaryPushIn
#define DWBezelStyleRegularSquare NSBezelStyleRegularSquare
#define DWBezelStyleRounded NSBezelStyleRounded
#define DWEventTypeApplicationDefined NSEventTypeApplicationDefined
#define DWCompositingOperationSourceOver NSCompositingOperationSourceOver
#define DWEventTypeKeyDown NSEventTypeKeyDown
#define DWEventModifierFlagControl NSEventModifierFlagControl
#define DWEventModifierFlagCommand NSEventModifierFlagCommand
#define DWEventModifierFlagOption NSEventModifierFlagOption
#define DWEventTypeRightMouseDown NSEventTypeRightMouseDown
#define DWEventTypeRightMouseUp NSEventTypeRightMouseUp
#define DWEventTypeLeftMouseDown NSEventTypeLeftMouseDown
#define DWEventTypeLeftMouseUp NSEventTypeLeftMouseUp
#define DWEventTypeOtherMouseDown NSEventTypeOtherMouseDown
#define DWEventTypeOtherMouseUp NSEventTypeOtherMouseUp
#define DWEventModifierFlagDeviceIndependentFlagsMask NSEventModifierFlagDeviceIndependentFlagsMask
#define DWEventMaskAny NSEventMaskAny
#define DWAlertStyleCritical NSAlertStyleCritical
#define DWAlertStyleInformational NSAlertStyleInformational
#define DWAlertStyleWarning NSAlertStyleWarning
#define DWTextAlignmentCenter NSTextAlignmentCenter
#define DWTextAlignmentRight NSTextAlignmentRight
#define DWTextAlignmentLeft NSTextAlignmentLeft
#define DWEventMaskLeftMouseUp NSEventMaskLeftMouseUp
#define DWEventMaskLeftMouseDown NSEventMaskLeftMouseDown
#define DWEventMaskRightMouseUp NSEventMaskRightMouseUp
#define DWEventMaskRightMouseDown NSEventMaskRightMouseDown
#define DWWindowStyleMaskResizable NSWindowStyleMaskResizable
#define BUILDING_FOR_SIERRA
#else
#define DWButtonTypeSwitch NSSwitchButton
#define DWButtonTypeRadio NSRadioButton
#define DWButtonTypeMomentaryPushIn NSMomentaryPushInButton
#define DWBezelStyleRegularSquare NSRegularSquareBezelStyle
#define DWBezelStyleRounded NSRoundedBezelStyle
#define DWEventTypeApplicationDefined NSApplicationDefined
#define DWCompositingOperationSourceOver NSCompositeSourceOver
#define DWEventTypeKeyDown NSKeyDown
#define DWEventModifierFlagControl NSControlKeyMask
#define DWEventModifierFlagCommand NSCommandKeyMask
#define DWEventModifierFlagOption NSAlternateKeyMask
#define DWEventTypeRightMouseDown NSRightMouseDown
#define DWEventTypeRightMouseUp NSRightMouseUp
#define DWEventTypeLeftMouseDown NSLeftMouseDown
#define DWEventTypeLeftMouseUp NSLeftMouseUp
#define DWEventTypeOtherMouseDown NSOtherMouseDown
#define DWEventTypeOtherMouseUp NSOtherMouseUp
#define DWEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask
#define DWEventMaskAny NSAnyEventMask
#define DWAlertStyleCritical NSCriticalAlertStyle
#define DWAlertStyleInformational NSInformationalAlertStyle
#define DWAlertStyleWarning NSWarningAlertStyle
#define DWTextAlignmentCenter NSCenterTextAlignment
#define DWTextAlignmentRight NSRightTextAlignment
#define DWTextAlignmentLeft NSLeftTextAlignment
#define DWEventMaskLeftMouseUp NSLeftMouseUpMask
#define DWEventMaskLeftMouseDown NSLeftMouseDownMask
#define DWEventMaskRightMouseUp NSRightMouseUpMask
#define DWEventMaskRightMouseDown NSRightMouseDownMask
#define DWWindowStyleMaskResizable NSResizableWindowMask
#endif

/* Handle deprecation of several constants in 10.13...
 * the replacements are not available in earlier versions.
 */
#if defined(MAC_OS_X_VERSION_10_13) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define DWControlStateValueOff NSControlStateValueOff
#define DWControlStateValueOn NSControlStateValueOn
#define BUILDING_FOR_HIGH_SIERRA
#else
#define DWControlStateValueOff NSOffState
#define DWControlStateValueOn NSOnState
#endif

/* Handle deprecation of several constants in 10.14...
 * the replacements are not available in earlier versions.
 */
#if defined(MAC_OS_X_VERSION_10_14) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define DWProgressIndicatorStyleBar NSProgressIndicatorStyleBar
#define BUILDING_FOR_MOJAVE
#import <UserNotifications/UserNotifications.h>
#else
#define DWProgressIndicatorStyleBar NSProgressIndicatorBarStyle
#endif

/* Handle deprecation of constants in 10.15... */
#if defined(MAC_OS_X_VERSION_10_15) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define BUILDING_FOR_CATALINA
#define WK_API_ENABLED 1
#endif

/* Handle deprecation of constants in 11.0 (also known as 10.16)... */
#if (defined(MAC_OS_VERSION_11_0) || defined(MAC_OS_X_VERSION_10_16)) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_10_16) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#import <UniformTypeIdentifiers/UTDefines.h>
#import <UniformTypeIdentifiers/UTType.h>
#import <UniformTypeIdentifiers/UTCoreTypes.h>
#define DWPrintingPaginationModeFit NSPrintingPaginationModeFit
#define DWDatePickerModeSingle NSDatePickerModeSingle
#define DWDatePickerStyleClockAndCalendar NSDatePickerStyleClockAndCalendar
#define DWDatePickerElementFlagYearMonthDay NSDatePickerElementFlagYearMonthDay
#define BUILDING_FOR_BIG_SUR
#else
#define DWPrintingPaginationModeFit NSFitPagination
#define DWDatePickerModeSingle NSSingleDateMode
#define DWDatePickerStyleClockAndCalendar NSClockAndCalendarDatePickerStyle
#define DWDatePickerElementFlagYearMonthDay NSYearMonthDayDatePickerElementFlag
#endif

/* Handle deprecation of constants in 12.0 */
#if defined(MAC_OS_VERSION_12_0) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define BUILDING_FOR_MONTEREY
#endif

/* Handle deprecation of constants in 13.0 */
#if defined(MAC_OS_VERSION_13_0) && ((defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_0) || !defined(MAC_OS_X_VERSION_MAX_ALLOWED))
#define BUILDING_FOR_VENTURA
#endif

#ifdef __clang__
#define _DW_ELSE_AVAILABLE  \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")

#define _DW_END_AVAILABLE _Pragma("clang diagnostic pop")
#else
#define _DW_ELSE_AVAILABLE
#define _DW_END_AVAILABLE
#endif

/* Macros to encapsulate running functions on the main thread
 * on Mojave or later... and locking mutexes on earlier versions.
 */
#ifdef BUILDING_FOR_MOJAVE
#define DW_FUNCTION_INIT
#define DW_FUNCTION_DEFINITION(func, rettype, ...)  void _##func(NSPointerArray *_args); \
rettype API func(__VA_ARGS__) { \
    DW_LOCAL_POOL_IN; \
    NSPointerArray *_args = [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsOpaqueMemory]; \
    [_args addPointer:(void *)_##func];
#define DW_FUNCTION_ADD_PARAM1(param1) [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM2(param1, param2) [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM3(param1, param2, param3) [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)&param3]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM4(param1, param2, param3, param4) [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)&param3]; \
    [_args addPointer:(void *)&param4]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM5(param1, param2, param3, param4, param5) [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)&param3]; \
    [_args addPointer:(void *)&param4]; \
    [_args addPointer:(void *)&param5]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM6(param1, param2, param3, param4, param5, param6) \
    [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)&param3]; \
    [_args addPointer:(void *)&param4]; \
    [_args addPointer:(void *)&param5]; \
    [_args addPointer:(void *)&param6]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM7(param1, param2, param3, param4, param5, param6, param7) \
    [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)&param3]; \
    [_args addPointer:(void *)&param4]; \
    [_args addPointer:(void *)&param5]; \
    [_args addPointer:(void *)&param6]; \
    [_args addPointer:(void *)&param7]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM8(param1, param2, param3, param4, param5, param6, param7, param8) \
    [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)&param3]; \
    [_args addPointer:(void *)&param4]; \
    [_args addPointer:(void *)&param5]; \
    [_args addPointer:(void *)&param6]; \
    [_args addPointer:(void *)&param7]; \
    [_args addPointer:(void *)&param8]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_ADD_PARAM9(param1, param2, param3, param4, param5, param6, param7, param8, param9) \
    [_args addPointer:(void *)&param1]; \
    [_args addPointer:(void *)&param2]; \
    [_args addPointer:(void *)&param3]; \
    [_args addPointer:(void *)&param4]; \
    [_args addPointer:(void *)&param5]; \
    [_args addPointer:(void *)&param6]; \
    [_args addPointer:(void *)&param7]; \
    [_args addPointer:(void *)&param8]; \
    [_args addPointer:(void *)&param9]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_fg_color_key)]; \
    [_args addPointer:(void *)pthread_getspecific(_dw_bg_color_key)];
#define DW_FUNCTION_RESTORE_PARAM1(param1, vartype1) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:2]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:3];
#define DW_FUNCTION_RESTORE_PARAM2(param1, vartype1, param2, vartype2) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:3]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:4];
#define DW_FUNCTION_RESTORE_PARAM3(param1, vartype1, param2, vartype2, param3, vartype3) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    vartype3 param3 = *((vartype3 *)[_args pointerAtIndex:3]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:4]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:5];
#define DW_FUNCTION_RESTORE_PARAM4(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    vartype3 param3 = *((vartype3 *)[_args pointerAtIndex:3]); \
    vartype4 param4 = *((vartype4 *)[_args pointerAtIndex:4]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:5]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:6];
#define DW_FUNCTION_RESTORE_PARAM5(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    vartype3 param3 = *((vartype3 *)[_args pointerAtIndex:3]); \
    vartype4 param4 = *((vartype4 *)[_args pointerAtIndex:4]); \
    vartype5 param5 = *((vartype5 *)[_args pointerAtIndex:5]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:6]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:7];
#define DW_FUNCTION_RESTORE_PARAM6(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    vartype3 param3 = *((vartype3 *)[_args pointerAtIndex:3]); \
    vartype4 param4 = *((vartype4 *)[_args pointerAtIndex:4]); \
    vartype5 param5 = *((vartype5 *)[_args pointerAtIndex:5]); \
    vartype6 param6 = *((vartype6 *)[_args pointerAtIndex:6]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:7]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:8];
#define DW_FUNCTION_RESTORE_PARAM7(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6, param7, vartype7) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    vartype3 param3 = *((vartype3 *)[_args pointerAtIndex:3]); \
    vartype4 param4 = *((vartype4 *)[_args pointerAtIndex:4]); \
    vartype5 param5 = *((vartype5 *)[_args pointerAtIndex:5]); \
    vartype6 param6 = *((vartype6 *)[_args pointerAtIndex:6]); \
    vartype7 param7 = *((vartype7 *)[_args pointerAtIndex:7]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:8]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:9];
#define DW_FUNCTION_RESTORE_PARAM8(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6, param7, vartype7, param8, vartype8) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    vartype3 param3 = *((vartype3 *)[_args pointerAtIndex:3]); \
    vartype4 param4 = *((vartype4 *)[_args pointerAtIndex:4]); \
    vartype5 param5 = *((vartype5 *)[_args pointerAtIndex:5]); \
    vartype6 param6 = *((vartype6 *)[_args pointerAtIndex:6]); \
    vartype7 param7 = *((vartype7 *)[_args pointerAtIndex:7]); \
    vartype8 param8 = *((vartype8 *)[_args pointerAtIndex:8]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:9]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:10];
#define DW_FUNCTION_RESTORE_PARAM9(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6, param7, vartype7, param8, vartype8, param9, vartype9) \
    vartype1 param1 = *((vartype1 *)[_args pointerAtIndex:1]); \
    vartype2 param2 = *((vartype2 *)[_args pointerAtIndex:2]); \
    vartype3 param3 = *((vartype3 *)[_args pointerAtIndex:3]); \
    vartype4 param4 = *((vartype4 *)[_args pointerAtIndex:4]); \
    vartype5 param5 = *((vartype5 *)[_args pointerAtIndex:5]); \
    vartype6 param6 = *((vartype6 *)[_args pointerAtIndex:6]); \
    vartype7 param7 = *((vartype7 *)[_args pointerAtIndex:7]); \
    vartype8 param8 = *((vartype8 *)[_args pointerAtIndex:8]); \
    vartype9 param9 = *((vartype9 *)[_args pointerAtIndex:9]); \
    NSColor * DW_UNUSED(_dw_fg_color) = (NSColor *)[_args pointerAtIndex:10]; \
    NSColor * DW_UNUSED(_dw_bg_color) = (NSColor *)[_args pointerAtIndex:11];
#define DW_FUNCTION_END }
#define DW_FUNCTION_NO_RETURN(func) [DWObj safeCall:@selector(callBack:) withObject:_args]; \
    [_args release]; \
    DW_LOCAL_POOL_OUT; } \
void _##func(NSPointerArray *_args) {
#define DW_FUNCTION_RETURN(func, rettype) [DWObj safeCall:@selector(callBack:) withObject:_args]; {\
        void *tmp = [_args pointerAtIndex:[_args count]-1]; \
        rettype myreturn = *((rettype *)tmp); \
        free(tmp); \
        [_args release]; \
        DW_LOCAL_POOL_OUT; \
        return myreturn; }} \
void _##func(NSPointerArray *_args) {
#define DW_FUNCTION_RETURN_THIS(_retvar) { void *_myreturn = malloc(sizeof(_retvar)); \
    memcpy(_myreturn, (void *)&_retvar, sizeof(_retvar)); \
    [_args addPointer:_myreturn]; }}
#define DW_FUNCTION_RETURN_NOTHING }
#else
/* Macros to protect access to thread unsafe classes */
#define DW_FUNCTION_INIT int _locked_by_me = FALSE; { \
    if(DWThread != (DWTID)-1 && pthread_self() != DWThread && pthread_self() != _dw_mutex_locked) { \
        dw_mutex_lock(DWThreadMutex); \
        _dw_mutex_locked = pthread_self(); \
        dw_mutex_lock(DWThreadMutex2); \
        [DWObj performSelectorOnMainThread:@selector(synchronizeThread:) withObject:nil waitUntilDone:NO]; \
        dw_mutex_lock(DWRunMutex); \
        _locked_by_me = TRUE; } }
#define DW_FUNCTION_DEFINITION(func, rettype, ...) rettype API func(__VA_ARGS__)
#define DW_FUNCTION_ADD_PARAM1(param1)
#define DW_FUNCTION_ADD_PARAM2(param1, param2)
#define DW_FUNCTION_ADD_PARAM3(param1, param2, param3)
#define DW_FUNCTION_ADD_PARAM4(param1, param2, param3, param4)
#define DW_FUNCTION_ADD_PARAM5(param1, param2, param3, param4, param5)
#define DW_FUNCTION_ADD_PARAM6(param1, param2, param3, param4, param5, param6)
#define DW_FUNCTION_ADD_PARAM7(param1, param2, param3, param4, param5, param6, param7)
#define DW_FUNCTION_ADD_PARAM8(param1, param2, param3, param4, param5, param6, param7, param8)
#define DW_FUNCTION_ADD_PARAM9(param1, param2, param3, param4, param5, param6, param7, param8, param9)
#define DW_FUNCTION_RESTORE_PARAM1(param1, vartype1)
#define DW_FUNCTION_RESTORE_PARAM2(param1, vartype1, param2, vartype2)
#define DW_FUNCTION_RESTORE_PARAM3(param1, vartype1, param2, vartype2, param3, vartype3)
#define DW_FUNCTION_RESTORE_PARAM4(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4)
#define DW_FUNCTION_RESTORE_PARAM5(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5)
#define DW_FUNCTION_RESTORE_PARAM6(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6)
#define DW_FUNCTION_RESTORE_PARAM7(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6, param7, vartype7)
#define DW_FUNCTION_RESTORE_PARAM8(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6, param7, vartype7, param8, vartype8)
#define DW_FUNCTION_RESTORE_PARAM9(param1, vartype1, param2, vartype2, param3, vartype3, param4, vartype4, param5, vartype5, param6, vartype6, param7, vartype7, param8, vartype8, param9, vartype9)
#define DW_FUNCTION_END
#define DW_FUNCTION_NO_RETURN(func)
#define DW_FUNCTION_RETURN(func, rettype)
#define DW_FUNCTION_RETURN_THIS(retvar) { \
    if(pthread_self() != DWThread && _locked_by_me == TRUE) { \
        dw_mutex_unlock(DWRunMutex); \
        dw_mutex_unlock(DWThreadMutex2); \
        _dw_mutex_locked = (pthread_t)-1; \
        dw_mutex_unlock(DWThreadMutex); \
        _locked_by_me = FALSE; } } \
    return retvar;
#define DW_FUNCTION_RETURN_NOTHING { \
    if(pthread_self() != DWThread && _locked_by_me == TRUE) { \
        dw_mutex_unlock(DWRunMutex); \
        dw_mutex_unlock(DWThreadMutex2); \
        _dw_mutex_locked = (pthread_t)-1; \
        dw_mutex_unlock(DWThreadMutex); \
        _locked_by_me = FALSE; } }
HMTX DWRunMutex;
HMTX DWThreadMutex;
HMTX DWThreadMutex2;
DWTID _dw_mutex_locked = (DWTID)-1;
#endif

unsigned long _dw_colors[] =
{
    0x00000000,   /* 0  black */
    0x000000bb,   /* 1  red */
    0x0000bb00,   /* 2  green */
    0x0000aaaa,   /* 3  yellow */
    0x00cc0000,   /* 4  blue */
    0x00bb00bb,   /* 5  magenta */
    0x00bbbb00,   /* 6  cyan */
    0x00bbbbbb,   /* 7  white */
    0x00777777,   /* 8  grey */
    0x000000ff,   /* 9  bright red */
    0x0000ff00,   /* 10 bright green */
    0x0000eeee,   /* 11 bright yellow */
    0x00ff0000,   /* 12 bright blue */
    0x00ff00ff,   /* 13 bright magenta */
    0x00eeee00,   /* 14 bright cyan */
    0x00ffffff,   /* 15 bright white */
    0xff000000    /* 16 default color */
};

/*
 * List those icons that have transparency first
 */
#define NUM_EXTS 8
char *_dw_image_exts[NUM_EXTS+1] =
{
   ".png",
   ".ico",
   ".icns",
   ".gif",
   ".jpg",
   ".jpeg",
   ".tiff",
   ".bmp",
    NULL
};

char *_dw_get_image_extension(const char *filename)
{
   char *file = alloca(strlen(filename) + 6);
   int found_ext = 0,i;

   /* Try various extentions */
   for ( i = 0; i < NUM_EXTS; i++ )
   {
      strcpy( file, filename );
      strcat( file, _dw_image_exts[i] );
      if ( access( file, R_OK ) == 0 )
      {
         found_ext = 1;
         break;
      }
   }
   if ( found_ext == 1 )
   {
      return _dw_image_exts[i];
   }
   return NULL;
}

/* Return the RGB color regardless if a predefined color was passed */
unsigned long _dw_get_color(unsigned long thiscolor)
{
    if(thiscolor & DW_RGB_COLOR)
    {
        return thiscolor & ~DW_RGB_COLOR;
    }
    else if(thiscolor < 17)
    {
        return _dw_colors[thiscolor];
    }
    return 0;
}

/* Returns TRUE if Mojave or later is in Dark Mode */
BOOL _dw_is_dark(id object)
{
#ifdef BUILDING_FOR_MOJAVE
    NSAppearance *appearance = [object effectiveAppearance];

    if(@available(macOS 10.14, *))
    {
        NSAppearanceName basicAppearance = [appearance bestMatchFromAppearancesWithNames:@[
                                                                                           NSAppearanceNameAqua,
                                                                                           NSAppearanceNameDarkAqua]];
        return [basicAppearance isEqualToString:NSAppearanceNameDarkAqua];
    }
#endif
    return NO;
}

/* Thread specific storage */
#if !defined(GARBAGE_COLLECT)
pthread_key_t _dw_pool_key;
#endif
pthread_key_t _dw_fg_color_key;
pthread_key_t _dw_bg_color_key;
static int DWOSMajor, DWOSMinor, DWOSBuild;
static char _dw_bundle_path[PATH_MAX+1] = { 0 };
static char _dw_app_id[_DW_APP_ID_SIZE+1]= {0};

/* Create a default colors for a thread */
void _dw_init_colors(void)
{
    NSColor *fgcolor = [[NSColor grayColor] retain];
    pthread_setspecific(_dw_fg_color_key, fgcolor);
    pthread_setspecific(_dw_bg_color_key, NULL);
}

typedef struct _dwsighandler
{
    struct _dwsighandler   *next;
    ULONG message;
    HWND window;
    int id;
    void *signalfunction;
    void *discfunction;
    void *data;

} DWSignalHandler;

static DWSignalHandler *DWRoot = NULL;

/* Some internal prototypes */
static void _dw_do_resize(Box *thisbox, int x, int y);
void _dw_handle_resize_events(Box *thisbox);
int _dw_remove_userdata(UserData **root, const char *varname, int all);
int _dw_main_iteration(NSDate *date);
NSGraphicsContext *_dw_draw_context(NSBitmapImageRep *image);
typedef id (*DWIMP)(id, SEL, ...);

/* Internal function to queue a window redraw */
void _dw_redraw(id window, int skip)
{
    static id lastwindow = nil;
    id redraw = lastwindow;

    if(skip && window == nil)
        return;

    lastwindow = window;
    if(redraw != lastwindow && redraw != nil)
    {
        dw_window_redraw(redraw);
    }
}

DWSignalHandler *_dw_get_handler(HWND window, int messageid)
{
    DWSignalHandler *tmp = DWRoot;

    /* Find any callbacks for this function */
    while(tmp)
    {
        if(tmp->message == messageid && window == tmp->window)
        {
            return tmp;
        }
        tmp = tmp->next;
    }
    return NULL;
}

typedef struct
{
    ULONG message;
    char name[30];

} DWSignalList;

/* List of signals */
#define SIGNALMAX 19

static DWSignalList DWSignalTranslate[SIGNALMAX] = {
    { _DW_EVENT_CONFIGURE,       DW_SIGNAL_CONFIGURE },
    { _DW_EVENT_KEY_PRESS,       DW_SIGNAL_KEY_PRESS },
    { _DW_EVENT_BUTTON_PRESS,    DW_SIGNAL_BUTTON_PRESS },
    { _DW_EVENT_BUTTON_RELEASE,  DW_SIGNAL_BUTTON_RELEASE },
    { _DW_EVENT_MOTION_NOTIFY,   DW_SIGNAL_MOTION_NOTIFY },
    { _DW_EVENT_DELETE,          DW_SIGNAL_DELETE },
    { _DW_EVENT_EXPOSE,          DW_SIGNAL_EXPOSE },
    { _DW_EVENT_CLICKED,         DW_SIGNAL_CLICKED },
    { _DW_EVENT_ITEM_ENTER,      DW_SIGNAL_ITEM_ENTER },
    { _DW_EVENT_ITEM_CONTEXT,    DW_SIGNAL_ITEM_CONTEXT },
    { _DW_EVENT_LIST_SELECT,     DW_SIGNAL_LIST_SELECT },
    { _DW_EVENT_ITEM_SELECT,     DW_SIGNAL_ITEM_SELECT },
    { _DW_EVENT_SET_FOCUS,       DW_SIGNAL_SET_FOCUS },
    { _DW_EVENT_VALUE_CHANGED,   DW_SIGNAL_VALUE_CHANGED },
    { _DW_EVENT_SWITCH_PAGE,     DW_SIGNAL_SWITCH_PAGE },
    { _DW_EVENT_TREE_EXPAND,     DW_SIGNAL_TREE_EXPAND },
    { _DW_EVENT_COLUMN_CLICK,    DW_SIGNAL_COLUMN_CLICK },
    { _DW_EVENT_HTML_RESULT,     DW_SIGNAL_HTML_RESULT },
    { _DW_EVENT_HTML_CHANGED,    DW_SIGNAL_HTML_CHANGED }
};

int _dw_event_handler1(id object, NSEvent *event, int message)
{
    DWSignalHandler *handler = _dw_get_handler(object, message);
    /* NSLog(@"Event handler - type %d\n", message); */

    if(handler)
    {
        switch(message)
        {
            /* Timer event */
            case _DW_EVENT_TIMER:
            {
                int (* API timerfunc)(void *) = (int (* API)(void *))handler->signalfunction;

                if(!timerfunc(handler->data))
                    dw_timer_disconnect(handler->id);
                return 0;
            }
            /* Configure/Resize event */
            case _DW_EVENT_CONFIGURE:
            {
                int (*sizefunc)(HWND, int, int, void *) = handler->signalfunction;
                NSSize size;

                if([object isKindOfClass:[NSWindow class]])
                {
                    NSWindow *window = object;
                    size = [[window contentView] frame].size;
                }
                else
                {
                    NSView *view = object;
                    size = [view frame].size;
                }

                if(size.width > 0 && size.height > 0)
                {
                    return sizefunc(object, size.width, size.height, handler->data);
                }
                return 0;
            }
            case _DW_EVENT_KEY_PRESS:
            {
                int (*keypressfunc)(HWND, char, int, int, void *, char *) = handler->signalfunction;
                NSString *nchar = [event charactersIgnoringModifiers];
                int special = (int)[event modifierFlags];
                unichar vk = [nchar characterAtIndex:0];
                char *utf8 = NULL, ch = '\0';

                /* Handle a valid key */
                if([nchar length] == 1)
                {
                    char *tmp = (char *)[nchar UTF8String];
                    if(tmp && strlen(tmp) == 1)
                    {
                        ch = tmp[0];
                    }
                    utf8 = tmp;
                }

                return keypressfunc(handler->window, ch, (int)vk, special, handler->data, utf8);
            }
            /* Button press and release event */
            case _DW_EVENT_BUTTON_PRESS:
            case _DW_EVENT_BUTTON_RELEASE:
            {
                int (* API buttonfunc)(HWND, int, int, int, void *) = (int (* API)(HWND, int, int, int, void *))handler->signalfunction;
                NSPoint p = [NSEvent mouseLocation];
                int button = DW_BUTTON1_MASK;

                if([event isMemberOfClass:[NSEvent class]])
                {
                    id view = [[[event window] contentView] superview];
                    NSEventType type = [event type];

                    p = [view convertPoint:[event locationInWindow] toView:object];

                    if(type == DWEventTypeRightMouseDown || type == DWEventTypeRightMouseUp)
                    {
                        button = DW_BUTTON2_MASK;
                    }
                    else if(type == DWEventTypeOtherMouseDown || type == DWEventTypeOtherMouseUp)
                    {
                        button = DW_BUTTON3_MASK;
                    }
                    else if([event modifierFlags] & DWEventModifierFlagControl)
                    {
                        button = DW_BUTTON2_MASK;
                    }
                }

                return buttonfunc(object, (int)p.x, (int)p.y, button, handler->data);
            }
            /* Motion notify event */
            case _DW_EVENT_MOTION_NOTIFY:
            {
                int (* API motionfunc)(HWND, int, int, int, void *) = (int (* API)(HWND, int, int, int, void *))handler->signalfunction;
                id view = [[[event window] contentView] superview];
                NSPoint p = [view convertPoint:[event locationInWindow] toView:object];
                SEL spmb = NSSelectorFromString(@"pressedMouseButtons");
                DWIMP ipmb = [[NSEvent class] respondsToSelector:spmb] ? (DWIMP)[[NSEvent class] methodForSelector:spmb] : 0;
                NSUInteger buttonmask = ipmb ? (NSUInteger)ipmb([NSEvent class], spmb) : (1 << [event buttonNumber]);

                return motionfunc(object, (int)p.x, (int)p.y, (int)buttonmask, handler->data);
            }
            /* Window close event */
            case _DW_EVENT_DELETE:
            {
                int (* API closefunc)(HWND, void *) = (int (* API)(HWND, void *))handler->signalfunction;
                return closefunc(object, handler->data);
            }
            /* Window expose/draw event */
            case _DW_EVENT_EXPOSE:
            {
                DWExpose exp;
                int (* API exposefunc)(HWND, DWExpose *, void *) = (int (* API)(HWND, DWExpose *, void *))handler->signalfunction;
                NSRect rect = [object frame];

                exp.x = rect.origin.x;
                exp.y = rect.origin.y;
                exp.width = rect.size.width;
                exp.height = rect.size.height;
                int result = exposefunc(object, &exp, handler->data);
#ifndef BUILDING_FOR_MOJAVE
                [[object window] flushWindow];
#endif
                return result;
            }
            /* Clicked event for buttons and menu items */
            case _DW_EVENT_CLICKED:
            {
                int (* API clickfunc)(HWND, void *) = (int (* API)(HWND, void *))handler->signalfunction;

                return clickfunc(object, handler->data);
            }
            /* Container class selection event */
            case _DW_EVENT_ITEM_ENTER:
            {
                int (*containerselectfunc)(HWND, char *, void *, void *) = handler->signalfunction;
                void **params = (void **)event;

                return containerselectfunc(handler->window, params[0], handler->data, params[1]);
            }
            /* Container context menu event */
            case _DW_EVENT_ITEM_CONTEXT:
            {
                int (* API containercontextfunc)(HWND, char *, int, int, void *, void *) = (int (* API)(HWND, char *, int, int, void *, void *))handler->signalfunction;
                char *text = NULL;
                void *user = NULL;
                LONG x,y;

                /* Fill in both items for the tree */
                if([object isKindOfClass:[NSOutlineView class]])
                {
                    id item = event;
                    NSString *nstr = [item objectAtIndex:1];
                    text = (char *)[nstr UTF8String];
                    NSValue *value = [item objectAtIndex:2];
                    if(value && [value isKindOfClass:[NSValue class]])
                    {
                        user = [value pointerValue];
                    }
                }
                else
                {
                    void **params = (void **)event;

                    text = params[0];
                    user = params[1];
                }

                dw_pointer_query_pos(&x, &y);

                return containercontextfunc(handler->window, text, (int)x, (int)y, handler->data, user);
            }
            /* Generic selection changed event for several classes */
            case _DW_EVENT_LIST_SELECT:
            case _DW_EVENT_VALUE_CHANGED:
            {
                int (* API valuechangedfunc)(HWND, int, void *) = (int (* API)(HWND, int, void *))handler->signalfunction;
                int selected = DW_POINTER_TO_INT(event);

                return valuechangedfunc(handler->window, selected, handler->data);;
            }
            /* Tree class selection event */
            case _DW_EVENT_ITEM_SELECT:
            {
                int (* API treeselectfunc)(HWND, HTREEITEM, char *, void *, void *) = (int (* API)(HWND, HTREEITEM, char *, void *, void *))handler->signalfunction;
                char *text = NULL;
                void *user = NULL;
                id item = nil;

                if([object isKindOfClass:[NSOutlineView class]])
                {
                    item = (id)event;
                    NSString *nstr = [item objectAtIndex:1];

                    if(nstr)
                    {
                        text = strdup([nstr UTF8String]);
                    }

                    NSValue *value = [item objectAtIndex:2];
                    if(value && [value isKindOfClass:[NSValue class]])
                    {
                        user = [value pointerValue];
                    }
                    int result = treeselectfunc(handler->window, item, text, handler->data, user);
                    if(text)
                    {
                        free(text);
                    }
                    return result;
                }
                else if(event)
                {
                    void **params = (void **)event;

                    text = params[0];
                    user = params[1];
                }

                return treeselectfunc(handler->window, item, text, handler->data, user);
            }
            /* Set Focus event */
            case _DW_EVENT_SET_FOCUS:
            {
                int (* API setfocusfunc)(HWND, void *) = (int (* API)(HWND, void *))handler->signalfunction;

                return setfocusfunc(handler->window, handler->data);
            }
            /* Notebook page change event */
            case _DW_EVENT_SWITCH_PAGE:
            {
                int (* API switchpagefunc)(HWND, unsigned long, void *) = (int (* API)(HWND, unsigned long, void *))handler->signalfunction;
                int pageid = DW_POINTER_TO_INT(event);

                return switchpagefunc(handler->window, pageid, handler->data);
            }
            /* Tree expand event */
            case _DW_EVENT_TREE_EXPAND:
            {
                int (* API treeexpandfunc)(HWND, HTREEITEM, void *) = (int (* API)(HWND, HTREEITEM, void *))handler->signalfunction;

                return treeexpandfunc(handler->window, (HTREEITEM)event, handler->data);
            }
            /* Column click event */
            case _DW_EVENT_COLUMN_CLICK:
            {
                int (* API clickcolumnfunc)(HWND, int, void *) = handler->signalfunction;
                int column_num = DW_POINTER_TO_INT(event);

                return clickcolumnfunc(handler->window, column_num, handler->data);
            }
            /* HTML result event */
            case _DW_EVENT_HTML_RESULT:
            {
                int (* API htmlresultfunc)(HWND, int, char *, void *, void *) = handler->signalfunction;
                void **params = (void **)event;
                NSString *result = params[0];

                return htmlresultfunc(handler->window, [result length] ? DW_ERROR_NONE : DW_ERROR_UNKNOWN, [result length] ? (char *)[result UTF8String] : NULL, params[1], handler->data);
            }
            /* HTML changed event */
            case _DW_EVENT_HTML_CHANGED:
            {
                int (* API htmlchangedfunc)(HWND, int, char *, void *) = handler->signalfunction;
                void **params = (void **)event;
                NSString *uri = params[1];

                return htmlchangedfunc(handler->window, DW_POINTER_TO_INT(params[0]), (char *)[uri UTF8String], handler->data);
            }
        }
    }
    return -1;
}

/* Sub function to handle redraws */
int _dw_event_handler(id object, NSEvent *event, int message)
{
    int ret = _dw_event_handler1(object, event, message);
    if(ret != -1)
        _dw_redraw(nil, FALSE);
    return ret;
}

/* Subclass for the Timer type */
@interface DWTimerHandler : NSObject { }
-(void)runTimer:(id)sender;
@end

@implementation DWTimerHandler
-(void)runTimer:(id)sender { _dw_event_handler(sender, nil, _DW_EVENT_TIMER); }
@end

static NSApplication *DWApp = nil;
static NSFontManager *DWFontManager = nil;
static NSMenu *DWMainMenu;
static NSFont *DWDefaultFont;
static DWTimerHandler *DWHandler;
#ifdef BUILDING_FOR_MOJAVE
static NSMutableArray *_DWDirtyDrawables;
#else
static HWND _DWLastDrawable;
#endif
static DWTID DWThread = (DWTID)-1;

/* Send fake event to make sure the loop isn't stuck */
void _dw_wakeup_app()
{
    [DWApp postEvent:[NSEvent otherEventWithType:DWEventTypeApplicationDefined
                                        location:NSMakePoint(0, 0)
                                   modifierFlags:0
                                       timestamp:0
                                    windowNumber:0
                                         context:NULL
                                         subtype:0
                                           data1:0
                                           data2:0]
             atStart:NO];
}

/* Used for doing bitblts from the main thread */
typedef struct _bitbltinfo
{
    id src;
    id dest;
    int xdest;
    int ydest;
    int width;
    int height;
    int xsrc;
    int ysrc;
    int srcwidth;
    int srcheight;
} DWBitBlt;

/* Subclass for a test object type */
@interface DWObject : NSObject {}
-(void)uselessThread:(id)sender;
#ifndef BUILDING_FOR_MOJAVE
-(void)synchronizeThread:(id)param;
#endif
-(void)menuHandler:(id)param;
-(void)doBitBlt:(id)param;
-(void)doFlush:(id)param;
@end

@interface DWMenuItem : NSMenuItem
{
    int check;
    void *userdata;
}
-(void)setCheck:(int)input;
-(int)check;
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)dealloc;
@end

/* So basically to implement our event handlers...
 * it looks like we are going to have to subclass
 * basically everything.  Was hoping to add methods
 * to the superclasses but it looks like you can
 * only add methods and no variables, which isn't
 * going to work. -Brian
 */

/* Subclass for a box type */
@interface DWBox : NSView
{
    Box *box;
    void *userdata;
    NSColor *bgcolor;
}
-(id)init;
-(void)dealloc;
-(Box *)box;
-(id)contentView;
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)drawRect:(NSRect)rect;
-(BOOL)isFlipped;
-(void)mouseDown:(NSEvent *)theEvent;
-(void)mouseUp:(NSEvent *)theEvent;
-(NSMenu *)menuForEvent:(NSEvent *)theEvent;
-(void)rightMouseUp:(NSEvent *)theEvent;
-(void)otherMouseDown:(NSEvent *)theEvent;
-(void)otherMouseUp:(NSEvent *)theEvent;
-(void)keyDown:(NSEvent *)theEvent;
-(void)setColor:(unsigned long)input;
@end

@implementation DWBox
-(id)init
{
    self = [super init];

    if (self)
    {
        box = calloc(1, sizeof(Box));
        box->type = DW_VERT;
        box->vsize = box->hsize = _DW_SIZE_EXPAND;
        box->width = box->height = 1;
    }
    return self;
}
-(void)dealloc
{
    UserData *root = userdata;
    if(box->items)
        free(box->items);
    free(box);
    _dw_remove_userdata(&root, NULL, TRUE);
    dw_signal_disconnect_by_window(self);
    [super dealloc];
}
-(Box *)box { return box; }
-(id)contentView { return self; }
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)drawRect:(NSRect)rect
{
    if(bgcolor)
    {
        [bgcolor set];
        NSRectFill([self bounds]);
    }
}
-(BOOL)isFlipped { return YES; }
-(void)mouseDown:(NSEvent *)theEvent { _dw_event_handler(self, DW_INT_TO_POINTER(DW_BUTTON1_MASK), _DW_EVENT_BUTTON_PRESS); }
-(void)mouseUp:(NSEvent *)theEvent { _dw_event_handler(self, DW_INT_TO_POINTER(DW_BUTTON1_MASK), _DW_EVENT_BUTTON_RELEASE); }
-(NSMenu *)menuForEvent:(NSEvent *)theEvent { _dw_event_handler(self, DW_INT_TO_POINTER(DW_BUTTON2_MASK), _DW_EVENT_BUTTON_PRESS); return nil; }
-(void)rightMouseUp:(NSEvent *)theEvent { _dw_event_handler(self, DW_INT_TO_POINTER(DW_BUTTON2_MASK), _DW_EVENT_BUTTON_RELEASE); }
-(void)otherMouseDown:(NSEvent *)theEvent { _dw_event_handler(self, DW_INT_TO_POINTER(DW_BUTTON3_MASK), _DW_EVENT_BUTTON_PRESS); }
-(void)otherMouseUp:(NSEvent *)theEvent { _dw_event_handler(self, DW_INT_TO_POINTER(DW_BUTTON3_MASK), _DW_EVENT_BUTTON_RELEASE); }
-(void)keyDown:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_KEY_PRESS); }
-(void)setColor:(unsigned long)input
{
    id orig = bgcolor;

    if(input == _dw_colors[DW_CLR_DEFAULT])
    {
        bgcolor = nil;
    }
    else
    {
        bgcolor = [[NSColor colorWithDeviceRed: DW_RED_VALUE(input)/255.0 green: DW_GREEN_VALUE(input)/255.0 blue: DW_BLUE_VALUE(input)/255.0 alpha: 1] retain];
#ifdef BUILDING_FOR_YOSEMITE
        if([[NSGraphicsContext currentContext] CGContext])
#else
        if([[NSGraphicsContext currentContext] graphicsPort])
#endif
        {
            [bgcolor set];
            NSRectFill([self bounds]);
        }
    }
    [self setNeedsDisplay:YES];
    [orig release];
}
@end

/* Subclass for a group box type */
@interface DWGroupBox : NSBox
{
    void *userdata;
    NSColor *bgcolor;
    NSSize borderSize;
}
-(Box *)box;
-(void *)userdata;
-(void)setUserdata:(void *)input;
@end

@implementation DWGroupBox
-(Box *)box { return [[self contentView] box]; }
-(void *)userdata { return userdata; }
-(NSSize)borderSize { return borderSize; }
-(NSSize)initBorder
{
    NSSize frameSize = [self frame].size;

    if(frameSize.height < 20 || frameSize.width < 20)
    {
        frameSize.width = frameSize.height = 100;
        [self setFrameSize:frameSize];
    }
    NSSize contentSize = [[self contentView] frame].size;
    NSSize titleSize = [self titleRect].size;

    borderSize.width = 100-contentSize.width;
    borderSize.height = (100-contentSize.height)-titleSize.height;
    return borderSize;
}
-(void)setUserdata:(void *)input { userdata = input; }
@end

@interface DWWindow : NSWindow
{
    int redraw;
    int shown;
}
-(void)sendEvent:(NSEvent *)theEvent;
-(void)keyDown:(NSEvent *)theEvent;
-(void)mouseDragged:(NSEvent *)theEvent;
-(int)redraw;
-(void)setRedraw:(int)val;
-(int)shown;
-(void)setShown:(int)val;
@end

@implementation DWWindow
-(void)sendEvent:(NSEvent *)theEvent
{
   int rcode = -1;
   if([theEvent type] == DWEventTypeKeyDown)
   {
      rcode = _dw_event_handler(self, theEvent, _DW_EVENT_KEY_PRESS);
   }
   if ( rcode != TRUE )
      [super sendEvent:theEvent];
}
-(void)keyDown:(NSEvent *)theEvent { }
-(void)mouseDragged:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_MOTION_NOTIFY); }
-(int)redraw { return redraw; }
-(void)setRedraw:(int)val { redraw = val; }
-(int)shown { return shown; }
-(void)setShown:(int)val { shown = val; }
-(void)dealloc { dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a render area type */
@interface DWRender : NSControl
{
    void *userdata;
    NSFont *font;
    NSSize size;
#ifdef BUILDING_FOR_MOJAVE
    NSBitmapImageRep *cachedDrawingRep;
#endif
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)setFont:(NSFont *)input;
-(NSFont *)font;
-(void)setSize:(NSSize)input;
-(NSSize)size;
#ifdef BUILDING_FOR_MOJAVE
-(NSBitmapImageRep *)cachedDrawingRep;
#endif
-(void)mouseDown:(NSEvent *)theEvent;
-(void)mouseUp:(NSEvent *)theEvent;
-(NSMenu *)menuForEvent:(NSEvent *)theEvent;
-(void)rightMouseUp:(NSEvent *)theEvent;
-(void)otherMouseDown:(NSEvent *)theEvent;
-(void)otherMouseUp:(NSEvent *)theEvent;
-(void)drawRect:(NSRect)rect;
-(void)keyDown:(NSEvent *)theEvent;
-(BOOL)isFlipped;
@end

@implementation DWRender
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)setFont:(NSFont *)input { [font release]; font = input; [font retain]; }
-(NSFont *)font { return font; }
-(void)setSize:(NSSize)input {
    size = input;
#ifdef BUILDING_FOR_MOJAVE
    if(cachedDrawingRep)
    {
        NSBitmapImageRep *oldrep = cachedDrawingRep;
        cachedDrawingRep = [self bitmapImageRepForCachingDisplayInRect:self.bounds];
        [cachedDrawingRep retain];
        [oldrep release];
    }
#endif
}
-(NSSize)size { return size; }
#ifdef BUILDING_FOR_MOJAVE
-(NSBitmapImageRep *)cachedDrawingRep {
    if(!cachedDrawingRep)
    {
        cachedDrawingRep = [self bitmapImageRepForCachingDisplayInRect:self.bounds];
        [cachedDrawingRep retain];
    }
    /* Mark this render dirty if something is requesting it to draw */
    if(![_DWDirtyDrawables containsObject:self])
        [_DWDirtyDrawables addObject:self];
    return cachedDrawingRep;
}
#endif
-(void)mouseDown:(NSEvent *)theEvent
{
    if(![theEvent isMemberOfClass:[NSEvent class]] || !([theEvent modifierFlags] & DWEventModifierFlagControl))
        _dw_event_handler(self, theEvent, _DW_EVENT_BUTTON_PRESS);
}
-(void)mouseUp:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_BUTTON_RELEASE); }
-(NSMenu *)menuForEvent:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_BUTTON_PRESS); return nil; }
-(void)rightMouseUp:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_BUTTON_RELEASE); }
-(void)otherMouseDown:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_BUTTON_PRESS); }
-(void)otherMouseUp:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_BUTTON_RELEASE); }
-(void)mouseDragged:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_MOTION_NOTIFY); }
-(void)delayedNeedsDisplay { [self setNeedsDisplay:YES]; }
-(void)drawRect:(NSRect)rect {
    _dw_event_handler(self, nil, _DW_EVENT_EXPOSE);
#ifdef BUILDING_FOR_MOJAVE
    if (cachedDrawingRep)
    {
        [cachedDrawingRep drawInRect:self.bounds];
        [_DWDirtyDrawables removeObject:self];
        /* Work around a bug in Mojave 10.14 by delaying the setNeedsDisplay */
        if(DWOSMinor != 14)
            [self setNeedsDisplay:YES];
        else
            [self performSelector:@selector(delayedNeedsDisplay) withObject:nil afterDelay:0];
    }
#endif
}
-(void)keyDown:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_KEY_PRESS); }
-(BOOL)isFlipped { return YES; }
-(void)dealloc {
    UserData *root = userdata;
    _dw_remove_userdata(&root, NULL, TRUE);
    [font release];
    dw_signal_disconnect_by_window(self);
#ifdef BUILDING_FOR_MOJAVE
    [cachedDrawingRep release];
    [_DWDirtyDrawables removeObject:self];
#endif
    [super dealloc];
}
-(BOOL)acceptsFirstResponder { return YES; }
@end

@implementation DWObject
-(void)uselessThread:(id)sender { /* Thread only to initialize threading */ }
#ifndef BUILDING_FOR_MOJAVE
-(void)synchronizeThread:(id)param
{
    pthread_mutex_unlock(DWRunMutex);
    pthread_mutex_lock(DWThreadMutex2);
    pthread_mutex_unlock(DWThreadMutex2);
    pthread_mutex_lock(DWRunMutex);
}
#endif
-(void)menuHandler:(id)param
{
    DWMenuItem *item = param;
    if([item check])
    {
        if([item state] == DWControlStateValueOn)
            [item setState:DWControlStateValueOff];
        else
            [item setState:DWControlStateValueOn];
    }
    _dw_event_handler(param, nil, _DW_EVENT_CLICKED);
}
#ifdef BUILDING_FOR_MOJAVE
-(void)callBack:(NSPointerArray *)params
{
    void (*mycallback)(NSPointerArray *) = [params pointerAtIndex:0];
    if(mycallback)
        mycallback(params);
}
#endif
-(void)messageBox:(NSMutableArray *)params
{
    NSInteger iResponse;
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:[params objectAtIndex:0]];
    [alert setInformativeText:[params objectAtIndex:1]];
    [alert addButtonWithTitle:[params objectAtIndex:3]];
    if([params count] > 4)
        [alert addButtonWithTitle:[params objectAtIndex:4]];
    if([params count] > 5)
        [alert addButtonWithTitle:[params objectAtIndex:5]];
    [alert setAlertStyle:[[params objectAtIndex:2] integerValue]];
    iResponse = [alert runModal];
    [alert release];
    [params addObject:[NSNumber numberWithInteger:iResponse]];
}
-(void)safeCall:(SEL)sel withObject:(id)param
{
    if([self respondsToSelector:sel])
    {
        DWTID curr = pthread_self();

        if(DWThread == (DWTID)-1 || DWThread == curr)
            [self performSelector:sel withObject:param];
        else
            [self performSelectorOnMainThread:sel withObject:param waitUntilDone:YES];
    }
}
-(void)doBitBlt:(id)param
{
    NSValue *bi = (NSValue *)param;
    DWBitBlt *bltinfo = (DWBitBlt *)[bi pointerValue];
    id bltdest = bltinfo->dest;
    id bltsrc = bltinfo->src;

#ifdef BUILDING_FOR_MOJAVE
    if([bltdest isMemberOfClass:[DWRender class]])
    {
        DWRender *render = bltdest;

        bltdest = [render cachedDrawingRep];
    }
#endif
    if([bltdest isMemberOfClass:[NSBitmapImageRep class]])
    {
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:_dw_draw_context(bltdest)];
        [[[NSDictionary alloc] initWithObjectsAndKeys:bltdest, NSGraphicsContextDestinationAttributeName, nil] autorelease];
    }
#ifndef BUILDING_FOR_MOJAVE
    else
    {
        if([bltdest lockFocusIfCanDraw] == NO)
        {
            free(bltinfo);
            return;
        }
        _DWLastDrawable = bltinfo->dest;
    }
#endif
    if(bltdest && [bltsrc isMemberOfClass:[NSBitmapImageRep class]])
    {
        NSBitmapImageRep *rep = bltsrc;
        NSImage *image = [NSImage alloc];
        SEL siwc = NSSelectorFromString(@"initWithCGImage");
        NSCompositingOperation op = DWCompositingOperationSourceOver;

        if([image respondsToSelector:siwc])
        {
            DWIMP iiwc = (DWIMP)[image methodForSelector:siwc];
            image = iiwc(image, siwc, [rep CGImage], NSZeroSize);
        }
        else
        {
            image = [image initWithSize:[rep size]];
            [image addRepresentation:rep];
        }
        if(bltinfo->srcwidth != -1)
        {
            [image drawInRect:NSMakeRect(bltinfo->xdest, bltinfo->ydest, bltinfo->width, bltinfo->height)
                     fromRect:NSMakeRect(bltinfo->xsrc, bltinfo->ysrc, bltinfo->srcwidth, bltinfo->srcheight)
                    operation:op fraction:1.0];
        }
        else
        {
            [image drawAtPoint:NSMakePoint(bltinfo->xdest, bltinfo->ydest)
                      fromRect:NSMakeRect(bltinfo->xsrc, bltinfo->ysrc, bltinfo->width, bltinfo->height)
                     operation:op fraction:1.0];
        }
        [bltsrc release];
        [image release];
    }
    if([bltdest isMemberOfClass:[NSBitmapImageRep class]])
    {
        [NSGraphicsContext restoreGraphicsState];
    }
#ifndef BUILDING_FOR_MOJAVE
    else
    {
        [bltdest unlockFocus];
    }
#endif
    free(bltinfo);
}
-(void)doFlush:(id)param
{
#ifdef BUILDING_FOR_MOJAVE
    NSEnumerator *enumerator = [_DWDirtyDrawables objectEnumerator];
    DWRender *rend;

    while (rend = [enumerator nextObject])
        [rend setNeedsDisplay:YES];
    [_DWDirtyDrawables removeAllObjects];
#else
    if(_DWLastDrawable)
    {
        id object = _DWLastDrawable;
        NSWindow *window = [object window];
        [window flushWindow];
        _DWLastDrawable = nil;
    }
#endif
}
-(void)doWindowFunc:(id)param
{
    NSValue *v = (NSValue *)param;
    void **params = (void **)[v pointerValue];
    void (* windowfunc)(void *);

    if(params)
    {
        windowfunc = params[0];
        if(windowfunc)
        {
            windowfunc(params[1]);
        }
    }
}
@end

DWObject *DWObj;

/* Subclass for the application class */
@interface DWAppDel : NSObject
#ifdef BUILDING_FOR_MOUNTAIN_LION
<NSApplicationDelegate, NSUserNotificationCenterDelegate>
#elif defined(BUILDING_FOR_SNOW_LEOPARD)
<NSApplicationDelegate>
#endif
{
}
-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
#if defined(BUILDING_FOR_MOUNTAIN_LION) && !defined(BUILDING_FOR_BIG_SUR)
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification;
-(BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification;
-(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification;
#endif
@end

/* Apparently the WKWebKit API is only enabled on intel 64bit...
 * Causing build failures on 32bit builds, so this should allow
 * WKWebKit on intel 64 and the old WebKit on intel 32 bit and earlier.
 */
#if WK_API_ENABLED
@interface DWWebView : WKWebView <WKNavigationDelegate>
{
    void *userdata;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
-(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
@end

@implementation DWWebView : WKWebView
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
{
    void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_STARTED), [[self URL] absoluteString] };
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
}
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_COMPLETE), [[self URL] absoluteString] };
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
}
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
    void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_LOADING), [[self URL] absoluteString] };
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
}
-(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
{
    void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_REDIRECT), [[self URL] absoluteString] };
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
}
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end
#else
@interface DWWebView : WebView
{
    void *userdata;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame;
-(void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame;
-(void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame;
@end

@implementation DWWebView : WebView
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
    void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_COMPLETE), [self mainFrameURL] };
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
}
-(void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame
{
    void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_STARTED), [self mainFrameURL] };
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
}
-(void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
    void *params[2] = { DW_INT_TO_POINTER(DW_HTML_CHANGE_LOADING), [self mainFrameURL] };
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_HTML_CHANGED);
}
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end
#endif


@implementation DWAppDel
-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
    if(_dw_event_handler(sender, nil, _DW_EVENT_DELETE) > 0)
        return NSTerminateCancel;
    return NSTerminateNow;
}
#if defined(BUILDING_FOR_MOUNTAIN_LION) && !defined(BUILDING_FOR_BIG_SUR)
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
#ifdef BUILDING_FOR_MOJAVE
    if (@available(macOS 10.14, *)) {} else
#endif
    {
        NSUserNotificationCenter* unc = [NSUserNotificationCenter defaultUserNotificationCenter];
        unc.delegate = self;
    }
    return;
}
-(BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification
{
    return YES;
}
-(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)usernotification
{
    NSScanner *objScanner = [NSScanner scannerWithString:usernotification.identifier];
    unsigned long long handle;
    HWND notification;
    
    /* Skip the dw-notification- prefix */
    [objScanner scanString:@"dw-notification-" intoString:nil];
    [objScanner scanUnsignedLongLong:&handle];
    notification = DW_UINT_TO_POINTER(handle);
    
    switch(usernotification.activationType)
    {
        case NSUserNotificationActivationTypeContentsClicked:
            _dw_event_handler(notification, nil, _DW_EVENT_CLICKED);
            break;
        default:
            break;
    }
    dw_signal_disconnect_by_window(notification);
}
#endif
@end

/* Subclass for a top-level window */
@interface DWView : DWBox
#ifdef BUILDING_FOR_SNOW_LEOPARD
<NSWindowDelegate>
#endif
{
    NSMenu *windowmenu;
    NSSize oldsize;
}
-(BOOL)windowShouldClose:(id)sender;
-(void)setMenu:(NSMenu *)input;
-(void)windowDidBecomeMain:(id)sender;
-(void)menuHandler:(id)sender;
-(void)mouseDragged:(NSEvent *)theEvent;
@end

@implementation DWView
-(BOOL)windowShouldClose:(id)sender
{
    if(_dw_event_handler(sender, nil, _DW_EVENT_DELETE) > 0)
        return NO;
    return YES;
}
-(void)viewDidMoveToWindow
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowResized:) name:NSWindowDidResizeNotification object:[self window]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeMain:) name:NSWindowDidBecomeMainNotification object:[self window]];
}
-(void)dealloc
{
    if(windowmenu)
    {
        [windowmenu release];
    }
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    dw_signal_disconnect_by_window(self);
    [super dealloc];
}
-(void)windowResized:(NSNotification *)notification;
{
    NSSize size = [self frame].size;

    if(oldsize.width != size.width || oldsize.height != size.height)
    {
        _dw_do_resize(box, size.width, size.height);
        _dw_event_handler([self window], nil, _DW_EVENT_CONFIGURE);
        oldsize.width = size.width;
        oldsize.height = size.height;
        _dw_handle_resize_events(box);
    }
}
-(void)showWindow
{
    NSSize size = [self frame].size;

    if(oldsize.width == size.width && oldsize.height == size.height)
    {
        _dw_do_resize(box, size.width, size.height);
        _dw_handle_resize_events(box);
    }

}
-(void)windowDidBecomeMain:(id)sender
{
    if(windowmenu)
    {
        [DWApp setMainMenu:windowmenu];
    }
    else
    {
        [DWApp setMainMenu:DWMainMenu];
    }
    _dw_event_handler([self window], nil, _DW_EVENT_SET_FOCUS);
}
-(void)setMenu:(NSMenu *)input { windowmenu = input; [windowmenu retain]; }
-(void)menuHandler:(id)sender
{
    id menu = [sender menu];

    /* Find the highest menu for this item */
    while([menu supermenu])
    {
        menu = [menu supermenu];
    }

    /* Only perform the delay if this item is a child of the main menu */
    if([DWApp mainMenu] == menu)
        [DWObj performSelector:@selector(menuHandler:) withObject:sender afterDelay:0];
    else
        [DWObj menuHandler:sender];
    _dw_wakeup_app();
}
-(void)mouseDragged:(NSEvent *)theEvent { _dw_event_handler(self, theEvent, _DW_EVENT_MOTION_NOTIFY); }
-(void)mouseMoved:(NSEvent *)theEvent
{
    id hit = [self hitTest:[theEvent locationInWindow]];

    if([hit isMemberOfClass:[DWRender class]])
    {
        _dw_event_handler(hit, theEvent, _DW_EVENT_MOTION_NOTIFY);
    }
}
@end

/* Subclass for a button type */
@interface DWButton : NSButton
{
    void *userdata;
    NSButtonType buttonType;
    DWBox *parent;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)buttonClicked:(id)sender;
-(void)setButtonType:(NSButtonType)input;
-(NSButtonType)buttonType;
-(void)setParent:(DWBox *)input;
-(DWBox *)parent;
-(NSColor *)textColor;
-(void)setTextColor:(NSColor *)textColor;
@end

@implementation DWButton
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)buttonClicked:(id)sender
{
    _dw_event_handler(self, nil, _DW_EVENT_CLICKED);
    if([self buttonType] == DWButtonTypeRadio)
    {
        DWBox *viewbox = [self parent];
        Box *thisbox = [viewbox box];
        int z;

        for(z=0;z<thisbox->count;z++)
        {
            if(thisbox->items[z].type != _DW_TYPE_BOX)
            {
                id object = thisbox->items[z].hwnd;

                if([object isMemberOfClass:[DWButton class]])
                {
                    DWButton *button = object;

                    if(button != self && [button buttonType] == DWButtonTypeRadio)
                    {
                        [button setState:DWControlStateValueOff];
                    }
                }
            }
        }
    }
}
-(void)setButtonType:(NSButtonType)input { buttonType = input; [super setButtonType:input]; }
-(NSButtonType)buttonType { return buttonType; }
-(void)setParent:(DWBox *)input { parent = input; }
-(DWBox *)parent { return parent; }
-(NSColor *)textColor
{
    NSAttributedString *attrTitle = [self attributedTitle];
    NSUInteger len = [attrTitle length];
    NSRange range = NSMakeRange(0, MIN(len, 1));
    NSDictionary *attrs = [attrTitle fontAttributesInRange:range];
    NSColor *textColor = [NSColor controlTextColor];
    if (attrs) {
        textColor = [attrs objectForKey:NSForegroundColorAttributeName];
    }
    return textColor;
}
-(void)setTextColor:(NSColor *)textColor
{
    NSMutableAttributedString *attrTitle = [[NSMutableAttributedString alloc]
                                            initWithAttributedString:[self attributedTitle]];
    NSUInteger len = [attrTitle length];
    NSRange range = NSMakeRange(0, len);
    [attrTitle addAttribute:NSForegroundColorAttributeName
                      value:textColor
                      range:range];
    [attrTitle fixAttributesInRange:range];
    [self setAttributedTitle:attrTitle];
    [attrTitle release];
}
-(void)keyDown:(NSEvent *)theEvent
{
    unichar vk = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
    if(vk == VK_RETURN || vk == VK_SPACE)
    {
        if(buttonType == DWButtonTypeSwitch)
            [self setState:([self state] ? DWControlStateValueOff : DWControlStateValueOn)];
        else if(buttonType == DWButtonTypeRadio)
            [self setState:DWControlStateValueOn];
        [self buttonClicked:self];
    }
    else
    {
        [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
        [super keyDown:theEvent];
    }
}
-(void)insertTab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectNextKeyView:self]; }
-(void)insertBacktab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectPreviousKeyView:self]; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a progress type */
@interface DWPercent : NSProgressIndicator
{
    void *userdata;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
@end

@implementation DWPercent
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a menu item type */
@implementation DWMenuItem
-(void)setCheck:(int)input { check = input; }
-(int)check { return check; }
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a scrollbox type */
@interface DWScrollBox : NSScrollView
{
    void *userdata;
    id box;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)setBox:(void *)input;
-(id)box;
@end

@implementation DWScrollBox
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)setBox:(void *)input { box = input; }
-(id)box { return box; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a textfield that supports vertical centering */
@interface DWTextFieldCell : NSTextFieldCell
{
    BOOL vcenter;
}
-(NSRect)drawingRectForBounds:(NSRect)theRect;
-(void)setVCenter:(BOOL)input;
@end

@implementation DWTextFieldCell
-(NSRect)drawingRectForBounds:(NSRect)theRect
{
    /* Get the parent's idea of where we should draw */
    NSRect newRect = [super drawingRectForBounds:theRect];

    /* If we are being vertically centered */
    if(vcenter)
    {
        /* Get our ideal size for current text */
        NSSize textSize = [self cellSizeForBounds:theRect];

        /* Center that in the proposed rect */
        float heightDelta = newRect.size.height - textSize.height;	
        if (heightDelta > 0)
        {
            newRect.size.height -= heightDelta;
            newRect.origin.y += (heightDelta / 2);
        }
    }
	
    return newRect;
}
-(void)setVCenter:(BOOL)input { vcenter = input; }
@end

@interface DWEntryFieldFormatter : NSFormatter
{
    int maxLength;
}
- (void)setMaximumLength:(int)len;
- (int)maximumLength;
@end

/* This formatter subclass will allow us to limit
 * the text length in an entryfield.
 */
@implementation DWEntryFieldFormatter
-(id)init
{
    self = [super init];
    maxLength = INT_MAX;
    return self;
}
-(void)setMaximumLength:(int)len { maxLength = len; }
-(int)maximumLength { return maxLength; }
-(NSString *)stringForObjectValue:(id)object { return (NSString *)object; }
-(BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error { *object = string; return YES; }
-(BOOL)isPartialStringValid:(NSString **)partialStringPtr
       proposedSelectedRange:(NSRangePointer)proposedSelRangePtr
              originalString:(NSString *)origString
       originalSelectedRange:(NSRange)origSelRange
            errorDescription:(NSString **)error
{
    if([*partialStringPtr length] > maxLength)
    {
        return NO;
    }
    return YES;
}
-(NSAttributedString *)attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes { return nil; }
@end

/* Subclass for a entryfield type */
@interface DWEntryField : NSTextField
{
    void *userdata;
    id clickDefault;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)setClickDefault:(id)input;
@end

@implementation DWEntryField
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)setClickDefault:(id)input { clickDefault = input; }
-(void)keyUp:(NSEvent *)theEvent
{
    unichar vk = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
    if(clickDefault && vk == VK_RETURN)
    {
        if([clickDefault isKindOfClass:[NSButton class]])
            [clickDefault buttonClicked:self];
        else
            [[self window] makeFirstResponder:clickDefault];
    } else
    {
        [super keyUp:theEvent];
    }
}
-(BOOL)performKeyEquivalent:(NSEvent *)theEvent
{
    if(([theEvent modifierFlags] & DWEventModifierFlagDeviceIndependentFlagsMask) == DWEventModifierFlagCommand)
    {
        if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"x"])
            return [NSApp sendAction:@selector(cut:) to:[[self window] firstResponder] from:self];
        else if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"c"])
            return [NSApp sendAction:@selector(copy:) to:[[self window] firstResponder] from:self];
        else if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"v"])
            return [NSApp sendAction:@selector(paste:) to:[[self window] firstResponder] from:self];
        else if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"a"])
            return [NSApp sendAction:@selector(selectAll:) to:[[self window] firstResponder] from:self];
    }
    return [super performKeyEquivalent:theEvent];
}
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a text and status text type */
@interface DWText : NSTextField
{
    void *userdata;
    id clickDefault;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
@end

@implementation DWText
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); [super dealloc]; }
@end


/* Subclass for a entryfield password type */
@interface DWEntryFieldPassword : NSSecureTextField
{
    void *userdata;
    id clickDefault;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)setClickDefault:(id)input;
@end

@implementation DWEntryFieldPassword
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)setClickDefault:(id)input { clickDefault = input; }
-(void)keyUp:(NSEvent *)theEvent
{
    if(clickDefault && [[theEvent charactersIgnoringModifiers] characterAtIndex:0] == VK_RETURN)
    {
        if([clickDefault isKindOfClass:[NSButton class]])
            [clickDefault buttonClicked:self];
        else
            [[self window] makeFirstResponder:clickDefault];
    }
    else
    {
        [super keyUp:theEvent];
    }
}
-(BOOL)performKeyEquivalent:(NSEvent *)theEvent
{
    if(([theEvent modifierFlags] & DWEventModifierFlagDeviceIndependentFlagsMask) == DWEventModifierFlagCommand)
    {
        if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"x"])
            return [NSApp sendAction:@selector(cut:) to:[[self window] firstResponder] from:self];
        else if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"c"])
            return [NSApp sendAction:@selector(copy:) to:[[self window] firstResponder] from:self];
        else if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"v"])
            return [NSApp sendAction:@selector(paste:) to:[[self window] firstResponder] from:self];
        else if ([[theEvent charactersIgnoringModifiers] isEqualToString:@"a"])
            return [NSApp sendAction:@selector(selectAll:) to:[[self window] firstResponder] from:self];
    }
    return [super performKeyEquivalent:theEvent];
}
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a Notebook control type */
@interface DWNotebook : NSTabView
#ifdef BUILDING_FOR_SNOW_LEOPARD
<NSTabViewDelegate>
#endif
{
    void *userdata;
    int pageid;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(int)pageid;
-(void)setPageid:(int)input;
-(void)tabView:(NSTabView *)notebook didSelectTabViewItem:(NSTabViewItem *)notepage;
@end

/* Subclass for a Notebook page type */
@interface DWNotebookPage : NSTabViewItem
{
    void *userdata;
    int pageid;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(int)pageid;
-(void)setPageid:(int)input;
@end

@implementation DWNotebook
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(int)pageid { return pageid; }
-(void)setPageid:(int)input { pageid = input; }
-(void)tabView:(NSTabView *)notebook didSelectTabViewItem:(NSTabViewItem *)notepage
{
    id object = [notepage view];
    DWNotebookPage *page = (DWNotebookPage *)notepage;

    if([object isMemberOfClass:[DWBox class]])
    {
        DWBox *view = object;
        Box *box = [view box];
        NSSize size = [view frame].size;
        _dw_do_resize(box, size.width, size.height);
        _dw_handle_resize_events(box);
    }
    _dw_event_handler(self, DW_INT_TO_POINTER([page pageid]), _DW_EVENT_SWITCH_PAGE);
}
-(void)keyDown:(NSEvent *)theEvent
{
    unichar vk = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];

    if(vk == NSTabCharacter || vk == NSBackTabCharacter)
        [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
    else if(vk == NSLeftArrowFunctionKey)
    {
        NSArray *pages = [self tabViewItems];
        DWNotebookPage *page = (DWNotebookPage *)[self selectedTabViewItem];
        NSUInteger index = [pages indexOfObject:page];

        if(index != NSNotFound)
        {
            if(index > 0)
               [self selectTabViewItem:[pages objectAtIndex:(index-1)]];
            else
               [self selectTabViewItem:[pages objectAtIndex:0]];

        }
    }
    else if(vk == NSRightArrowFunctionKey)
    {
        NSArray *pages = [self tabViewItems];
        DWNotebookPage *page = (DWNotebookPage *)[self selectedTabViewItem];
        NSUInteger index = [pages indexOfObject:page];
        NSUInteger count = [pages count];

        if(index != NSNotFound)
        {
            if(index + 1 < count)
                [self selectTabViewItem:[pages objectAtIndex:(index+1)]];
            else
                [self selectTabViewItem:[pages objectAtIndex:(count-1)]];

        }
    }
    [super keyDown:theEvent];
}
-(void)insertTab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectNextKeyView:self]; }
-(void)insertBacktab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectPreviousKeyView:self]; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

@implementation DWNotebookPage
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(int)pageid { return pageid; }
-(void)setPageid:(int)input { pageid = input; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a color chooser type */
@interface DWColorChoose : NSColorPanel
{
    DWDialog *dialog;
    NSColor *pickedcolor;
}
-(void)changeColor:(id)sender;
-(void)setDialog:(DWDialog *)input;
-(DWDialog *)dialog;
@end

@implementation DWColorChoose
-(void)changeColor:(id)sender
{
    if(!dialog)
        [self close];
    else
        pickedcolor = [self color];
}
-(BOOL)windowShouldClose:(id)window
{
    if(dialog)
    {
        DWDialog *d = dialog;
        dialog = nil;
        dw_dialog_dismiss(d, pickedcolor);
    }
    [self close];
    return NO;
}
-(void)setDialog:(DWDialog *)input { dialog = input; }
-(DWDialog *)dialog { return dialog; }
@end

/* Subclass for a font chooser type */
@interface DWFontChoose : NSFontPanel
{
    DWDialog *dialog;
}
-(void)setDialog:(DWDialog *)input;
-(DWDialog *)dialog;
@end

@implementation DWFontChoose
-(BOOL)windowShouldClose:(id)window
{
    DWDialog *d = dialog; dialog = nil;
    NSFont *pickedfont = [DWFontManager selectedFont];
    dw_dialog_dismiss(d, pickedfont);
    [window orderOut:nil];
    return NO;
}
-(void)setDialog:(DWDialog *)input { dialog = input; }
-(DWDialog *)dialog { return dialog; }
@end

/* Subclass for a splitbar type */
@interface DWSplitBar : NSSplitView
#ifdef BUILDING_FOR_SNOW_LEOPARD
<NSSplitViewDelegate>
#endif
{
    void *userdata;
    float percent;
    NSInteger Tag;
}
-(void)splitViewDidResizeSubviews:(NSNotification *)aNotification;
-(void)setTag:(NSInteger)tag;
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(float)percent;
-(void)setPercent:(float)input;
@end

@implementation DWSplitBar
-(void)splitViewDidResizeSubviews:(NSNotification *)aNotification
{
    NSArray *views = [self subviews];
    id object;

    for(object in views)
    {
        if([object isMemberOfClass:[DWBox class]])
        {
            DWBox *view = object;
            Box *box = [view box];
            NSSize size = [view frame].size;
            _dw_do_resize(box, size.width, size.height);
            _dw_handle_resize_events(box);
        }
    }
}
-(void)setTag:(NSInteger)tag { Tag = tag; }
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(float)percent { return percent; }
-(void)setPercent:(float)input { percent = input; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a slider type */
@interface DWSlider : NSSlider
{
    void *userdata;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)sliderChanged:(id)sender;
@end

@implementation DWSlider
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)sliderChanged:(id)sender { _dw_event_handler(self, (void *)[self integerValue], _DW_EVENT_VALUE_CHANGED); }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a slider type */
@interface DWScrollbar : NSScroller
{
    void *userdata;
    double range;
    double visible;
    int vertical;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(float)range;
-(float)visible;
-(int)vertical;
-(void)setVertical:(int)value;
-(void)setRange:(double)input1 andVisible:(double)input2;
-(void)scrollerChanged:(id)sender;
@end

@implementation DWScrollbar
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(float)range { return range; }
-(float)visible { return visible; }
-(int)vertical { return vertical; }
-(void)setVertical:(int)value { vertical = value; }
-(void)setRange:(double)input1 andVisible:(double)input2 { range = input1; visible = input2; }
-(void)scrollerChanged:(id)sender
{
    double max = (range - visible);
    double result = ([self doubleValue] * max);
    double newpos = result;

    switch ([sender hitPart])
    {
#ifndef BUILDING_FOR_YOSEMITE
        case NSScrollerDecrementLine:
            if(newpos > 0)
            {
                newpos--;
            }
            break;

        case NSScrollerIncrementLine:
            if(newpos < max)
            {
                newpos++;
            }
            break;
#endif
        case NSScrollerDecrementPage:
            newpos -= visible;
            if(newpos < 0)
            {
                newpos = 0;
            }
            break;

        case NSScrollerIncrementPage:
            newpos += visible;
            if(newpos > max)
            {
                newpos = max;
            }
            break;

        default:
            ; /* do nothing */
    }
    int newposi = (int)newpos;
    newpos = (newpos - (double)newposi) > 0.5 ? (double)(newposi + 1) : (double)newposi;
    if(newpos != result)
    {
        [self setDoubleValue:(newpos/max)];
    }
    _dw_event_handler(self, DW_INT_TO_POINTER((int)newpos), _DW_EVENT_VALUE_CHANGED);
}
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a MLE type */
@interface DWMLE : NSTextView
{
    void *userdata;
    id scrollview;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(id)scrollview;
-(void)setScrollview:(id)input;
@end

@implementation DWMLE
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(id)scrollview { return scrollview; }
-(void)setScrollview:(id)input { scrollview = input; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [scrollview release]; [super dealloc]; }
@end

#ifndef DW_USE_NSVIEW
/* Subclass NSTextFieldCell for displaying image and text */
@interface DWImageAndTextCell : NSTextFieldCell
{
@private
    NSImage	*image;
}
-(void)setImage:(NSImage *)anImage;
-(NSImage *)image;
-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
-(NSSize)cellSize;
@end

@implementation DWImageAndTextCell
-(void)dealloc
{
    [image release];
    image = nil;
    [super dealloc];
}
-copyWithZone:(NSZone *)zone
{
    DWImageAndTextCell *cell = (DWImageAndTextCell *)[super copyWithZone:zone];
    cell->image = [image retain];
    return cell;
}
-(void)setImage:(NSImage *)anImage
{
    if(anImage != image)
    {
        [image release];
        image = [anImage retain];
    }
}
-(NSImage *)image
{
    return [[image retain] autorelease];
}
-(NSRect)imageFrameForCellFrame:(NSRect)cellFrame
{
    if(image != nil)
    {
        NSRect imageFrame;
        imageFrame.size = [image size];
        imageFrame.origin = cellFrame.origin;
        imageFrame.origin.x += 3;
        imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2);
        return imageFrame;
    }
    else
        return NSZeroRect;
}
-(void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent
{
    NSRect textFrame, imageFrame;
    NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge);
    [super editWithFrame: textFrame inView: controlView editor:textObj delegate:anObject event: theEvent];
}
-(void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength
{
    NSRect textFrame, imageFrame;
    NSDivideRect (aRect, &imageFrame, &textFrame, 3 + [image size].width, NSMinXEdge);
    [super selectWithFrame: textFrame inView: controlView editor:textObj delegate:anObject start:selStart length:selLength];
}
-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
    if(image != nil)
    {
        NSSize	imageSize;
        NSRect	imageFrame;
        SEL sdir = NSSelectorFromString(@"drawInRect:fromRect:operation:fraction:respectFlipped:hints:");
        NSCompositingOperation op = DWCompositingOperationSourceOver;

        imageSize = [image size];
        NSDivideRect(cellFrame, &imageFrame, &cellFrame, 3 + imageSize.width, NSMinXEdge);
        if ([self drawsBackground])
        {
            [[self backgroundColor] set];
            NSRectFill(imageFrame);
        }
        imageFrame.origin.x += 3;
        imageFrame.size = imageSize;

        /* New method for 10.6 and later */
        if([image respondsToSelector:sdir])
        {
            DWIMP idir = (DWIMP)[image methodForSelector:sdir];

            imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2);

            idir(image, sdir, imageFrame, NSZeroRect, op, 1.0, YES, nil);
        }
        else
        {
            /* Old method for 10.5 */
            SEL sctp = NSSelectorFromString(@"compositeToPoint:operation:");

            if ([controlView isFlipped])
                imageFrame.origin.y += ceil((cellFrame.size.height + imageFrame.size.height) / 2);
            else
                imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2);

            if([image respondsToSelector:sctp])
            {
                DWIMP ictp = (DWIMP)[image methodForSelector:sctp];

                ictp(image, sctp, imageFrame.origin, op);
            }
        }
    }
    [super drawWithFrame:cellFrame inView:controlView];
}
-(NSSize)cellSize
{
    NSSize cellSize = [super cellSize];
    cellSize.width += (image ? [image size].width : 0) + 3;
    return cellSize;
}
@end
#endif

#ifdef DW_USE_NSVIEW
NSTableCellView *_dw_table_cell_view_new(NSImage *icon, NSString *text)
{
    NSTableCellView *browsercell = [[[NSTableCellView alloc] init] autorelease];
    [browsercell setAutoresizesSubviews:YES];
    if(icon)
    {
        NSImageView *iv = [[[NSImageView alloc] init] autorelease];
        [iv setAutoresizingMask:NSViewHeightSizable|(text ? 0 :NSViewWidthSizable)];
        [iv setImage:icon];
        [browsercell setImageView:iv];
        [browsercell addSubview:iv];
    }
    if(text)
    {
        NSTextField *tf = [[[NSTextField alloc] init] autorelease];
        [tf setAutoresizingMask:NSViewHeightSizable|NSViewWidthSizable];
        [tf setStringValue:text];
        [tf setEditable:NO];
        [tf setBezeled:NO];
        [tf setBordered:NO];
        [tf setDrawsBackground:NO];
        [[tf cell] setVCenter:YES];
        [browsercell setTextField:tf];
        [browsercell addSubview:tf];
    }
    return browsercell;
}

void _dw_table_cell_view_layout(NSTableCellView *result)
{
    /* Adjust the frames of the textField and imageView when both are present */
    if([result imageView] && [result textField])
    {
        NSImageView *iv = [result imageView];
        NSImage *icon = [iv image];
        NSTextField *tf = [result textField];
        NSRect rect = result.frame;
        CGFloat width = [icon size].width;

        [iv setFrame:NSMakeRect(0,0,width,rect.size.height)];

        /* Adjust the rect to allow space for the image */
        rect.origin.x += width;
        if(rect.size.width)
            rect.size.width -= width;
        [tf setFrame:rect];
    }
}
#endif

@interface DWFocusRingScrollView : NSScrollView
{
    BOOL shouldDrawFocusRing;
    NSResponder* lastResp;
}
@end

@implementation DWFocusRingScrollView
-(BOOL)needsDisplay;
{
    NSResponder* resp = nil;
    
    if([[self window] isKeyWindow])
    {
        resp = [[self window] firstResponder];
        if (resp == lastResp)
            return [super needsDisplay];
    }
    else if (lastResp == nil)
    {
        return [super needsDisplay];
    }
    shouldDrawFocusRing = (resp != nil && [resp isKindOfClass:[NSView class]] && [(NSView*)resp isDescendantOf:self]);
    lastResp = resp;
    [self setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
    return YES;
}
-(void)drawRect:(NSRect)rect
{
    [super drawRect:rect];
                                                                                                    
    if(shouldDrawFocusRing)
    {
        NSSetFocusRingStyle(NSFocusRingOnly);
        NSRectFill(rect);
    }
}
@end

/* Subclass for a Container/List type */
@interface DWContainer : NSTableView
#ifdef BUILDING_FOR_SNOW_LEOPARD
<NSTableViewDataSource,NSTableViewDelegate>
#endif
{
    void *userdata;
    NSMutableArray *tvcols;
    NSMutableArray *data;
    NSMutableArray *types;
    NSPointerArray *titles;
    NSPointerArray *rowdatas;
    NSColor *fgcolor, *oddcolor, *evencolor;
    unsigned long dw_oddcolor, dw_evencolor;
    unsigned long _DW_COLOR_ROW_ODD, _DW_COLOR_ROW_EVEN;
    int lastAddPoint, lastQueryPoint;
    id scrollview;
    int filesystem;
}
-(NSInteger)numberOfRowsInTableView:(NSTableView *)aTable;
-(id)tableView:(NSTableView *)aTable objectValueForTableColumn:(NSTableColumn *)aCol row:(NSInteger)aRow;
-(BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)setFilesystem:(int)input;
-(int)filesystem;
-(id)scrollview;
-(void)setScrollview:(id)input;
-(void)addColumn:(NSTableColumn *)input andType:(int)type;
-(NSTableColumn *)getColumn:(int)col;
-(int)addRow:(NSArray *)input;
-(int)addRows:(int)number;
-(void)editCell:(id)input at:(int)row and:(int)col;
-(void)removeRow:(int)row;
-(void)setRow:(int)row title:(const char *)input;
-(void *)getRowTitle:(int)row;
-(id)getRow:(int)row and:(int)col;
-(int)cellType:(int)col;
-(int)lastAddPoint;
-(int)lastQueryPoint;
-(void)setLastQueryPoint:(int)input;
-(void)clear;
-(void)setup;
-(void)optimize;
-(NSSize)getsize;
-(void)setForegroundColor:(NSColor *)input;
-(void)doubleClicked:(id)sender;
-(void)keyUp:(NSEvent *)theEvent;
-(void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn;
-(void)selectionChanged:(id)sender;
-(NSMenu *)menuForEvent:(NSEvent *)event;
#ifdef DW_USE_NSVIEW
-(void)tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row;
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
#else
-(void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
#endif
@end

@implementation DWContainer
-(NSInteger)numberOfRowsInTableView:(NSTableView *)aTable
{
    if(tvcols && data)
    {
        int cols = (int)[tvcols count];
        int total = (int)[data count];
        if(cols && total)
        {
            return total / cols;
        }
    }
    return 0;
}
-(id)tableView:(NSTableView *)aTable objectValueForTableColumn:(NSTableColumn *)aCol row:(NSInteger)aRow
{
    if(tvcols && data)
    {
        int z, col = -1;
        int count = (int)[tvcols count];

        for(z=0;z<count;z++)
        {
            if([tvcols objectAtIndex:z] == aCol)
            {
                col = z;
                break;
            }
        }
        if(col != -1)
        {
            int index = (int)(aRow * count) + col;
            if(index < [data count])
            {
                id this = [data objectAtIndex:index];
                return ([this isKindOfClass:[NSNull class]]) ? nil : this;
            }
        }
    }
    return nil;
}
-(BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { return NO; }
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)setFilesystem:(int)input { filesystem = input; }
-(int)filesystem { return filesystem; }
-(id)scrollview { return scrollview; }
-(void)setScrollview:(id)input { scrollview = input; }
-(void)addColumn:(NSTableColumn *)input andType:(int)type { if(tvcols) { [tvcols addObject:input]; [types addObject:[NSNumber numberWithInt:type]]; } }
-(NSTableColumn *)getColumn:(int)col { if(tvcols) { return [tvcols objectAtIndex:col]; } return nil; }
-(void)refreshColors
{
    NSColor *oldodd = oddcolor;
    NSColor *oldeven = evencolor;
    unsigned long thisodd = dw_oddcolor == DW_CLR_DEFAULT ? _DW_COLOR_ROW_ODD : dw_oddcolor;
    unsigned long _odd = _dw_get_color(thisodd);
    unsigned long thiseven = dw_evencolor == DW_CLR_DEFAULT ? _DW_COLOR_ROW_EVEN : dw_evencolor;
    unsigned long _even = _dw_get_color(thiseven);

    /* Get the NSColor for non-default colors */
    if(thisodd != DW_RGB_TRANSPARENT)
        oddcolor = [[NSColor colorWithDeviceRed: DW_RED_VALUE(_odd)/255.0 green: DW_GREEN_VALUE(_odd)/255.0 blue: DW_BLUE_VALUE(_odd)/255.0 alpha: 1] retain];
    else
        oddcolor = NULL;
    if(thiseven != DW_RGB_TRANSPARENT)
        evencolor = [[NSColor colorWithDeviceRed: DW_RED_VALUE(_even)/255.0 green: DW_GREEN_VALUE(_even)/255.0 blue: DW_BLUE_VALUE(_even)/255.0 alpha: 1] retain];
    else
        evencolor = NULL;
    [oldodd release];
    [oldeven release];
}
-(void)setRowBgOdd:(unsigned long)oddcol andEven:(unsigned long)evencol
{
    /* Save the set colors in case we get a theme change */
    dw_oddcolor = oddcol;
    dw_evencolor = evencol;
    [self refreshColors];
}
-(void)checkDark
{
    /* Update any system colors based on the Dark Mode */
    _DW_COLOR_ROW_EVEN = DW_RGB_TRANSPARENT;
    if(_dw_is_dark(self))
        _DW_COLOR_ROW_ODD = DW_RGB(100, 100, 100);
    else
        _DW_COLOR_ROW_ODD = DW_RGB(230, 230, 230);
    /* Only refresh if we've been setup already */
    if(titles)
        [self refreshColors];
}
-(void)viewDidChangeEffectiveAppearance { [self checkDark]; }
-(int)insertRow:(NSArray *)input at:(int)index
{
    if(data)
    {
        unsigned long start = [tvcols count] * index;
        NSIndexSet *set = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(start, start + [tvcols count])];
        if(index < lastAddPoint)
        {
            lastAddPoint++;
        }
        [data insertObjects:input atIndexes:set];
        [titles insertPointer:NULL atIndex:index];
        [rowdatas insertPointer:NULL atIndex:index];
        [set release];
        return (int)[titles count];
    }
    return 0;
}
-(int)addRow:(NSArray *)input
{
    if(data)
    {
        lastAddPoint = (int)[titles count];
        [data addObjectsFromArray:input];
        [titles addPointer:NULL];
        [rowdatas addPointer:NULL];
        return (int)[titles count];
    }
    return 0;
}
-(int)addRows:(int)number
{
    if(tvcols)
    {
        int count = (int)(number * [tvcols count]);
        int z;

        lastAddPoint = (int)[titles count];

        for(z=0;z<count;z++)
        {
            [data addObject:[NSNull null]];
        }
        for(z=0;z<number;z++)
        {
            [titles addPointer:NULL];
            [rowdatas addPointer:NULL];
        }
        return (int)[titles count];
    }
    return 0;
}
#ifdef DW_USE_NSVIEW
-(void)tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
{
    /* Handle drawing alternating row colors if enabled */
    if ((row % 2) == 0)
    {
        if(evencolor)
            [rowView setBackgroundColor:evencolor];
    }
    else
    {
        if(oddcolor)
            [rowView setBackgroundColor:oddcolor];
    }
}
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
{
    /* Not reusing cell views, so get the cell from our array */
    int index = (int)(row * [tvcols count]) + (int)[tvcols indexOfObject:tableColumn];
    id celldata = [data objectAtIndex:index];

    /* The data is already a NSTableCellView so just return that */
    if([celldata isMemberOfClass:[NSTableCellView class]])
    {
        NSTableCellView *result = celldata;
        NSTextAlignment align = [[tableColumn headerCell] alignment];
        
        _dw_table_cell_view_layout(result);
        
        /* Copy the alignment setting from the column,
         * and set the text color from the container.
         */
        if([result textField])
        {
            NSTextField *tf = [result textField];
            
            [tf setAlignment:align];
            if(fgcolor)
                [tf setTextColor:fgcolor];
        }
        
        /* Return the result */
        return result;
    }
    return nil;
}
#else
-(void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    DWImageAndTextCell *bcell = cell, *tcell = cell;

    /* Handle drawing image and text if necessary */
    if([cell isMemberOfClass:[DWImageAndTextCell class]])
    {
        int index = (int)(row * [tvcols count]);
        DWImageAndTextCell *browsercell = [data objectAtIndex:index];
        NSImage *img = [browsercell image];
        [bcell setImage:img];
    }
    
    if([tcell isKindOfClass:[NSTextFieldCell class]])
    {
        /* Handle drawing alternating row colors if enabled */
        if ((row % 2) == 0)
        {
            if(evencolor)
            {
                [tcell setDrawsBackground:YES];
                [tcell setBackgroundColor:evencolor];
            }
            else
                [tcell setDrawsBackground:NO];
        }
        else
        {
            if(oddcolor)
            {
                [tcell setDrawsBackground:YES];
                [tcell setBackgroundColor:oddcolor];
            }
            else
                [tcell setDrawsBackground:NO];
        }
    }
}
#endif
-(void)editCell:(id)input at:(int)row and:(int)col
{
    if(tvcols)
    {
        int index = (int)(row * [tvcols count]) + col;
        if(index < [data count])
        {
            if(!input)
                input = [NSNull null];
            [data replaceObjectAtIndex:index withObject:input];
        }
    }
}
-(void)removeRow:(int)row
{
    if(tvcols)
    {
        int z, start, end;
        int count = (int)[tvcols count];
        void *oldtitle;

        start = (count * row);
        end = start + count;

        for(z=start;z<end;z++)
        {
            [data removeObjectAtIndex:start];
        }
        oldtitle = [titles pointerAtIndex:row];
        [titles removePointerAtIndex:row];
        [rowdatas removePointerAtIndex:row];
        if(lastAddPoint > 0 && lastAddPoint > row)
        {
            lastAddPoint--;
        }
        if(oldtitle)
            free(oldtitle);
    }
}
-(void)setRow:(int)row title:(const char *)input
{
    if(titles && input)
    {
        void *oldtitle = [titles pointerAtIndex:row];
        void *newtitle = input ? (void *)strdup(input) : NULL;
        [titles replacePointerAtIndex:row withPointer:newtitle];
        if(oldtitle)
            free(oldtitle);
    }
}
-(void)setRowData:(int)row title:(void *)input { if(rowdatas && input) { [rowdatas replacePointerAtIndex:row withPointer:input]; } }
-(void *)getRowTitle:(int)row { if(titles && row > -1) { return [titles pointerAtIndex:row]; } return NULL; }
-(void *)getRowData:(int)row { if(rowdatas && row > -1) { return [rowdatas pointerAtIndex:row]; } return NULL; }
-(id)getRow:(int)row and:(int)col { if(data) { int index = (int)(row * [tvcols count]) + col; return [data objectAtIndex:index]; } return nil; }
-(int)cellType:(int)col { return [[types objectAtIndex:col] intValue]; }
-(int)lastAddPoint { return lastAddPoint; }
-(int)lastQueryPoint { return lastQueryPoint; }
-(void)setLastQueryPoint:(int)input { lastQueryPoint = input; }
-(void)clear
{
    if(data)
    {
        [data removeAllObjects];
        while([titles count])
        {
            void *oldtitle = [titles pointerAtIndex:0];
            [titles removePointerAtIndex:0];
            [rowdatas removePointerAtIndex:0];
            if(oldtitle)
                free(oldtitle);
        }
    }
    lastAddPoint = 0;
}
-(void)setup
{
    SEL swopa = NSSelectorFromString(@"pointerArrayWithWeakObjects");

    if(![[NSPointerArray class] respondsToSelector:swopa])
        swopa = NSSelectorFromString(@"weakObjectsPointerArray");
    if(![[NSPointerArray class] respondsToSelector:swopa])
        return;

    DWIMP iwopa = (DWIMP)[[NSPointerArray class] methodForSelector:swopa];

    titles = iwopa([NSPointerArray class], swopa);
    [titles retain];
    rowdatas = iwopa([NSPointerArray class], swopa);
    [rowdatas retain];
    tvcols = [[[NSMutableArray alloc] init] retain];
    data = [[[NSMutableArray alloc] init] retain];
    types = [[[NSMutableArray alloc] init] retain];
    if(!dw_oddcolor && !dw_evencolor)
        dw_oddcolor = dw_evencolor = DW_CLR_DEFAULT;
    [self checkDark];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(selectionChanged:) name:NSTableViewSelectionDidChangeNotification object:self];
}
-(NSSize)getsize
{
    int cwidth = 0, cheight = 0;

    if(tvcols)
    {
        int z;
        int colcount = (int)[tvcols count];
        int rowcount = (int)[self numberOfRowsInTableView:self];

        for(z=0;z<colcount;z++)
        {
            NSTableColumn *column = [tvcols objectAtIndex:z];
            int width = [column width];

            if(rowcount > 0)
            {
                int x;

                for(x=0;x<rowcount;x++)
                {
#ifdef DW_USE_NSVIEW
                    NSTableCellView *cell = [self viewAtColumn:z row:x makeIfNecessary:YES];
                    int thiswidth = 4, thisheight = 0;
                    
                    if([cell imageView])
                    {
                        thiswidth += [[cell imageView] image].size.width;
                        thisheight = [[cell imageView] image].size.height;
                    }
                    if([cell textField])
                    {
                        int textheight = [[cell textField] intrinsicContentSize].width;
                        thiswidth += [[cell textField] intrinsicContentSize].width;
                        if(textheight > thisheight)
                            thisheight = textheight;
                    }
                    
                    /* If on the first column... add up the heights */
                    if(z == 0)
                        cheight += thisheight;
#else
                    NSCell *cell = [self preparedCellAtColumn:z row:x];
                    int thiswidth = [cell cellSize].width;

                    /* If on the first column... add up the heights */
                    if(z == 0)
                        cheight += [cell cellSize].height;

                    /* Check the image inside the cell to get its width */
                    if([cell isMemberOfClass:[NSImageCell class]])
                    {
                        int index = (int)(x * [tvcols count]) + z;
                        NSImage *image = [data objectAtIndex:index];
                        if([image isMemberOfClass:[NSImage class]])
                        {
                            thiswidth = [image size].width;
                        }
                    }
#endif
                    
                    if(thiswidth > width)
                    {
                        width = thiswidth;
                    }
                }
                /* If the image is missing default the optimized width to 16. */
                if(!width && [[types objectAtIndex:z] intValue] & DW_CFA_BITMAPORICON)
                {
                    width = 16;
                }
            }
            if(width)
                cwidth += width;
        }
    }
    cwidth += 16;
    cheight += 16;
    return NSMakeSize(cwidth, cheight);
}
-(void)optimize
{
    if(tvcols)
    {
        int z;
        int colcount = (int)[tvcols count];
        int rowcount = (int)[self numberOfRowsInTableView:self];

        for(z=0;z<colcount;z++)
        {
            NSTableColumn *column = [tvcols objectAtIndex:z];
            if([column resizingMask] != NSTableColumnNoResizing)
            {
                if(rowcount > 0)
                {
                    int x;
                    NSCell *colcell = [column headerCell];
                    int width = [colcell cellSize].width;

                    for(x=0;x<rowcount;x++)
                    {
#ifdef DW_USE_NSVIEW
                        NSTableCellView *cell = [self viewAtColumn:z row:x makeIfNecessary:YES];
                        int thiswidth = 4;
                        
                        if([cell imageView])
                            thiswidth += [[cell imageView] image].size.width;
                        if([cell textField])
                            thiswidth += [[cell textField] intrinsicContentSize].width;
#else
                        NSCell *cell = [self preparedCellAtColumn:z row:x];
                        int thiswidth = [cell cellSize].width;

                        /* Check the image inside the cell to get its width */
                        if([cell isMemberOfClass:[NSImageCell class]])
                        {
                            int index = (int)(x * [tvcols count]) + z;
                            NSImage *image = [data objectAtIndex:index];
                            if([image isMemberOfClass:[NSImage class]])
                            {
                                thiswidth = [image size].width;
                            }
                        }
#endif
                        
                        if(thiswidth > width)
                        {
                            width = thiswidth;
                        }
                    }
                    /* If the image is missing default the optimized width to 16. */
                    if(!width && [[types objectAtIndex:z] intValue] & DW_CFA_BITMAPORICON)
                    {
                        width = 16;
                    }
                    /* Sanity check... don't set the width to 0 */
                    if(width)
                    {
                        [column setWidth:width+1];
                    }
                }
                else
                {
                    if(self.headerView)
                        [column sizeToFit];
                }
            }
        }
    }
}
-(void)setForegroundColor:(NSColor *)input
{
    int z, count = (int)[tvcols count];

    fgcolor = input;
    [fgcolor retain];

    for(z=0;z<count;z++)
    {
        NSTableColumn *tableColumn = [tvcols objectAtIndex:z];
        NSTextFieldCell *cell = [tableColumn dataCell];
        [cell setTextColor:fgcolor];
    }
}
-(void)doubleClicked:(id)sender
{
    void *params[2];

    params[0] = (void *)[self getRowTitle:(int)[self selectedRow]];
    params[1] = (void *)[self getRowData:(int)[self selectedRow]];

    /* Handler for container class */
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_ITEM_ENTER);
}
-(void)keyUp:(NSEvent *)theEvent
{
    if([[theEvent charactersIgnoringModifiers] characterAtIndex:0] == VK_RETURN)
    {
        void *params[2];

        params[0] = (void *)[self getRowTitle:(int)[self selectedRow]];
        params[1] = (void *)[self getRowData:(int)[self selectedRow]];

        _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_ITEM_ENTER);
    }
    [super keyUp:theEvent];
}

-(void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn
{
    NSUInteger index = [tvcols indexOfObject:tableColumn];

    /* Handler for column click class */
    _dw_event_handler(self, (NSEvent *)index, _DW_EVENT_COLUMN_CLICK);
}
-(void)selectionChanged:(id)sender
{
    void *params[2];

    params[0] = (void *)[self getRowTitle:(int)[self selectedRow]];
    params[1] = (void *)[self getRowData:(int)[self selectedRow]];

    /* Handler for container class */
    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_ITEM_SELECT);
    /* Handler for listbox class */
    _dw_event_handler(self, DW_INT_TO_POINTER((int)[self selectedRow]), _DW_EVENT_LIST_SELECT);
}
-(NSMenu *)menuForEvent:(NSEvent *)event
{
    NSPoint where = [self convertPoint:[event locationInWindow] fromView:nil];
    int row = (int)[self rowAtPoint:where];
    void *params[2];

    params[0] = [self getRowTitle:row];
    params[1] = [self getRowData:row];

    _dw_event_handler(self, (NSEvent *)params, _DW_EVENT_ITEM_CONTEXT);
    return nil;
}
-(void)keyDown:(NSEvent *)theEvent
{
    unichar vk = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];

    if(vk == NSTabCharacter || vk == NSBackTabCharacter)
        [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
    [super keyDown:theEvent];
}
-(void)insertTab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectNextKeyView:self]; }
-(void)insertBacktab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectPreviousKeyView:self]; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [self clear]; [scrollview release]; [super dealloc]; }
@end

/* Dive into the tree freeing all desired child nodes */
void _dw_free_tree_recurse(NSMutableArray *node, NSMutableArray *item)
{
    if(node && ([node isKindOfClass:[NSArray class]]))
    {
        int count = (int)[node count];
        NSInteger index = -1;
        int z;

        if(item)
            index = [node indexOfObject:item];

        for(z=0;z<count;z++)
        {
            NSMutableArray *pnt = [node objectAtIndex:z];
            NSMutableArray *children = nil;

            if(pnt && [pnt isKindOfClass:[NSArray class]])
            {
                children = (NSMutableArray *)[pnt objectAtIndex:3];
            }

            if(z == index)
            {
                _dw_free_tree_recurse(children, NULL);
                [node removeObjectAtIndex:z];
                count = (int)[node count];
                index = -1;
                z--;
            }
            else if(item == NULL)
            {
                NSString *oldstr = [pnt objectAtIndex:1];
                [oldstr release];
                _dw_free_tree_recurse(children, item);
            }
            else
                _dw_free_tree_recurse(children, item);
        }
    }
    if(!item)
    {
        [node release];
    }
}

/* Subclass for a Tree type */
@interface DWTree : NSOutlineView
#ifdef BUILDING_FOR_SNOW_LEOPARD
<NSOutlineViewDataSource,NSOutlineViewDelegate>
#endif
{
    void *userdata;
    NSTableColumn *treecol;
    NSMutableArray *data;
    /* Each data item consists of a linked lists of tree item data.
     * NSImage *, NSString *, Item Data *, NSMutableArray * of Children
     */
    id scrollview;
    NSColor *fgcolor;
}
-(id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item;
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;
-(int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item;
#ifdef DW_USE_NSVIEW
-(NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item;
#else
-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
-(void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item;
#endif
-(BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item;
-(void)addTree:(NSMutableArray *)item and:(NSMutableArray *)parent after:(NSMutableArray *)after;
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)treeSelectionChanged:(id)sender;
-(void)treeItemExpanded:(NSNotification *)notification;
-(NSScrollView *)scrollview;
-(void)setScrollview:(NSScrollView *)input;
-(void)deleteNode:(NSMutableArray *)item;
-(void)setForegroundColor:(NSColor *)input;
-(void)clear;
@end

@implementation DWTree
-(id)init
{
    self = [super init];

    if (self)
    {
        treecol = [[NSTableColumn alloc] initWithIdentifier:@"_DWTreeColumn"];
#ifndef DW_USE_NSVIEW
        DWImageAndTextCell *browsercell = [[[DWImageAndTextCell alloc] init] autorelease];
        [treecol setDataCell:browsercell];
#endif
        [self addTableColumn:treecol];
        [self setOutlineTableColumn:treecol];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(treeSelectionChanged:) name:NSOutlineViewSelectionDidChangeNotification object:self];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(treeItemExpanded:) name:NSOutlineViewItemDidExpandNotification object:self];
    }
    return self;
}
-(id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
{
    if(item)
    {
        NSMutableArray *array = [item objectAtIndex:3];
        return ([array isKindOfClass:[NSNull class]]) ? nil : [array objectAtIndex:index];
    }
    else
    {
        return [data objectAtIndex:index];
    }
}
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
    return [self outlineView:outlineView numberOfChildrenOfItem:item] != 0;
}
-(int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
    if(item)
    {
        if([item isKindOfClass:[NSMutableArray class]])
        {
            NSMutableArray *array = [item objectAtIndex:3];
            return ([array isKindOfClass:[NSNull class]]) ? 0 : (int)[array count];
        }
        else
        {
            return 0;
        }
    }
    else
    {
        return data ? (int)[data count] : 0;
    }
}
#ifdef DW_USE_NSVIEW
-(NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    NSTableCellView *view = [outlineView makeViewWithIdentifier:[tableColumn identifier] owner:self];
    
    if([item isKindOfClass:[NSMutableArray class]])
    {
        NSMutableArray *this = (NSMutableArray *)item;
        NSImage *icon = [this objectAtIndex:0];
        NSString *text = [this objectAtIndex:1];
        if(![icon isKindOfClass:[NSImage class]])
            icon = nil;
        if(view)
        {
            NSTextField *tf = [view textField];
            NSImageView *iv = [view imageView];
            
            if(tf)
            {
                [tf setStringValue: text];
                if(fgcolor)
                    [tf setTextColor:fgcolor];
            }
            if(iv)
                [iv setImage:icon];
        }
        else
            view = _dw_table_cell_view_new(icon, text);
    }
    _dw_table_cell_view_layout(view);
    return view;
}
#else
-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
    if(item)
    {
        if([item isKindOfClass:[NSMutableArray class]])
        {
            NSMutableArray *this = (NSMutableArray *)item;
            return [this objectAtIndex:1];
        }
        else
        {
            return nil;
        }
    }
    return @"List Root";
}
-(void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    if([cell isMemberOfClass:[DWImageAndTextCell class]])
    {
        NSMutableArray *this = (NSMutableArray *)item;
        NSImage *img = [this objectAtIndex:0];
        if([img isKindOfClass:[NSImage class]])
            [(DWImageAndTextCell*)cell setImage:img];
    }
}
#endif
-(BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item { return NO; }
-(void)addTree:(NSMutableArray *)item and:(NSMutableArray *)parent after:(NSMutableArray *)after
{
    NSMutableArray *children = data;
    if(parent)
    {
        children = [parent objectAtIndex:3];
        if([children isKindOfClass:[NSNull class]])
        {
            children = [[[NSMutableArray alloc] init] retain];
            [parent replaceObjectAtIndex:3 withObject:children];
        }
    }
    else
    {
        if(!data)
        {
            children = data = [[[NSMutableArray alloc] init] retain];
        }
    }
    if(after)
    {
        NSInteger index = [children indexOfObject:after];
        int count = (int)[children count];
        if(index != NSNotFound && (index+1) < count)
            [children insertObject:item atIndex:(index+1)];
        else
            [children addObject:item];
    }
    else
    {
        [children addObject:item];
    }
}
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)treeSelectionChanged:(id)sender
{
    /* Handler for tree class */
    id item = [self itemAtRow:[self selectedRow]];

    if(item)
    {
        _dw_event_handler(self, (void *)item, _DW_EVENT_ITEM_SELECT);
    }
}
-(void)treeItemExpanded:(NSNotification *)notification
{
    id item = [[notification userInfo ] objectForKey: @"NSObject"];

    if(item)
    {
        _dw_event_handler(self, (void *)item, _DW_EVENT_TREE_EXPAND);
    }
}
-(NSMenu *)menuForEvent:(NSEvent *)event
{
    int row;
    NSPoint where = [self convertPoint:[event locationInWindow] fromView:nil];
    row = (int)[self rowAtPoint:where];
    id item = [self itemAtRow:row];
    _dw_event_handler(self, (NSEvent *)item, _DW_EVENT_ITEM_CONTEXT);
    return nil;
}
-(NSScrollView *)scrollview { return scrollview; }
-(void)setScrollview:(NSScrollView *)input { scrollview = input; }
-(void)deleteNode:(NSMutableArray *)item { _dw_free_tree_recurse(data, item); }
-(void)setForegroundColor:(NSColor *)input
{
    NSTextFieldCell *cell = [treecol dataCell];
    fgcolor = input;
    [fgcolor retain];
    [cell setTextColor:fgcolor];
}
-(void)clear { NSMutableArray *toclear = data; data = nil; _dw_free_tree_recurse(toclear, NULL); [self reloadData]; }
-(void)keyDown:(NSEvent *)theEvent
{
    unichar vk = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];

    if(vk == NSTabCharacter || vk == NSBackTabCharacter)
        [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
    [super keyDown:theEvent];
}
-(void)insertTab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectNextKeyView:self]; }
-(void)insertBacktab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectPreviousKeyView:self]; }
-(void)dealloc
{
    UserData *root = userdata;
    _dw_remove_userdata(&root, NULL, TRUE);
    _dw_free_tree_recurse(data, NULL);
    [treecol release];
    dw_signal_disconnect_by_window(self);
    [super dealloc];
}
@end

/* Subclass for a Calendar type */
@interface DWCalendar : NSDatePicker
{
    void *userdata;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
@end

@implementation DWCalendar
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a Combobox type */
@interface DWComboBox : NSComboBox
#ifdef BUILDING_FOR_SNOW_LEOPARD
<NSComboBoxDelegate>
#endif
{
    void *userdata;
    id clickDefault;
}
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(void)comboBoxSelectionDidChange:(NSNotification *)not;
-(void)setClickDefault:(id)input;
@end

@implementation DWComboBox
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(void)comboBoxSelectionDidChange:(NSNotification *)not { _dw_event_handler(self, (void *)[self indexOfSelectedItem], _DW_EVENT_LIST_SELECT); }
-(void)setClickDefault:(id)input { clickDefault = input; }
-(void)keyUp:(NSEvent *)theEvent
{
    if(clickDefault && [[theEvent charactersIgnoringModifiers] characterAtIndex:0] == VK_RETURN)
    {
        [[self window] makeFirstResponder:clickDefault];
    } else
    {
        [super keyUp:theEvent];
    }
}
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

/* Subclass for a stepper component of the spinbutton type */
/* This is a bad way of doing this... but I can't get the other methods to work */
@interface DWStepper : NSStepper
{
    id textfield;
    id parent;
}
-(void)setTextfield:(id)input;
-(id)textfield;
-(void)setParent:(id)input;
-(id)parent;
-(void)mouseDown:(NSEvent *)event;
-(void)mouseUp:(NSEvent *)event;
@end

@implementation DWStepper
-(void)setTextfield:(id)input { textfield = input; }
-(id)textfield { return textfield; }
-(void)setParent:(id)input { parent = input; }
-(id)parent { return parent; }
-(void)mouseDown:(NSEvent *)event
{
    [super mouseDown:event];
    if([[NSApp currentEvent] type] == DWEventTypeLeftMouseUp)
        [self mouseUp:event];
}
-(void)mouseUp:(NSEvent *)event
{
    [textfield takeIntValueFrom:self];
    _dw_event_handler(parent, (void *)[self integerValue], _DW_EVENT_VALUE_CHANGED);
}
-(void)keyDown:(NSEvent *)theEvent
{
    unichar vk = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
    if(vk == VK_UP || vk == VK_DOWN)
    {
        if(vk == VK_UP)
            [self setIntegerValue:([self integerValue]+[self increment])];
        else
            [self setIntegerValue:([self integerValue]-[self increment])];
        [self mouseUp:theEvent];
    }
    else
    {
        [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
        [super keyDown:theEvent];
    }
}
-(void)insertTab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectNextKeyView:self]; }
-(void)insertBacktab:(id)sender { if([[self window] firstResponder] == self) [[self window] selectPreviousKeyView:self]; }
@end

/* Subclass for a Spinbutton type */
@interface DWSpinButton : NSView
#ifdef BUILDING_FOR_SNOW_LEOPARD
<NSTextFieldDelegate>
#endif
{
    void *userdata;
    NSTextField *textfield;
    DWStepper *stepper;
    id clickDefault;
}
-(id)init;
-(void *)userdata;
-(void)setUserdata:(void *)input;
-(NSTextField *)textfield;
-(NSStepper *)stepper;
-(void)controlTextDidChange:(NSNotification *)aNotification;
-(void)setClickDefault:(id)input;
@end

@implementation DWSpinButton
-(id)init
{
    self = [super init];

    if(self)
    {
        textfield = [[[NSTextField alloc] init] autorelease];
        /* Workaround for infinite loop in Snow Leopard 10.6 */
        if(DWOSMajor == 10 && DWOSMinor < 7)
            [textfield setFrameSize:NSMakeSize(10,10)];
        [self addSubview:textfield];
        stepper = [[[DWStepper alloc] init] autorelease];
        [self addSubview:stepper];
        [stepper setParent:self];
        [stepper setTextfield:textfield];
        [textfield takeIntValueFrom:stepper];
        [textfield setDelegate:self];
    }
    return self;
}
-(void *)userdata { return userdata; }
-(void)setUserdata:(void *)input { userdata = input; }
-(NSTextField *)textfield { return textfield; }
-(NSStepper *)stepper { return stepper; }
-(void)controlTextDidChange:(NSNotification *)aNotification
{
    [stepper takeIntValueFrom:textfield];
    [textfield takeIntValueFrom:stepper];
    _dw_event_handler(self, (void *)[stepper integerValue], _DW_EVENT_VALUE_CHANGED);
}
-(void)setClickDefault:(id)input { clickDefault = input; }
-(void)keyUp:(NSEvent *)theEvent
{
    if(clickDefault && [[theEvent charactersIgnoringModifiers] characterAtIndex:0] == VK_RETURN)
    {
        [[self window] makeFirstResponder:clickDefault];
    }
    else
    {
        [super keyUp:theEvent];
    }
}
-(void)performClick:(id)sender { [textfield performClick:sender]; }
-(void)dealloc { UserData *root = userdata; _dw_remove_userdata(&root, NULL, TRUE); dw_signal_disconnect_by_window(self); [super dealloc]; }
@end

#ifdef BUILDING_FOR_MOJAVE
API_AVAILABLE(macos(10.14))
@interface DWUserNotificationCenterDelegate : NSObject <UNUserNotificationCenterDelegate>
@end

@implementation DWUserNotificationCenterDelegate
/* Called when a notification is delivered to a foreground app. */
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler API_AVAILABLE(macos(10.14))
{
    completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}
/* Called to let your app know which action was selected by the user for a given notification. */
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(macos(10.14))
{
    NSScanner *objScanner = [NSScanner scannerWithString:response.notification.request.identifier];
    unsigned long long handle;
    HWND notification;

    /* Skip the dw-notification- prefix */
    [objScanner scanString:@"dw-notification-" intoString:nil];
    [objScanner scanUnsignedLongLong:&handle];
    notification = DW_UINT_TO_POINTER(handle);
    
    if ([response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier])
    {
        /* The user dismissed the notification without taking action. */
        dw_signal_disconnect_by_window(notification);
    }
    else if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier])
    {
        /* The user launched the app. */
        _dw_event_handler(notification, nil, _DW_EVENT_CLICKED);
        dw_signal_disconnect_by_window(notification);
    }
    completionHandler();
}
@end
#endif

/* Subclass for a MDI type
 * This is just a box for display purposes... but it is a
 * unique class so it can be identified when creating windows.
 */
@interface DWMDI : DWBox {}
@end

@implementation DWMDI
@end

/* This function adds a signal handler callback into the linked list.
 */
void _dw_new_signal(ULONG message, HWND window, int msgid, void *signalfunction, void *discfunc, void *data)
{
    DWSignalHandler *new = malloc(sizeof(DWSignalHandler));

    new->message = message;
    new->window = window;
    new->id = msgid;
    new->signalfunction = signalfunction;
    new->discfunction = discfunc;
    new->data = data;
    new->next = NULL;

    if (!DWRoot)
        DWRoot = new;
    else
    {
        DWSignalHandler *prev = NULL, *tmp = DWRoot;
        while(tmp)
        {
            if(tmp->message == message &&
               tmp->window == window &&
               tmp->id == msgid &&
               tmp->signalfunction == signalfunction)
            {
                tmp->data = data;
                free(new);
                return;
            }
            prev = tmp;
            tmp = tmp->next;
        }
        if(prev)
            prev->next = new;
        else
            DWRoot = new;
    }
}

/* Finds the message number for a given signal name */
ULONG _dw_findsigmessage(const char *signame)
{
    int z;

    for(z=0;z<SIGNALMAX;z++)
    {
        if(strcasecmp(signame, DWSignalTranslate[z].name) == 0)
            return DWSignalTranslate[z].message;
    }
    return 0L;
}

unsigned long _dw_foreground = 0xAAAAAA, _dw_background = 0;

void _dw_handle_resize_events(Box *thisbox)
{
    int z;

    for(z=0;z<thisbox->count;z++)
    {
        id handle = thisbox->items[z].hwnd;

        if(thisbox->items[z].type == _DW_TYPE_BOX)
        {
            Box *tmp = (Box *)[handle box];

            if(tmp)
            {
                _dw_handle_resize_events(tmp);
            }
        }
        else
        {
            if([handle isMemberOfClass:[DWRender class]])
            {
                DWRender *render = (DWRender *)handle;
                NSSize oldsize = [render size];
                NSSize newsize = [render frame].size;

                /* The 10.14 appkit warns this property does nothing... not sure
                 * how long that has been the case but just removing it for Mojave.
                 */
#ifndef BUILDING_FOR_MOJAVE
                NSWindow *window = [render window];

                if([window preferredBackingLocation] != NSWindowBackingLocationVideoMemory)
                {
                    [window setPreferredBackingLocation:NSWindowBackingLocationVideoMemory];
                }
#endif

                /* Eliminate duplicate configure requests */
                if(oldsize.width != newsize.width || oldsize.height != newsize.height)
                {
                    if(newsize.width > 0 && newsize.height > 0)
                    {
                        [render setSize:newsize];
                        _dw_event_handler(handle, nil, _DW_EVENT_CONFIGURE);
                    }
                }
            }
            /* Special handling for notebook controls */
            else if([handle isMemberOfClass:[DWNotebook class]])
            {
                DWNotebook *notebook = (DWNotebook *)handle;
                DWNotebookPage *notepage = (DWNotebookPage *)[notebook selectedTabViewItem];
                id view = [notepage view];

                if([view isMemberOfClass:[DWBox class]])
                {
                    Box *box = (Box *)[view box];
                    _dw_handle_resize_events(box);
                }
            }
            /* Handle laying out scrollviews... if required space is less
             * than available space, then expand.  Otherwise use required space.
             */
            else if([handle isMemberOfClass:[DWScrollBox class]])
            {
                DWScrollBox *scrollbox = (DWScrollBox *)handle;
                DWBox *contentbox = [scrollbox documentView];
                Box *thisbox = [contentbox box];

                /* Get the required space for the box */
                _dw_handle_resize_events(thisbox);
            }
        }
    }
}

/* This function calculates how much space the widgets and boxes require
 * and does expansion as necessary.
 */
static void _dw_resize_box(Box *thisbox, int *depth, int x, int y, int pass)
{
    /* Current item position */
    int z, currentx = thisbox->pad, currenty = thisbox->pad;
    /* Used x, y and padding maximum values...
     * These will be used to find the widest or
     * tallest items in a box.
     */
    int uymax = 0, uxmax = 0;
    int upymax = 0, upxmax = 0;

    /* Reset the box sizes */
    thisbox->minwidth = thisbox->minheight = thisbox->usedpadx = thisbox->usedpady = thisbox->pad * 2;

    /* Handle special groupbox case */
    if(thisbox->grouphwnd)
    {
        /* Only calculate the size on the first pass...
         * use the cached values on second.
         */
        if(pass == 1)
        {
            DWGroupBox *groupbox = thisbox->grouphwnd;
            NSSize borderSize = [groupbox borderSize];
            NSRect titleRect;

            if(borderSize.width == 0 || borderSize.height == 0)
            {
                borderSize = [groupbox initBorder];
            }
            /* Get the title size for a more accurate groupbox padding size */
            titleRect = [groupbox titleRect];

            thisbox->grouppadx = borderSize.width;
            thisbox->grouppady = borderSize.height + titleRect.size.height;
        }

        thisbox->minwidth += thisbox->grouppadx;
        thisbox->usedpadx += thisbox->grouppadx;
        thisbox->minheight += thisbox->grouppady;
        thisbox->usedpady += thisbox->grouppady;
    }

    /* Count up all the space for all items in the box */
    for(z=0;z<thisbox->count;z++)
    {
        int itempad, itemwidth, itemheight;

        if(thisbox->items[z].type == _DW_TYPE_BOX)
        {
            id box = thisbox->items[z].hwnd;
            Box *tmp = (Box *)[box box];

            if(tmp)
            {
                /* On the first pass calculate the box contents */
                if(pass == 1)
                {
                    (*depth)++;

                    /* Save the newly calculated values on the box */
                    _dw_resize_box(tmp, depth, x, y, pass);

                    /* Duplicate the values in the item list for use below */
                    thisbox->items[z].width = tmp->minwidth;
                    thisbox->items[z].height = tmp->minheight;

                    /* If the box has no contents but is expandable... default the size to 1 */
                    if(!thisbox->items[z].width && thisbox->items[z].hsize)
                       thisbox->items[z].width = 1;
                    if(!thisbox->items[z].height && thisbox->items[z].vsize)
                       thisbox->items[z].height = 1;

                    (*depth)--;
                }
            }
        }

        /* Precalculate these values, since they will
         * be used used repeatedly in the next section.
         */
        itempad = thisbox->items[z].pad * 2;
        itemwidth = thisbox->items[z].width + itempad;
        itemheight = thisbox->items[z].height + itempad;

        /* Calculate the totals and maximums */
        if(thisbox->type == DW_VERT)
        {
            if(itemwidth > uxmax)
                uxmax = itemwidth;

            if(thisbox->items[z].hsize != _DW_SIZE_EXPAND)
            {
                if(itemwidth > upxmax)
                    upxmax = itemwidth;
            }
            else
            {
                if(itempad > upxmax)
                    upxmax = itempad;
            }
            thisbox->minheight += itemheight;
            if(thisbox->items[z].vsize != _DW_SIZE_EXPAND)
                thisbox->usedpady += itemheight;
            else
                thisbox->usedpady += itempad;
        }
        else
        {
            if(itemheight > uymax)
                uymax = itemheight;
            if(thisbox->items[z].vsize != _DW_SIZE_EXPAND)
            {
                if(itemheight > upymax)
                    upymax = itemheight;
            }
            else
            {
                if(itempad > upymax)
                    upymax = itempad;
            }
            thisbox->minwidth += itemwidth;
            if(thisbox->items[z].hsize != _DW_SIZE_EXPAND)
                thisbox->usedpadx += itemwidth;
            else
                thisbox->usedpadx += itempad;
        }
    }

    /* Add the maximums which were calculated in the previous loop */
    thisbox->minwidth += uxmax;
    thisbox->minheight += uymax;
    thisbox->usedpadx += upxmax;
    thisbox->usedpady += upymax;

    /* The second pass is for actual placement. */
    if(pass > 1)
    {
        for(z=0;z<(thisbox->count);z++)
        {
            int height = thisbox->items[z].height;
            int width = thisbox->items[z].width;
            int itempad = thisbox->items[z].pad * 2;
            int thispad = thisbox->pad * 2;

            /* Calculate the new sizes */
            if(thisbox->items[z].hsize == _DW_SIZE_EXPAND)
            {
                if(thisbox->type == DW_HORZ)
                {
                    int expandablex = thisbox->minwidth - thisbox->usedpadx;

                    if(expandablex)
                        width = (int)(((float)width / (float)expandablex) * (float)(x - thisbox->usedpadx));
                }
                else
                    width = x - (itempad + thispad + thisbox->grouppadx);
            }
            if(thisbox->items[z].vsize == _DW_SIZE_EXPAND)
            {
                if(thisbox->type == DW_VERT)
                {
                    int expandabley = thisbox->minheight - thisbox->usedpady;

                    if(expandabley)
                        height = (int)(((float)height / (float)expandabley) * (float)(y - thisbox->usedpady));
                }
                else
                    height = y - (itempad + thispad + thisbox->grouppady);
            }

            /* If the calculated size is valid... */
            if(height > 0 && width > 0)
            {
                int pad = thisbox->items[z].pad;
                NSView *handle = thisbox->items[z].hwnd;
                NSPoint point;
                NSSize size;

                point.x = currentx + pad;
                point.y = currenty + pad;
                size.width = width;
                size.height = height;
                [handle setFrameOrigin:point];
                [handle setFrameSize:size];

                /* After placing a box... place its components */
                if(thisbox->items[z].type == _DW_TYPE_BOX)
                {
                    id box = thisbox->items[z].hwnd;
                    Box *tmp = (Box *)[box box];

                    if(tmp)
                    {
                        (*depth)++;
                        _dw_resize_box(tmp, depth, width, height, pass);
                        (*depth)--;
                    }
                }

                /* Special handling for notebook controls */
                if([handle isMemberOfClass:[DWNotebook class]])
                {
                    DWNotebook *notebook = (DWNotebook *)handle;
                    DWNotebookPage *notepage = (DWNotebookPage *)[notebook selectedTabViewItem];
                    id view = [notepage view];

                    if([view isMemberOfClass:[DWBox class]])
                    {
                        Box *box = (Box *)[view box];
                        NSSize size = [view frame].size;
                        _dw_do_resize(box, size.width, size.height);
                        _dw_handle_resize_events(box);
                    }
                }
                /* Handle laying out scrollviews... if required space is less
                 * than available space, then expand.  Otherwise use required space.
                 */
                else if([handle isMemberOfClass:[DWScrollBox class]])
                {
                    int depth = 0;
                    DWScrollBox *scrollbox = (DWScrollBox *)handle;
                    DWBox *contentbox = [scrollbox documentView];
                    Box *thisbox = [contentbox box];
                    NSSize contentsize = [scrollbox contentSize];

                    /* Get the required space for the box */
                    _dw_resize_box(thisbox, &depth, x, y, 1);

                    if(contentsize.width < thisbox->minwidth)
                    {
                        contentsize.width = thisbox->minwidth;
                    }
                    if(contentsize.height < thisbox->minheight)
                    {
                        contentsize.height = thisbox->minheight;
                    }
                    [contentbox setFrameSize:contentsize];

                    /* Layout the content of the scrollbox */
                    _dw_do_resize(thisbox, contentsize.width, contentsize.height);
                    _dw_handle_resize_events(thisbox);
                }
                /* Special handling for spinbutton controls */
                else if([handle isMemberOfClass:[DWSpinButton class]])
                {
                    DWSpinButton *spinbutton = (DWSpinButton *)handle;
                    NSTextField *textfield = [spinbutton textfield];
                    NSStepper *stepper = [spinbutton stepper];
                    [textfield setFrameOrigin:NSMakePoint(0,0)];
                    [textfield setFrameSize:NSMakeSize(size.width-20,size.height)];
                    [stepper setFrameOrigin:NSMakePoint(size.width-20,0)];
                    [stepper setFrameSize:NSMakeSize(20,size.height)];
                }
                else if([handle isMemberOfClass:[DWSplitBar class]])
                {
                    DWSplitBar *split = (DWSplitBar *)handle;
                    DWWindow *window = (DWWindow *)[split window];
                    float percent = [split percent];

                    if(percent > 0 && size.width > 20 && size.height > 20)
                    {
                        dw_splitbar_set(handle, percent);
                        [split setPercent:0];
                    }
                    else if([window redraw])
                    {
                        [split splitViewDidResizeSubviews:nil];
                    }
                }

                /* Advance the current position in the box */
                if(thisbox->type == DW_HORZ)
                    currentx += width + (pad * 2);
                if(thisbox->type == DW_VERT)
                    currenty += height + (pad * 2);
            }
        }
    }
}

static void _dw_do_resize(Box *thisbox, int x, int y)
{
    if(x > 0 && y > 0)
    {
        if(thisbox)
        {
            int depth = 0;

            /* Calculate space requirements */
            _dw_resize_box(thisbox, &depth, x, y, 1);

            /* Finally place all the boxes and controls */
            _dw_resize_box(thisbox, &depth, x, y, 2);
        }
    }
}

NSMenu *_dw_generate_main_menu()
{
    NSString *applicationName = nil;

    /* This only works on 10.6 so we have a backup method */
#ifdef BUILDING_FOR_SNOW_LEOPARD
    applicationName = [[NSRunningApplication currentApplication] localizedName];
#endif
    if(applicationName == nil)
    {
        applicationName = [[NSProcessInfo processInfo] processName];
    }

    /* Create the main menu */
    NSMenu * mainMenu = [[[NSMenu alloc] initWithTitle:@"MainMenu"] autorelease];

    NSMenuItem * mitem = [mainMenu addItemWithTitle:@"Apple" action:NULL keyEquivalent:@""];
    NSMenu * menu = [[[NSMenu alloc] initWithTitle:@"Apple"] autorelease];

    [DWApp performSelector:@selector(setAppleMenu:) withObject:menu];

    /* Setup the Application menu */
    NSMenuItem * item = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"About", nil), applicationName]
                                        action:@selector(orderFrontStandardAboutPanel:)
                                 keyEquivalent:@""];
    [item setTarget:DWApp];

    [menu addItem:[NSMenuItem separatorItem]];

    item = [menu addItemWithTitle:NSLocalizedString(@"Services", nil)
                           action:NULL
                    keyEquivalent:@""];
    NSMenu * servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease];
    [menu setSubmenu:servicesMenu forItem:item];
    [DWApp setServicesMenu:servicesMenu];

    [menu addItem:[NSMenuItem separatorItem]];

    item = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Hide", nil), applicationName]
                           action:@selector(hide:)
                    keyEquivalent:@"h"];
    [item setTarget:DWApp];

    item = [menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil)
                           action:@selector(hideOtherApplications:)
                    keyEquivalent:@"h"];
    [item setKeyEquivalentModifierMask:DWEventModifierFlagCommand | DWEventModifierFlagOption];
    [item setTarget:DWApp];

    item = [menu addItemWithTitle:NSLocalizedString(@"Show All", nil)
                           action:@selector(unhideAllApplications:)
                    keyEquivalent:@""];
    [item setTarget:DWApp];

    [menu addItem:[NSMenuItem separatorItem]];

    item = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Quit", nil), applicationName]
                           action:@selector(terminate:)
                    keyEquivalent:@"q"];
    [item setTarget:DWApp];

    [mainMenu setSubmenu:menu forItem:mitem];

    return mainMenu;
}

/*
 * Runs a message loop for Dynamic Windows.
 */
void API dw_main(void)
{
#ifndef BUILDING_FOR_MOJAVE
    dw_mutex_lock(DWRunMutex);
#endif
    DWThread = dw_thread_id();
    /* Make sure any queued redraws are handled */
    _dw_redraw(0, FALSE);
    [DWApp run];
    DWThread = (DWTID)-1;
#ifndef BUILDING_FOR_MOJAVE
    dw_mutex_unlock(DWRunMutex);
#endif
}

/*
 * Causes running dw_main() to return.
 */
void API dw_main_quit(void)
{
    [DWApp stop:nil];
    _dw_wakeup_app();
}

/*
 * Runs a message loop for Dynamic Windows, for a period of milliseconds.
 * Parameters:
 *           milliseconds: Number of milliseconds to run the loop for.
 */
void API dw_main_sleep(int milliseconds)
{
    DWTID curr = pthread_self();

    if(DWThread == (DWTID)-1 || DWThread == curr)
    {
        DWTID orig = DWThread;
        NSDate *until = [NSDate dateWithTimeIntervalSinceNow:(milliseconds/1000.0)];

        if(orig == (DWTID)-1)
        {
#ifndef BUILDING_FOR_MOJAVE
            dw_mutex_lock(DWRunMutex);
#endif
            DWThread = curr;
        }
       /* Process any pending events */
        while(_dw_main_iteration(until))
        {
            /* Just loop */
        }
        if(orig == (DWTID)-1)
        {
            DWThread = orig;
#ifndef BUILDING_FOR_MOJAVE
            dw_mutex_unlock(DWRunMutex);
#endif
        }
    }
    else
    {
        usleep(milliseconds * 1000);
    }
}

/* Internal version that doesn't lock the run mutex */
int _dw_main_iteration(NSDate *date)
{
    NSEvent *event = [DWApp nextEventMatchingMask:DWEventMaskAny
                                        untilDate:date
                                           inMode:NSDefaultRunLoopMode
                                          dequeue:YES];
    if(event)
    {
        [DWApp sendEvent:event];
        [DWApp updateWindows];
        return 1;
    }
    return 0;
}

/*
 * Processes a single message iteration and returns.
 */
void API dw_main_iteration(void)
{
    DWTID curr = pthread_self();

    if(DWThread == (DWTID)-1)
    {
#ifndef BUILDING_FOR_MOJAVE
        dw_mutex_lock(DWRunMutex);
#endif
        DWThread = curr;
        _dw_main_iteration([NSDate distantPast]);
        DWThread = (DWTID)-1;
#ifndef BUILDING_FOR_MOJAVE
        dw_mutex_unlock(DWRunMutex);
#endif
    }
    else if(DWThread == curr)
    {
        _dw_main_iteration([NSDate distantPast]);
    }
}

/*
 * Cleanly terminates a DW session, should be signal handler safe.
 */
void API dw_shutdown(void)
{
#if !defined(GARBAGE_COLLECT)
    NSAutoreleasePool *pool = pthread_getspecific(_dw_pool_key);
    [pool drain];
#endif
}

/*
 * Cleanly terminates a DW session, should be signal handler safe.
 * Parameters:
 *       exitcode: Exit code reported to the operating system.
 */
void API dw_exit(int exitcode)
{
    dw_shutdown();
    exit(exitcode);
}

/*
 * Free's memory allocated by dynamic windows.
 * Parameters:
 *           ptr: Pointer to dynamic windows allocated
 *                memory to be free()'d.
 */
void API dw_free(void *ptr)
{
   free(ptr);
}

/*
 * Returns a pointer to a static buffer which containes the
 * current user directory.  Or the root directory (C:\ on
 * OS/2 and Windows).
 */
char *dw_user_dir(void)
{
    static char _user_dir[PATH_MAX+1] = { 0 };

    if(!_user_dir[0])
    {
        char *home = getenv("HOME");

        if(home)
            strncpy(_user_dir, home, PATH_MAX);
        else
            strcpy(_user_dir, "/");
    }
    return _user_dir;
}

/*
 * Returns a pointer to a static buffer which containes the
 * private application data directory.
 */
char * API dw_app_dir(void)
{
    return _dw_bundle_path;
}

/*
 * Sets the application ID used by this Dynamic Windows application instance.
 * Parameters:
 *         appid: A string typically in the form: com.company.division.application
 *         appname: The application name used on Windows or NULL.
 * Returns:
 *         DW_ERROR_NONE after successfully setting the application ID.
 *         DW_ERROR_UNKNOWN if unsupported on this system.
 *         DW_ERROR_GENERAL if the application ID is not allowed.
 * Remarks:
 *          This must be called before dw_init().  If dw_init() is called first
 *          it will create a unique ID in the form: org.dbsoft.dwindows.application
 *          or if the application name cannot be detected: org.dbsoft.dwindows.pid.#
 *          The appname is only required on Windows.  If NULL is passed the detected
 *          application name will be used, but a prettier name may be desired.
 */
int dw_app_id_set(const char *appid, const char *appname)
{
    strncpy(_dw_app_id, appid, _DW_APP_ID_SIZE);
    return DW_ERROR_NONE;
}

/*
 * Displays a debug message on the console...
 * Parameters:
 *           format: printf style format string.
 *           ...: Additional variables for use in the format.
 */
void API dw_debug(const char *format, ...)
{
   va_list args;

   va_start(args, format);
   dw_vdebug(format, args);
   va_end(args);
}

void API dw_vdebug(const char *format, va_list args)
{
   NSString *nformat = [[NSString stringWithUTF8String:format] autorelease];

   NSLogv(nformat, args);
}

/*
 * Displays a Message Box with given text and title..
 * Parameters:
 *           title: The title of the message box.
 *           flags: flags to indicate buttons and icon
 *           format: printf style format string.
 *           ...: Additional variables for use in the format.
 */
int API dw_messagebox(const char *title, int flags, const char *format, ...)
{
   va_list args;
   int rc;

   va_start(args, format);
   rc = dw_vmessagebox(title, flags, format, args);
   va_end(args);
   return rc;
}

int API dw_vmessagebox(const char *title, int flags, const char *format, va_list args)
{
    NSInteger iResponse;
    NSString *button1 = @"OK";
    NSString *button2 = nil;
    NSString *button3 = nil;
    NSString *mtitle = [NSString stringWithUTF8String:title];
    NSString *mtext;
    NSAlertStyle mstyle = DWAlertStyleWarning;
    NSArray *params;

    if(flags & DW_MB_OKCANCEL)
    {
        button2 = @"Cancel";
    }
    else if(flags & DW_MB_YESNO)
    {
        button1 = @"Yes";
        button2 = @"No";
    }
    else if(flags & DW_MB_YESNOCANCEL)
    {
        button1 = @"Yes";
        button2 = @"No";
        button3 = @"Cancel";
    }

    mtext = [[[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:args] autorelease];

    if(flags & DW_MB_ERROR)
        mstyle = DWAlertStyleCritical;
    else if(flags & DW_MB_INFORMATION)
        mstyle = DWAlertStyleInformational;

    params = [NSMutableArray arrayWithObjects:mtitle, mtext, [NSNumber numberWithInteger:mstyle], button1, button2, button3, nil];
    [DWObj safeCall:@selector(messageBox:) withObject:params];
    iResponse = [[params lastObject] integerValue];

    switch(iResponse)
    {
        case NSAlertFirstButtonReturn:    /* user pressed OK */
            if(flags & DW_MB_YESNO || flags & DW_MB_YESNOCANCEL)
            {
                return DW_MB_RETURN_YES;
            }
            return DW_MB_RETURN_OK;
        case NSAlertSecondButtonReturn:  /* user pressed Cancel */
            if(flags & DW_MB_OKCANCEL)
            {
                return DW_MB_RETURN_CANCEL;
            }
            return DW_MB_RETURN_NO;
        case NSAlertThirdButtonReturn:      /* user pressed the third button */
            return DW_MB_RETURN_CANCEL;
    }
    return 0;
}

/*
 * Opens a file dialog and queries user selection.
 * Parameters:
 *       title: Title bar text for dialog.
 *       defpath: The default path of the open dialog.
 *       ext: Default file extention.
 *       flags: DW_FILE_OPEN or DW_FILE_SAVE.
 * Returns:
 *       NULL on error. A malloced buffer containing
 *       the file path on success.
 *
 */
char * API dw_file_browse(const char *title, const char *defpath, const char *ext, int flags)
{
    char temp[PATH_MAX+1];
    char *file = NULL, *path = NULL;
    DW_LOCAL_POOL_IN;

    /* Figure out path information...
     * These functions are only support in Snow Leopard and later...
     */
    if(defpath && *defpath && (DWOSMinor > 5 || DWOSMajor > 10))
    {
        struct stat buf;

        /* Get an absolute path */
        if(!realpath(defpath, temp))
            strcpy(temp, defpath);

        /* Check if the defpath exists */
        if(stat(temp, &buf) == 0)
        {
            /* Can be a directory or file */
            if(buf.st_mode & S_IFDIR)
                path = temp;
            else
                file = temp;
        }
        /* If it wasn't a directory... check if there is a path */
        if(!path && strchr(temp, '/'))
        {
            unsigned long x = strlen(temp) - 1;

            /* Trim off the filename */
            while(x > 0 && temp[x] != '/')
            {
                x--;
            }
            if(temp[x] == '/')
            {
                temp[x] = 0;
                /* Check to make sure the trimmed piece is a directory */
                if(stat(temp, &buf) == 0)
                {
                    if(buf.st_mode & S_IFDIR)
                    {
                        /* We now have it split */
                        path = temp;
                        file = &temp[x+1];
                    }
                }
            }
        }
    }

    if(flags == DW_FILE_OPEN || flags == DW_DIRECTORY_OPEN)
    {
        /* Create the File Open Dialog class. */
        NSOpenPanel* openDlg = [NSOpenPanel openPanel];

        if(path)
        {
            SEL ssdu = NSSelectorFromString(@"setDirectoryURL");

            if([openDlg respondsToSelector:ssdu])
            {
                DWIMP isdu = (DWIMP)[openDlg methodForSelector:ssdu];
                isdu(openDlg, ssdu, [NSURL fileURLWithPath:[NSString stringWithUTF8String:path]]);
            }
        }

        /* Enable the selection of files in the dialog. */
        if(flags == DW_FILE_OPEN)
        {
            [openDlg setCanChooseFiles:YES];
            [openDlg setCanChooseDirectories:NO];

            /* Handle file types */
            if(ext && *ext)
            {
#ifdef BUILDING_FOR_BIG_SUR
                if (@available(macOS 11.0, *)) {
                    UTType *extuti = ext ? [UTType typeWithFilenameExtension:[NSString stringWithUTF8String:ext]] : nil;
                    NSArray *UTIs;
                    
                    /* Try to generate a UTI for our passed extension */
                    if(extuti)
                        UTIs = [NSArray arrayWithObjects:extuti, UTTypeText, nil];
                    else
                        UTIs = @[UTTypeText];
                    [openDlg setAllowedContentTypes:UTIs];
                } else
#endif
                {
                    _DW_ELSE_AVAILABLE
                    NSArray* fileTypes = [[[NSArray alloc] initWithObjects:[NSString stringWithUTF8String:ext], nil] autorelease];
                    [openDlg setAllowedFileTypes:fileTypes];
                    _DW_END_AVAILABLE
                }
            }
        }
        else
        {
            [openDlg setCanChooseFiles:NO];
            [openDlg setCanChooseDirectories:YES];
#ifdef BUILDING_FOR_BIG_SUR
            if (@available(macOS 11.0, *)) {
                [openDlg setAllowedContentTypes:@[UTTypeFolder]];
            }
#endif
        }

        /* Disable multiple selection */
        [openDlg setAllowsMultipleSelection:NO];

        /* Display the dialog.  If the OK button was pressed,
         * process the files.
         */
        if([openDlg runModal] == DWModalResponseOK)
        {
            /* Get an array containing the full filenames of all
             * files and directories selected.
             */
            NSArray *files = [openDlg URLs];
            NSString *fileName = [[files objectAtIndex:0] path];
            if(fileName)
            {
                char *ret = strdup([ fileName UTF8String ]);
                DW_LOCAL_POOL_OUT;
                return ret;
            }
        }
    }
    else
    {
        /* Create the File Save Dialog class. */
        NSSavePanel* saveDlg = [NSSavePanel savePanel];

        if(path)
        {
            SEL ssdu = NSSelectorFromString(@"setDirectoryURL");

            if([saveDlg respondsToSelector:ssdu])
            {
                DWIMP isdu = (DWIMP)[saveDlg methodForSelector:ssdu];
                isdu(saveDlg, ssdu, [NSURL fileURLWithPath:[NSString stringWithUTF8String:path]]);
            }
        }
        if(file)
        {
            SEL ssnfsv = NSSelectorFromString(@"setNameFieldStringValue");

            if([saveDlg respondsToSelector:ssnfsv])
            {
                DWIMP isnfsv = (DWIMP)[saveDlg methodForSelector:ssnfsv];
                isnfsv(saveDlg, ssnfsv, [NSString stringWithUTF8String:file]);
            }
        }

        /* Enable the creation of directories in the dialog. */
        [saveDlg setCanCreateDirectories:YES];

        /* Handle file types */
        if(ext && *ext)
        {
#ifdef BUILDING_FOR_BIG_SUR
            if (@available(macOS 11.0, *)) {
                UTType *extuti = ext ? [UTType typeWithFilenameExtension:[NSString stringWithUTF8String:ext]] : nil;
                NSArray *UTIs;
                
                /* Try to generate a UTI for our passed extension */
                if(extuti)
                    UTIs = [NSArray arrayWithObjects:extuti, UTTypeText, nil];
                else
                    UTIs = @[UTTypeText];
                [saveDlg setAllowedContentTypes:UTIs];
            } else
#endif
            {
                _DW_ELSE_AVAILABLE
                NSArray* fileTypes = [[[NSArray alloc] initWithObjects:[NSString stringWithUTF8String:ext], nil] autorelease];
                [saveDlg setAllowedFileTypes:fileTypes];
                _DW_END_AVAILABLE
            }
        }

        /* Display the dialog.  If the OK button was pressed,
         * process the files.
         */
        if([saveDlg runModal] == DWModalResponseOK)
        {
            /* Get an array containing the full filenames of all
             * files and directories selected.
             */
            NSString* fileName = [[saveDlg URL] path];
            if(fileName)
            {
                char *ret = strdup([ fileName UTF8String ]);
                DW_LOCAL_POOL_OUT;
                return ret;
            }
        }
    }
    DW_LOCAL_POOL_OUT;
    return NULL;
}

/*
 * Gets the contents of the default clipboard as text.
 * Parameters:
 *       None.
 * Returns:
 *       Pointer to an allocated string of text or NULL if clipboard empty or contents could not
 *       be converted to text.
 */
char *dw_clipboard_get_text()
{
    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
    NSString *str = [pasteboard stringForType:DWPasteboardTypeString];
    if(str != nil)
    {
        return strdup([ str UTF8String ]);
    }
    return NULL;
}

/*
 * Sets the contents of the default clipboard to the supplied text.
 * Parameters:
 *       Text.
 */
void dw_clipboard_set_text(const char *str, int len)
{
    NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
    SEL scc = NSSelectorFromString(@"clearContents");

    if([pasteboard respondsToSelector:scc])
    {
        DWIMP icc = (DWIMP)[pasteboard methodForSelector:scc];
        icc(pasteboard, scc);
    }

    [pasteboard setString:[ NSString stringWithUTF8String:str ] forType:DWPasteboardTypeString];
}


/*
 * Allocates and initializes a dialog struct.
 * Parameters:
 *           data: User defined data to be passed to functions.
 */
DWDialog * API dw_dialog_new(void *data)
{
    DWDialog *tmp = malloc(sizeof(DWDialog));

    if(tmp)
    {
        tmp->eve = dw_event_new();
        dw_event_reset(tmp->eve);
        tmp->data = data;
        tmp->done = FALSE;
        tmp->result = NULL;
    }
    return tmp;
}

/*
 * Accepts a dialog struct and returns the given data to the
 * initial called of dw_dialog_wait().
 * Parameters:
 *           dialog: Pointer to a dialog struct aquired by dw_dialog_new).
 *           result: Data to be returned by dw_dialog_wait().
 */
int API dw_dialog_dismiss(DWDialog *dialog, void *result)
{
    dialog->result = result;
    dw_event_post(dialog->eve);
    dialog->done = TRUE;
    return 0;
}

/*
 * Accepts a dialog struct waits for dw_dialog_dismiss() to be
 * called by a signal handler with the given dialog struct.
 * Parameters:
 *           dialog: Pointer to a dialog struct aquired by dw_dialog_new).
 */
void * API dw_dialog_wait(DWDialog *dialog)
{
    void *tmp = NULL;

    if(dialog)
    {
        while(!dialog->done)
        {
            _dw_main_iteration([NSDate dateWithTimeIntervalSinceNow:0.01]);
        }
        dw_event_close(&dialog->eve);
        tmp = dialog->result;
        free(dialog);
    }
    return tmp;
}

/*
 * Create a new Box to be packed.
 * Parameters:
 *       type: Either DW_VERT (vertical) or DW_HORZ (horizontal).
 *       pad: Number of pixels to pad around the box.
 */
DW_FUNCTION_DEFINITION(dw_box_new, HWND, int type, int pad)
DW_FUNCTION_ADD_PARAM2(type, pad)
DW_FUNCTION_RETURN(dw_box_new, HWND)
DW_FUNCTION_RESTORE_PARAM2(type, int, pad, int)
{
    DW_FUNCTION_INIT;
    DWBox *view = [[[DWBox alloc] init] retain];
    Box *newbox = [view box];
    memset(newbox, 0, sizeof(Box));
    newbox->pad = pad;
    newbox->type = type;
    DW_FUNCTION_RETURN_THIS(view);
}

/*
 * Create a new Group Box to be packed.
 * Parameters:
 *       type: Either DW_VERT (vertical) or DW_HORZ (horizontal).
 *       pad: Number of pixels to pad around the box.
 *       title: Text to be displayined in the group outline.
 */
HWND API dw_groupbox_new(int type, int pad, const char *title)
{
    NSBox *groupbox = [[[DWGroupBox alloc] init] retain];
    DWBox *thisbox = dw_box_new(type, pad);
    Box *box = [thisbox box];

#ifndef BUILDING_FOR_CATALINA
    [groupbox setBorderType:NSBezelBorder];
#endif
    [groupbox setTitle:[NSString stringWithUTF8String:title]];
    box->grouphwnd = groupbox;
    [groupbox setContentView:thisbox];
    [thisbox release];
    [thisbox autorelease];
    return groupbox;
}

/*
 * Create a new scrollable Box to be packed.
 * Parameters:
 *       type: Either DW_VERT (vertical) or DW_HORZ (horizontal).
 *       pad: Number of pixels to pad around the box.
 */
HWND API dw_scrollbox_new( int type, int pad )
{
    DWScrollBox *scrollbox = [[[DWScrollBox alloc] init] retain];
    DWBox *box = dw_box_new(type, pad);
    DWBox *tmpbox = dw_box_new(DW_VERT, 0);
    dw_box_pack_start(tmpbox, box, 1, 1, TRUE, TRUE, 0);
    [scrollbox setHasVerticalScroller:YES];
    [scrollbox setHasHorizontalScroller:YES];
    [scrollbox setBorderType:NSNoBorder];
    [scrollbox setDrawsBackground:NO];
    [scrollbox setBox:box];
    [scrollbox setDocumentView:tmpbox];
    [tmpbox release];
    [tmpbox autorelease];
    return scrollbox;
}

/*
 * Returns the position of the scrollbar in the scrollbox
 * Parameters:
 *          handle: Handle to the scrollbox to be queried.
 *          orient: The vertical or horizontal scrollbar.
 */
int API dw_scrollbox_get_pos(HWND handle, int orient)
{
    DWScrollBox *scrollbox = handle;
    NSView *view = [scrollbox documentView];
    NSSize contentsize = [scrollbox contentSize];
    NSScroller *scrollbar;
    int range = 0;
    int val = 0;
    if(orient == DW_VERT)
    {
        scrollbar = [scrollbox verticalScroller];
        range = [view bounds].size.height - contentsize.height;
    }
    else
    {
        scrollbar = [scrollbox horizontalScroller];
        range = [view bounds].size.width - contentsize.width;
    }
    if(range > 0)
    {
        val = [scrollbar floatValue] * range;
    }
    return val;
}

/*
 * Gets the range for the scrollbar in the scrollbox.
 * Parameters:
 *          handle: Handle to the scrollbox to be queried.
 *          orient: The vertical or horizontal scrollbar.
 */
int API dw_scrollbox_get_range(HWND handle, int orient)
{
    DWScrollBox *scrollbox = handle;
    NSView *view = [scrollbox documentView];
    int range = 0;
    if(orient == DW_VERT)
    {
        range = [view bounds].size.height;
    }
    else
    {
        range = [view bounds].size.width;
    }
    return range;
}

/* Return the handle to the text object */
id _dw_text_handle(id object)
{
    if([object isMemberOfClass:[ DWSpinButton class]])
    {
        DWSpinButton *spinbutton = object;
        object = [spinbutton textfield];
    }
    if([object isMemberOfClass:[ NSBox class]])
    {
        NSBox *box = object;
        id content = [box contentView];

        if([content isMemberOfClass:[ DWText class]])
        {
            object = content;
        }
    }
    return object;
}

/* Internal function to calculate the widget's required size..
 * These are the general rules for widget sizes:
 *
 * Render/Unspecified: 1x1
 * Scrolled(Container,Tree,MLE): Guessed size clamped to min and max in dw.h
 * Entryfield/Combobox/Spinbutton: 150x(maxfontheight)
 * Spinbutton: 50x(maxfontheight)
 * Text/Status: (textwidth)x(textheight)
 * Ranged: 100x14 or 14x100 for vertical.
 * Buttons/Bitmaps: Size of text or image and border.
*/
void _dw_control_size(id handle, int *width, int *height)
{
    int thiswidth = 1, thisheight = 1, extrawidth = 0, extraheight = 0;
    NSString *nsstr = nil;
    id object = _dw_text_handle(handle);

    /* Handle all the different button types */
    if([object isKindOfClass:[NSButton class]])
    {
        switch([object buttonType])
        {
            case DWButtonTypeSwitch:
            case DWButtonTypeRadio:
                extrawidth = 24;
                extraheight = 4;
                nsstr = [object title];
                break;
            default:
            {
                NSImage *image = [object image];

                if(image)
                {
                    /* Image button */
                    NSSize size = [image size];
                    thiswidth = (int)size.width;
                    thisheight = (int)size.height;
                    if([object isBordered])
                    {
                        extrawidth = 4;
                        extraheight = 4;
                    }
                }
                else
                {
                    /* Text button */
                    nsstr = [object title];

                    if([object isBordered])
                    {
                        extrawidth = 30;
                        extraheight = 8;
                    }
                    else
                    {
                        extrawidth = 8;
                        extraheight = 4;
                    }
                }
                break;
            }
        }
    }
    /* If the control is an entryfield set width to 150 */
    else if([object isKindOfClass:[NSTextField class]])
    {
        NSFont *font = [object font];

        if([object isEditable])
        {
            /* Spinbuttons don't need to be as wide */
            if([handle isMemberOfClass:[DWSpinButton class]])
                thiswidth = 50;
            else
                thiswidth = 150;
            /* Comboboxes need some extra height for the border */
            if([handle isMemberOfClass:[DWComboBox class]])
                extraheight = 4;
            /* Yosemite and higher requires even more border space */
            if(DWOSMinor > 9 || DWOSMajor > 10)
                extraheight += 4;
        }
        else
            nsstr = [object stringValue];

        if(font)
            thisheight = (int)[font boundingRectForFont].size.height;
    }
    /* Handle the ranged widgets */
    else if([object isMemberOfClass:[DWPercent class]] ||
            [object isMemberOfClass:[DWSlider class]])
    {
        thiswidth = 100;
        thisheight = 20;
    }
    /* Handle the ranged widgets */
    else if([object isMemberOfClass:[DWScrollbar class]])
    {
        if([object vertical])
        {
            thiswidth = 14;
            thisheight = 100;
        }
        else
        {
            thiswidth = 100;
            thisheight = 14;
        }
    }
    /* Handle bitmap size */
    else if([object isMemberOfClass:[NSImageView class]])
    {
        NSImage *image = [object image];

        if(image)
        {
            NSSize size = [image size];
            thiswidth = (int)size.width;
            thisheight = (int)size.height;
        }
    }
    /* Handle calendar */
    else if([object isMemberOfClass:[DWCalendar class]])
    {
        NSCell *cell = [object cell];

        if(cell)
        {
            NSSize size = [cell cellSize];

            thiswidth = size.width;
            thisheight = size.height;
        }
    }
    /* MLE and Container */
    else if([object isMemberOfClass:[DWMLE class]] ||
            [object isMemberOfClass:[DWContainer class]])
    {
        NSSize size;

        if([object isMemberOfClass:[DWMLE class]])
        {
            NSScrollView *sv = [object scrollview];
            NSSize frame = [sv frame].size;
            BOOL hscroll = [sv hasHorizontalScroller];

            /* Make sure word wrap is off for the first part */
            if(!hscroll)
            {
                [[object textContainer] setWidthTracksTextView:NO];
                [[object textContainer] setContainerSize:[object maxSize]];
                [object setHorizontallyResizable:YES];
                [sv setHasHorizontalScroller:YES];
            }
            /* Size the text view to fit */
            [object sizeToFit];
            size = [object bounds].size;
            size.width += 2.0;
            size.height += 2.0;
            /* Re-enable word wrapping if it was on */
            if(!hscroll)
            {
                [[object textContainer] setWidthTracksTextView:YES];
                [sv setHasHorizontalScroller:NO];

                /* If the un wrapped it is beyond the bounds... */
                if(size.width > _DW_SCROLLED_MAX_WIDTH)
                {
                    NSSize max = [object maxSize];

                    /* Set the max size to the limit */
                    [object setMaxSize:NSMakeSize(_DW_SCROLLED_MAX_WIDTH, max.height)];
                    /* Recalculate the size */
                    [object sizeToFit];
                    size = [object bounds].size;
                    size.width += 2.0;
                    size.height += 2.0;
                    [object setMaxSize:max];
                }
            }
            [sv setFrameSize:frame];
            /* Take into account the horizontal scrollbar */
            if(hscroll && size.width > _DW_SCROLLED_MAX_WIDTH)
                size.height += 16.0;
        }
        else
            size = [object getsize];

        thiswidth = size.width;
        thisheight = size.height;

        if(thiswidth < _DW_SCROLLED_MIN_WIDTH)
            thiswidth = _DW_SCROLLED_MIN_WIDTH;
        if(thiswidth > _DW_SCROLLED_MAX_WIDTH)
            thiswidth = _DW_SCROLLED_MAX_WIDTH;
        if(thisheight < _DW_SCROLLED_MIN_HEIGHT)
            thisheight = _DW_SCROLLED_MIN_HEIGHT;
        if(thisheight > _DW_SCROLLED_MAX_HEIGHT)
            thisheight = _DW_SCROLLED_MAX_HEIGHT;
    }
    /* Tree */
    else if([object isMemberOfClass:[DWTree class]])
    {
        thiswidth = (int)((_DW_SCROLLED_MAX_WIDTH + _DW_SCROLLED_MIN_WIDTH)/2);
        thisheight = (int)((_DW_SCROLLED_MAX_HEIGHT + _DW_SCROLLED_MIN_HEIGHT)/2);
    }
    /* Any other control type */
    else if([object isKindOfClass:[NSControl class]])
        nsstr = [object stringValue];

    /* If we have a string...
     * calculate the size with the current font.
     */
    if(nsstr)
    {
        int textwidth, textheight;

        /* If we have an empty string, use "gT" to get the most height for the font */
        dw_font_text_extents_get(object, NULL, [nsstr length] ? (char *)[nsstr UTF8String] : "gT", &textwidth, &textheight);

        if(textheight > thisheight)
            thisheight = textheight;
        if(textwidth > thiswidth)
            thiswidth = textwidth;
    }

    /* Handle static text fields */
    if([object isKindOfClass:[NSTextField class]] && ![object isEditable])
    {
        id border = handle;

        extrawidth = 10;

        /* Handle status bar field */
        if([border isMemberOfClass:[NSBox class]])
        {
            extrawidth += 2;
            extraheight = 8;
        }
    }

    /* Set the requested sizes */
    if(width)
        *width = thiswidth + extrawidth;
    if(height)
        *height = thisheight + extraheight;
}

/* Internal box packing function called by the other 3 functions */
void _dw_box_pack(HWND box, HWND item, int index, int width, int height, int hsize, int vsize, int pad, char *funcname)
{
    id object = box;
    DWBox *view = box;
    DWBox *this = item;
    Box *thisbox;
    int z, x = 0;
    Item *tmpitem, *thisitem;

    /*
     * If you try and pack an item into itself VERY bad things can happen; like at least an
     * infinite loop on GTK! Lets be safe!
     */
    if(box == item)
    {
        dw_messagebox(funcname, DW_MB_OK|DW_MB_ERROR, "Danger! Danger! Will Robinson; box and item are the same!");
        return;
    }

    /* Query the objects */
    if([ object isKindOfClass:[ NSWindow class ] ])
    {
        NSWindow *window = box;
        view = [window contentView];
    }
    else if([ object isMemberOfClass:[ DWScrollBox class ] ])
    {
        DWScrollBox *scrollbox = box;
        view = [scrollbox box];
    }

    thisbox = [view box];
    thisitem = thisbox->items;
    object = item;

    /* Query the objects */
    if([ object isMemberOfClass:[ DWContainer class ] ] ||
       [ object isMemberOfClass:[ DWTree class ] ] ||
       [ object isMemberOfClass:[ DWMLE class ] ])
    {
        this = item = [object scrollview];
    }

    /* Do some sanity bounds checking */
    if(!thisitem)
        thisbox->count = 0;
    if(index < 0)
        index = 0;
    if(index > thisbox->count)
        index = thisbox->count;

    /* Duplicate the existing data */
    tmpitem = calloc(sizeof(Item), (thisbox->count+1));

    for(z=0;z<thisbox->count;z++)
    {
        if(z == index)
            x++;
        tmpitem[x] = thisitem[z];
        x++;
    }

    /* Sanity checks */
    if(vsize && !height)
       height = 1;
    if(hsize && !width)
       width = 1;

    /* Fill in the item data appropriately */
    if([object isKindOfClass:[DWBox class]] || [object isMemberOfClass:[DWGroupBox class]])
       tmpitem[index].type = _DW_TYPE_BOX;
    else
    {
        if(width == 0 && hsize == FALSE)
            dw_messagebox(funcname, DW_MB_OK|DW_MB_ERROR, "Width and expand Horizonal both unset for box: %x item: %x",box,item);
        if(height == 0 && vsize == FALSE)
            dw_messagebox(funcname, DW_MB_OK|DW_MB_ERROR, "Height and expand Vertical both unset for box: %x item: %x",box,item);

        tmpitem[index].type = _DW_TYPE_ITEM;
    }

    tmpitem[index].hwnd = item;
    tmpitem[index].origwidth = tmpitem[index].width = width;
    tmpitem[index].origheight = tmpitem[index].height = height;
    tmpitem[index].pad = pad;
    tmpitem[index].hsize = hsize ? _DW_SIZE_EXPAND : _DW_SIZE_STATIC;
    tmpitem[index].vsize = vsize ? _DW_SIZE_EXPAND : _DW_SIZE_STATIC;

    /* If either of the parameters are -1 ... calculate the size */
    if(width == DW_SIZE_AUTO || height ==  DW_SIZE_AUTO)
        _dw_control_size(object, width == DW_SIZE_AUTO ? &tmpitem[index].width : NULL, height == DW_SIZE_AUTO ? &tmpitem[index].height : NULL);

    thisbox->items = tmpitem;

    /* Update the item count */
    thisbox->count++;

    /* Add the item to the box */
    [view addSubview:this];
    [this release];
    /* Enable autorelease on the item...
     * so it will get destroyed when the parent is.
     */
    [this autorelease];
    /* If we are packing a button... */
    if([this isMemberOfClass:[DWButton class]])
    {
        DWButton *button = (DWButton *)this;

        /* Save the parent box so radio
         * buttons can use it later.
         */
        [button setParent:view];
    }
    /* Queue a redraw on the top-level window */
    _dw_redraw([view window], TRUE);

    /* Free the old data */
    if(thisitem)
       free(thisitem);
}

/*
 * Remove windows (widgets) from the box they are packed into.
 * Parameters:
 *       handle: Window handle of the packed item to be removed.
 * Returns:
 *       DW_ERROR_NONE on success and DW_ERROR_GENERAL on failure.
 */
DW_FUNCTION_DEFINITION(dw_box_unpack, int, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_RETURN(dw_box_unpack, int)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id object = handle;
    int retval = DW_ERROR_NONE;

    if([object isKindOfClass:[NSView class]] || [object isKindOfClass:[NSControl class]])
    {
        DWBox *parent = (DWBox *)[object superview];

        /* Some controls are embedded in scrollviews...
         * so get the parent of the scrollview in that case.
         */
        if(([object isKindOfClass:[NSTableView class]] || [object isMemberOfClass:[DWMLE class]])
           && [parent isMemberOfClass:[NSClipView class]])
        {
            object = [parent superview];
            parent = (DWBox *)[object superview];
        }

        if([parent isKindOfClass:[DWBox class]] || [parent isKindOfClass:[DWGroupBox class]])
        {
            id window = [object window];
            Box *thisbox = [parent box];
            int z, index = -1;
            Item *tmpitem = NULL, *thisitem = thisbox->items;

            if(!thisitem)
                thisbox->count = 0;

            for(z=0;z<thisbox->count;z++)
            {
                if(thisitem[z].hwnd == object)
                    index = z;
            }

            if(index == -1)
                retval = DW_ERROR_GENERAL;
            else
            {
                [object retain];
                [object removeFromSuperview];
                [object retain];

                if(thisbox->count > 1)
                {
                    tmpitem = calloc(sizeof(Item), (thisbox->count-1));

                    /* Copy all but the current entry to the new list */
                    for(z=0;z<index;z++)
                    {
                        tmpitem[z] = thisitem[z];
                    }
                    for(z=index+1;z<thisbox->count;z++)
                    {
                        tmpitem[z-1] = thisitem[z];
                    }
                }

                thisbox->items = tmpitem;
                if(thisitem)
                    free(thisitem);
                if(tmpitem)
                    thisbox->count--;
                else
                    thisbox->count = 0;
                /* Queue a redraw on the top-level window */
                _dw_redraw(window, TRUE);
            }
        }
    }
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Remove windows (widgets) from a box at an arbitrary location.
 * Parameters:
 *       box: Window handle of the box to be removed from.
 *       index: 0 based index of packed items.
 * Returns:
 *       Handle to the removed item on success, 0 on failure or padding.
 */
DW_FUNCTION_DEFINITION(dw_box_unpack_at_index, HWND, HWND box, int index)
DW_FUNCTION_ADD_PARAM2(box, index)
DW_FUNCTION_RETURN(dw_box_unpack_at_index, HWND)
DW_FUNCTION_RESTORE_PARAM2(box, HWND, index, int)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    DWBox *parent = (DWBox *)box;
    id object = nil;

    if([parent isKindOfClass:[DWBox class]] || [parent isKindOfClass:[DWGroupBox class]])
    {
        id window = [parent window];
        Box *thisbox = [parent box];

        if(thisbox && index > -1 && index < thisbox->count)
        {
            int z;
            Item *tmpitem = NULL, *thisitem = thisbox->items;

            object = thisitem[index].hwnd;

            if(object)
            {
                [object retain];
                [object removeFromSuperview];
                [object retain];
            }

            if(thisbox->count > 1)
            {
                tmpitem = calloc(sizeof(Item), (thisbox->count-1));

                /* Copy all but the current entry to the new list */
                for(z=0;thisitem && z<index;z++)
                {
                    tmpitem[z] = thisitem[z];
                }
                for(z=index+1;z<thisbox->count;z++)
                {
                    tmpitem[z-1] = thisitem[z];
                }
            }

            thisbox->items = tmpitem;
            if(thisitem)
                free(thisitem);
            if(tmpitem)
                thisbox->count--;
            else
                thisbox->count = 0;

            /* Queue a redraw on the top-level window */
            _dw_redraw(window, TRUE);
        }
    }
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_THIS(object);
}

/*
 * Pack windows (widgets) into a box at an arbitrary location.
 * Parameters:
 *       box: Window handle of the box to be packed into.
 *       item: Window handle of the item to pack.
 *       index: 0 based index of packed items.
 *       width: Width in pixels of the item or -1 to be self determined.
 *       height: Height in pixels of the item or -1 to be self determined.
 *       hsize: TRUE if the window (widget) should expand horizontally to fill space given.
 *       vsize: TRUE if the window (widget) should expand vertically to fill space given.
 *       pad: Number of pixels of padding around the item.
 */
void API dw_box_pack_at_index(HWND box, HWND item, int index, int width, int height, int hsize, int vsize, int pad)
{
    _dw_box_pack(box, item, index, width, height, hsize, vsize, pad, "dw_box_pack_at_index()");
}

/*
 * Pack windows (widgets) into a box from the start (or top).
 * Parameters:
 *       box: Window handle of the box to be packed into.
 *       item: Window handle of the item to pack.
 *       width: Width in pixels of the item or -1 to be self determined.
 *       height: Height in pixels of the item or -1 to be self determined.
 *       hsize: TRUE if the window (widget) should expand horizontally to fill space given.
 *       vsize: TRUE if the window (widget) should expand vertically to fill space given.
 *       pad: Number of pixels of padding around the item.
 */
void API dw_box_pack_start(HWND box, HWND item, int width, int height, int hsize, int vsize, int pad)
{
    /* 65536 is the table limit on GTK...
     * seems like a high enough value we will never hit it here either.
     */
    _dw_box_pack(box, item, 65536, width, height, hsize, vsize, pad, "dw_box_pack_start()");
}

/*
 * Pack windows (widgets) into a box from the end (or bottom).
 * Parameters:
 *       box: Window handle of the box to be packed into.
 *       item: Window handle of the item to pack.
 *       width: Width in pixels of the item or -1 to be self determined.
 *       height: Height in pixels of the item or -1 to be self determined.
 *       hsize: TRUE if the window (widget) should expand horizontally to fill space given.
 *       vsize: TRUE if the window (widget) should expand vertically to fill space given.
 *       pad: Number of pixels of padding around the item.
 */
void API dw_box_pack_end(HWND box, HWND item, int width, int height, int hsize, int vsize, int pad)
{
    _dw_box_pack(box, item, 0, width, height, hsize, vsize, pad, "dw_box_pack_end()");
}

/* Internal function to create a basic button, used by all button types */
HWND _dw_button_new(const char *text, ULONG cid)
{
    DWButton *button = [[[DWButton alloc] init] retain];
    if(text)
    {
        [button setTitle:[ NSString stringWithUTF8String:text ]];
    }
    [button setTarget:button];
    [button setAction:@selector(buttonClicked:)];
    [button setTag:cid];
    [button setButtonType:DWButtonTypeMomentaryPushIn];
    [button setBezelStyle:DWBezelStyleRegularSquare];
    /* TODO: Reenable scaling in the future if it is possible on other platforms.
    [[button cell] setImageScaling:NSImageScaleProportionallyDown]; */
    if(DWDefaultFont)
    {
        [[button cell] setFont:DWDefaultFont];
    }
    return button;
}

/*
 * Create a new button window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_button_new(const char *text, ULONG cid)
{
    DWButton *button = _dw_button_new(text, cid);
    [button setButtonType:DWButtonTypeMomentaryPushIn];
    [button setBezelStyle:DWBezelStyleRounded];
    [button setImagePosition:NSNoImage];
    [button setAlignment:DWTextAlignmentCenter];
    [[button cell] setControlTint:NSBlueControlTint];
    return button;
}

/*
 * Create a new Entryfield window (widget) to be packed.
 * Parameters:
 *       text: The default text to be in the entryfield widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_entryfield_new(const char *text, ULONG cid)
{
    DWEntryField *entry = [[[DWEntryField alloc] init] retain];
    [entry setStringValue:[ NSString stringWithUTF8String:text ]];
    [entry setTag:cid];
    [[entry cell] setScrollable:YES];
    [[entry cell] setWraps:NO];
    return entry;
}

/*
 * Create a new Entryfield (password) window (widget) to be packed.
 * Parameters:
 *       text: The default text to be in the entryfield widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_entryfield_password_new(const char *text, ULONG cid)
{
    DWEntryFieldPassword *entry = [[[DWEntryFieldPassword alloc] init] retain];
    [entry setStringValue:[ NSString stringWithUTF8String:text ]];
    [entry setTag:cid];
    [[entry cell] setScrollable:YES];
    [[entry cell] setWraps:NO];
    return entry;
}

/*
 * Sets the entryfield character limit.
 * Parameters:
 *          handle: Handle to the spinbutton to be set.
 *          limit: Number of characters the entryfield will take.
 */
void API dw_entryfield_set_limit(HWND handle, ULONG limit)
{
    DWEntryField *entry = handle;
    DWEntryFieldFormatter *formatter = [[[DWEntryFieldFormatter alloc] init] autorelease];

    [formatter setMaximumLength:(int)limit];
    [[entry cell] setFormatter:formatter];
}

/*
 * Create a new bitmap button window (widget) to be packed.
 * Parameters:
 *       text: Bubble help text to be displayed.
 *       id: An ID of a bitmap in the resource file.
 */
HWND API dw_bitmapbutton_new(const char *text, ULONG resid)
{
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *respath = [bundle resourcePath];
    NSString *filepath = [respath stringByAppendingFormat:@"/%lu.png", resid];
    NSImage *image = [[NSImage alloc] initWithContentsOfFile:filepath];
    DWButton *button = _dw_button_new("", resid);
    if(image)
    {
        [button setImage:image];
    }
    if(text)
        [button setToolTip:[NSString stringWithUTF8String:text]];
    [image release];
    return button;
}

/*
 * Create a new bitmap button window (widget) to be packed from a file.
 * Parameters:
 *       text: Bubble help text to be displayed.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 *       filename: Name of the file, omit extention to have
 *                 DW pick the appropriate file extension.
 *                 (BMP on OS/2 or Windows, XPM on Unix)
 */
HWND API dw_bitmapbutton_new_from_file(const char *text, unsigned long cid, const char *filename)
{
    char *ext = _dw_get_image_extension(filename);

    NSString *nstr = [ NSString stringWithUTF8String:filename ];
    NSImage *image = [[NSImage alloc] initWithContentsOfFile:nstr];

    if(!image && ext)
    {
        nstr = [nstr stringByAppendingString: [NSString stringWithUTF8String:ext]];
        image = [[NSImage alloc] initWithContentsOfFile:nstr];
    }
    DWButton *button = _dw_button_new("", cid);
    if(image)
    {
        [button setImage:image];
    }
    if(text)
        [button setToolTip:[NSString stringWithUTF8String:text]];
    [image release];
    return button;
}

/*
 * Create a new bitmap button window (widget) to be packed from data.
 * Parameters:
 *       text: Bubble help text to be displayed.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 *       data: The contents of the image
 *            (BMP or ICO on OS/2 or Windows, XPM on Unix)
 *       len: length of str
 */
HWND API dw_bitmapbutton_new_from_data(const char *text, unsigned long cid, const char *data, int len)
{
    NSData *thisdata = [NSData dataWithBytes:data length:len];
    NSImage *image = [[NSImage alloc] initWithData:thisdata];
    DWButton *button = _dw_button_new("", cid);
    if(image)
    {
        [button setImage:image];
    }
    if(text)
        [button setToolTip:[NSString stringWithUTF8String:text]];
    [image release];
    return button;
}

/*
 * Create a new spinbutton window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_spinbutton_new(const char *text, ULONG cid)
{
    DWSpinButton *spinbutton = [[[DWSpinButton alloc] init] retain];
    NSStepper *stepper = [spinbutton stepper];
    NSTextField *textfield = [spinbutton textfield];
    [stepper setIncrement:1];
    [stepper setTag:cid];
    [stepper setMinValue:-65536];
    [stepper setMaxValue:65536];
    [stepper setIntValue:atoi(text)];
    [textfield takeIntValueFrom:stepper];
    return spinbutton;
}

/*
 * Sets the spinbutton value.
 * Parameters:
 *          handle: Handle to the spinbutton to be set.
 *          position: Current value of the spinbutton.
 */
void API dw_spinbutton_set_pos(HWND handle, long position)
{
    DWSpinButton *spinbutton = handle;
    NSStepper *stepper = [spinbutton stepper];
    NSTextField *textfield = [spinbutton textfield];
    [stepper setIntValue:(int)position];
    [textfield takeIntValueFrom:stepper];
}

/*
 * Sets the spinbutton limits.
 * Parameters:
 *          handle: Handle to the spinbutton to be set.
 *          upper: Upper limit.
 *          lower: Lower limit.
 */
void API dw_spinbutton_set_limits(HWND handle, long upper, long lower)
{
    DWSpinButton *spinbutton = handle;
    NSStepper *stepper = [spinbutton stepper];
    [stepper setMinValue:(double)lower];
    [stepper setMaxValue:(double)upper];
}

/*
 * Returns the current value of the spinbutton.
 * Parameters:
 *          handle: Handle to the spinbutton to be queried.
 */
long API dw_spinbutton_get_pos(HWND handle)
{
    DWSpinButton *spinbutton = handle;
    NSStepper *stepper = [spinbutton stepper];
    return (long)[stepper integerValue];
}

/*
 * Create a new radiobutton window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_radiobutton_new(const char *text, ULONG cid)
{
    DWButton *button = _dw_button_new(text, cid);
    [button setButtonType:DWButtonTypeRadio];
    return button;
}

/*
 * Create a new slider window (widget) to be packed.
 * Parameters:
 *       vertical: TRUE or FALSE if slider is vertical.
 *       increments: Number of increments available.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_slider_new(int vertical, int increments, ULONG cid)
{
    DWSlider *slider = [[[DWSlider alloc] init] retain];
    [slider setMaxValue:(double)increments];
    [slider setMinValue:0];
    [slider setContinuous:YES];
    [slider setTarget:slider];
    [slider setAction:@selector(sliderChanged:)];
    [slider setTag:cid];
    return slider;
}

/*
 * Returns the position of the slider.
 * Parameters:
 *          handle: Handle to the slider to be queried.
 */
unsigned int API dw_slider_get_pos(HWND handle)
{
    DWSlider *slider = handle;
    double val = [slider doubleValue];
    return (int)val;
}

/*
 * Sets the slider position.
 * Parameters:
 *          handle: Handle to the slider to be set.
 *          position: Position of the slider withing the range.
 */
void API dw_slider_set_pos(HWND handle, unsigned int position)
{
    DWSlider *slider = handle;
    [slider setDoubleValue:(double)position];
}

/*
 * Create a new scrollbar window (widget) to be packed.
 * Parameters:
 *       vertical: TRUE or FALSE if scrollbar is vertical.
 *       increments: Number of increments available.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_scrollbar_new(int vertical, ULONG cid)
{
    DWScrollbar *scrollbar;
    if(vertical)
    {
        scrollbar = [[[DWScrollbar alloc] init] retain];
        [scrollbar setVertical:YES];
    }
    else
    {
        scrollbar = [[[DWScrollbar alloc] initWithFrame:NSMakeRect(0,0,100,5)] retain];
    }
#ifndef BUILDING_FOR_YOSEMITE
    [scrollbar setArrowsPosition:NSScrollerArrowsDefaultSetting];
#endif
    [scrollbar setRange:0.0 andVisible:0.0];
    [scrollbar setKnobProportion:1.0];
    [scrollbar setTarget:scrollbar];
    [scrollbar setAction:@selector(scrollerChanged:)];
    [scrollbar setTag:cid];
    [scrollbar setEnabled:YES];
    return scrollbar;
}

/*
 * Returns the position of the scrollbar.
 * Parameters:
 *          handle: Handle to the scrollbar to be queried.
 */
unsigned int API dw_scrollbar_get_pos(HWND handle)
{
    DWScrollbar *scrollbar = handle;
    float range = [scrollbar range];
    float fresult = [scrollbar doubleValue] * range;
    return (int)fresult;
}

/*
 * Sets the scrollbar position.
 * Parameters:
 *          handle: Handle to the scrollbar to be set.
 *          position: Position of the scrollbar withing the range.
 */
void API dw_scrollbar_set_pos(HWND handle, unsigned int position)
{
    DWScrollbar *scrollbar = handle;
    double range = [scrollbar range];
    double visible = [scrollbar visible];
    double newpos = (double)position/(range-visible);
    [scrollbar setDoubleValue:newpos];
}

/*
 * Sets the scrollbar range.
 * Parameters:
 *          handle: Handle to the scrollbar to be set.
 *          range: Maximum range value.
 *          visible: Visible area relative to the range.
 */
void API dw_scrollbar_set_range(HWND handle, unsigned int range, unsigned int visible)
{
    DWScrollbar *scrollbar = handle;
    double knob = (double)visible/(double)range;
    [scrollbar setRange:(double)range andVisible:(double)visible];
    [scrollbar setKnobProportion:knob];
}

/*
 * Create a new percent bar window (widget) to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_percent_new(ULONG cid)
{
    DWPercent *percent = [[[DWPercent alloc] init] retain];
    [percent setStyle:DWProgressIndicatorStyleBar];
    [percent setBezeled:YES];
    [percent setMaxValue:100];
    [percent setMinValue:0];
    [percent incrementBy:1];
    [percent setIndeterminate:NO];
    [percent setDoubleValue:0];
    /*[percent setTag:cid]; Why doesn't this work? */
    return percent;
}

/*
 * Sets the percent bar position.
 * Parameters:
 *          handle: Handle to the percent bar to be set.
 *          position: Position of the percent bar withing the range.
 */
DW_FUNCTION_DEFINITION(dw_percent_set_pos, void, HWND handle, unsigned int position)
DW_FUNCTION_ADD_PARAM2(handle, position)
DW_FUNCTION_NO_RETURN(dw_percent_set_pos)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, position, unsigned int)
{
    DW_FUNCTION_INIT;
    DWPercent *percent = handle;

    /* Handle indeterminate */
    if(position == DW_PERCENT_INDETERMINATE)
    {
        [percent setIndeterminate:YES];
        [percent startAnimation:percent];
    }
    else
    {
        /* Handle normal */
        if([percent isIndeterminate])
        {
            [percent stopAnimation:percent];
            [percent setIndeterminate:NO];
        }
        [percent setDoubleValue:(double)position];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Create a new checkbox window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_checkbox_new(const char *text, ULONG cid)
{
    DWButton *button = _dw_button_new(text, cid);
    [button setButtonType:DWButtonTypeSwitch];
    [button setBezelStyle:DWBezelStyleRegularSquare];
    return button;
}

/*
 * Returns the state of the checkbox.
 * Parameters:
 *          handle: Handle to the checkbox to be queried.
 */
int API dw_checkbox_get(HWND handle)
{
    DWButton *button = handle;
    if([button state])
    {
        return TRUE;
    }
    return FALSE;
}

/*
 * Sets the state of the checkbox.
 * Parameters:
 *          handle: Handle to the checkbox to be queried.
 *          value: TRUE for checked, FALSE for unchecked.
 */
void API dw_checkbox_set(HWND handle, int value)
{
    DWButton *button = handle;
    if(value)
    {
        [button setState:DWControlStateValueOn];
    }
    else
    {
        [button setState:DWControlStateValueOff];
    }

}

/* Internal common function to create containers and listboxes */
HWND _dw_cont_new(ULONG cid, int multi)
{
    DWFocusRingScrollView *scrollview  = [[[DWFocusRingScrollView alloc] init] retain];
    DWContainer *cont = [[[DWContainer alloc] init] retain];

    [cont setScrollview:scrollview];
    [scrollview setBorderType:NSBezelBorder];
    [scrollview setHasVerticalScroller:YES];
    [scrollview setAutohidesScrollers:YES];
    [cont setAllowsMultipleSelection:(multi ? YES : NO)];
    [cont setAllowsColumnReordering:NO];
    [cont setDataSource:cont];
    [cont setDelegate:cont];
    [scrollview setDocumentView:cont];
    [cont setTag:cid];
    [cont autorelease];
    [cont setRowBgOdd:DW_RGB_TRANSPARENT andEven:DW_RGB_TRANSPARENT];
    return cont;
}

/*
 * Create a new listbox window (widget) to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id() or 0L.
 *       multi: Multiple select TRUE or FALSE.
 */
DW_FUNCTION_DEFINITION(dw_listbox_new, HWND, ULONG cid, int multi)
DW_FUNCTION_ADD_PARAM2(cid, multi)
DW_FUNCTION_RETURN(dw_listbox_new, HWND)
DW_FUNCTION_RESTORE_PARAM2(cid, ULONG, multi, int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = _dw_cont_new(cid, multi);
    [cont setHeaderView:nil];
    int type = DW_CFA_STRING;
    [cont setup];
    NSTableColumn *column = [[[NSTableColumn alloc] initWithIdentifier:@"_DWListboxColumn"] autorelease];
    [column setEditable:NO];
    [cont addTableColumn:column];
    [cont addColumn:column andType:type];
    DW_FUNCTION_RETURN_THIS(cont);
}

/*
 * Appends the specified text to the listbox's (or combobox) entry list.
 * Parameters:
 *          handle: Handle to the listbox to be appended to.
 *          text: Text to append into listbox.
 */
DW_FUNCTION_DEFINITION(dw_listbox_append, void, HWND handle, const char *text)
DW_FUNCTION_ADD_PARAM2(handle, text)
DW_FUNCTION_NO_RETURN(dw_listbox_append)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, text, char *)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;

        [combo addItemWithObjectValue:[NSString stringWithUTF8String:text]];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        NSString *nstr = [NSString stringWithUTF8String:text];
#ifdef DW_USE_NSVIEW
        NSArray *newrow = [NSArray arrayWithObject:_dw_table_cell_view_new(nil, nstr)];
#else
        NSArray *newrow = [NSArray arrayWithObject:nstr];
#endif

        [cont addRow:newrow];
        [cont reloadData];
        [cont optimize];
        [cont setNeedsDisplay:YES];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Inserts the specified text into the listbox's (or combobox) entry list.
 * Parameters:
 *          handle: Handle to the listbox to be inserted into.
 *          text: Text to insert into listbox.
 *          pos: 0-based position to insert text
 */
DW_FUNCTION_DEFINITION(dw_listbox_insert, void, HWND handle, const char *text, int pos)
DW_FUNCTION_ADD_PARAM3(handle, text, pos)
DW_FUNCTION_NO_RETURN(dw_listbox_insert)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, text, const char *, pos, int)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;

        [combo insertItemWithObjectValue:[NSString stringWithUTF8String:text] atIndex:pos];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        NSString *nstr = [NSString stringWithUTF8String:text];
#ifdef DW_USE_NSVIEW
        NSArray *newrow = [NSArray arrayWithObject:_dw_table_cell_view_new(nil, nstr)];
#else
        NSArray *newrow = [NSArray arrayWithObject:nstr];
#endif

        [cont insertRow:newrow at:pos];
        [cont reloadData];
        [cont optimize];
        [cont setNeedsDisplay:YES];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Appends the specified text items to the listbox's (or combobox) entry list.
 * Parameters:
 *          handle: Handle to the listbox to be appended to.
 *          text: Text strings to append into listbox.
 *          count: Number of text strings to append
 */
DW_FUNCTION_DEFINITION(dw_listbox_list_append, void, HWND handle, char **text, int count)
DW_FUNCTION_ADD_PARAM3(handle, text, count)
DW_FUNCTION_NO_RETURN(dw_listbox_list_append)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, text, char **, count, int)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;
        int z;

        for(z=0;z<count;z++)
        {
            [combo addItemWithObjectValue:[ NSString stringWithUTF8String:text[z] ]];
        }
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        int z;

        for(z=0;z<count;z++)
        {
            NSString *nstr = [NSString stringWithUTF8String:text[z]];
#ifdef DW_USE_NSVIEW
            NSArray *newrow = [NSArray arrayWithObjects:_dw_table_cell_view_new(nil, nstr),nil];
#else
            NSArray *newrow = [NSArray arrayWithObjects:nstr,nil];
#endif

            [cont addRow:newrow];
        }
        [cont reloadData];
        [cont optimize];
        [cont setNeedsDisplay:YES];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Clears the listbox's (or combobox) list of all entries.
 * Parameters:
 *          handle: Handle to the listbox to be cleared.
 */
DW_FUNCTION_DEFINITION(dw_listbox_clear, void, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_NO_RETURN(dw_listbox_clear)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;

        [combo removeAllItems];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;

        [cont clear];
        [cont reloadData];
        [cont setNeedsDisplay:YES];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Returns the listbox's item count.
 * Parameters:
 *          handle: Handle to the listbox to be cleared.
 */
DW_FUNCTION_DEFINITION(dw_listbox_count, int, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_RETURN(dw_listbox_count, int)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    id object = handle;
    int result = 0;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;

        result = (int)[combo numberOfItems];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        result = (int)[cont numberOfRowsInTableView:cont];
    }
    DW_FUNCTION_RETURN_THIS(result);
}

/*
 * Sets the topmost item in the viewport.
 * Parameters:
 *          handle: Handle to the listbox to be cleared.
 *          top: Index to the top item.
 */
DW_FUNCTION_DEFINITION(dw_listbox_set_top, void, HWND handle, int top)
DW_FUNCTION_ADD_PARAM2(handle, top)
DW_FUNCTION_NO_RETURN(dw_listbox_set_top)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, top, int)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;

        [combo scrollItemAtIndexToTop:top];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;

        [cont scrollRowToVisible:top];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Copies the given index item's text into buffer.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 *          index: Index into the list to be queried.
 *          buffer: Buffer where text will be copied.
 *          length: Length of the buffer (including NULL).
 */
DW_FUNCTION_DEFINITION(dw_listbox_get_text, void, HWND handle, unsigned int index, char *buffer, unsigned int length)
DW_FUNCTION_ADD_PARAM4(handle, index, buffer, length)
DW_FUNCTION_NO_RETURN(dw_listbox_get_text)
DW_FUNCTION_RESTORE_PARAM4(handle, HWND, index, unsigned int, buffer, char *, length, unsigned int)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;
        int count = (int)[combo numberOfItems];

        if(index > count)
        {
            *buffer = '\0';
        }
        else
        {
            NSString *nstr = [combo itemObjectValueAtIndex:index];
            strncpy(buffer, [ nstr UTF8String ], length - 1);
        }
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        int count = (int)[cont numberOfRowsInTableView:cont];

        if(index > count)
        {
            *buffer = '\0';
        }
        else
        {
#ifdef DW_USE_NSVIEW
            NSTableCellView *cell = [cont getRow:index and:0];
            NSString *nstr = [[cell textField] stringValue];
#else
            NSString *nstr = [cont getRow:index and:0];
#endif

            strncpy(buffer, [nstr UTF8String], length - 1);
        }
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the text of a given listbox entry.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 *          index: Index into the list to be queried.
 *          buffer: Buffer where text will be copied.
 */
DW_FUNCTION_DEFINITION(dw_listbox_set_text, void, HWND handle, unsigned int index, const char *buffer)
DW_FUNCTION_ADD_PARAM3(handle, index, buffer)
DW_FUNCTION_NO_RETURN(dw_listbox_set_text)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, index, unsigned int, buffer, char *)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;
        int count = (int)[combo numberOfItems];

        if(index <= count)
        {
            [combo removeItemAtIndex:index];
            [combo insertItemWithObjectValue:[NSString stringWithUTF8String:buffer] atIndex:index];
        }
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        int count = (int)[cont numberOfRowsInTableView:cont];

        if(index <= count)
        {
            NSString *nstr = [NSString stringWithUTF8String:buffer];
#ifdef DW_USE_NSVIEW
            NSTableCellView *cell = [cont getRow:index and:0];
            
            [[cell textField] setStringValue:nstr];
#else
            [cont editCell:nstr at:index and:0];
#endif
            [cont reloadData];
            [cont optimize];
            [cont setNeedsDisplay:YES];
        }
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Returns the index to the item in the list currently selected.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 */
DW_FUNCTION_DEFINITION(dw_listbox_selected, int, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_RETURN(dw_listbox_selected, int)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    id object = handle;
    int result = -1;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;
        result = (int)[combo indexOfSelectedItem];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        result = (int)[cont selectedRow];
    }
    DW_FUNCTION_RETURN_THIS(result);
}

/*
 * Returns the index to the current selected item or -1 when done.
 * Parameters:
 *          handle: Handle to the listbox to be queried.
 *          where: Either the previous return or -1 to restart.
 */
DW_FUNCTION_DEFINITION(dw_listbox_selected_multi, int, HWND handle, int where)
DW_FUNCTION_ADD_PARAM2(handle, where)
DW_FUNCTION_RETURN(dw_listbox_selected_multi, int)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, where, int)
{
    DW_FUNCTION_INIT;
    id object = handle;
    int retval = -1;

    if([object isMemberOfClass:[DWContainer class]])
    {
        NSUInteger result;
        DWContainer *cont = handle;
        NSIndexSet *selected = [cont selectedRowIndexes];
        if( where == -1 )
           result = [selected indexGreaterThanOrEqualToIndex:0];
        else
           result = [selected indexGreaterThanIndex:where];

        if(result != NSNotFound)
        {
            retval = (int)result;
        }
    }
    DW_FUNCTION_RETURN_THIS(retval)
}

/*
 * Sets the selection state of a given index.
 * Parameters:
 *          handle: Handle to the listbox to be set.
 *          index: Item index.
 *          state: TRUE if selected FALSE if unselected.
 */
DW_FUNCTION_DEFINITION(dw_listbox_select, void, HWND handle, int index, int state)
DW_FUNCTION_ADD_PARAM3(handle, index, state)
DW_FUNCTION_NO_RETURN(dw_listbox_select)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, index, int, state, int)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;
        if(state)
            [combo selectItemAtIndex:index];
        else
            [combo deselectItemAtIndex:index];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;
        NSIndexSet *selected = [[NSIndexSet alloc] initWithIndex:(NSUInteger)index];

        [cont selectRowIndexes:selected byExtendingSelection:YES];
        [selected release];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Deletes the item with given index from the list.
 * Parameters:
 *          handle: Handle to the listbox to be set.
 *          index: Item index.
 */
DW_FUNCTION_DEFINITION(dw_listbox_delete, void, HWND handle, int index)
DW_FUNCTION_ADD_PARAM2(handle, index)
DW_FUNCTION_NO_RETURN(dw_listbox_delete)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, index, int)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[DWComboBox class]])
    {
        DWComboBox *combo = handle;

        [combo removeItemAtIndex:index];
    }
    else if([object isMemberOfClass:[DWContainer class]])
    {
        DWContainer *cont = handle;

        [cont removeRow:index];
        [cont reloadData];
        [cont setNeedsDisplay:YES];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Create a new Combobox window (widget) to be packed.
 * Parameters:
 *       text: The default text to be in the combpbox widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
DW_FUNCTION_DEFINITION(dw_combobox_new, HWND, const char *text, ULONG cid)
DW_FUNCTION_ADD_PARAM2(text, cid)
DW_FUNCTION_RETURN(dw_combobox_new, HWND)
DW_FUNCTION_RESTORE_PARAM2(text, const char *, cid, ULONG)
{
    DW_FUNCTION_INIT;
    DWComboBox *combo = [[[DWComboBox alloc] init] retain];
    [combo setStringValue:[NSString stringWithUTF8String:text]];
    [combo setDelegate:combo];
    [combo setTag:cid];
    DW_FUNCTION_RETURN_THIS(combo);
}

/*
 * Create a new Multiline Editbox window (widget) to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_mle_new(ULONG cid)
{
    DWMLE *mle = [[[DWMLE alloc] init] retain];
    NSScrollView *scrollview  = [[[NSScrollView alloc] init] retain];
    NSSize size = [mle maxSize];

    size.width = size.height;
    [mle setMaxSize:size];
    [scrollview setBorderType:NSBezelBorder];
    [scrollview setHasVerticalScroller:YES];
    [scrollview setAutohidesScrollers:YES];
    [scrollview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
    [scrollview setDocumentView:mle];
    [mle setVerticallyResizable:YES];
    [mle setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
    [mle setScrollview:scrollview];
#ifdef BUILDING_FOR_SNOW_LEOPARD
    [mle setAutomaticQuoteSubstitutionEnabled:NO];
    [mle setAutomaticDashSubstitutionEnabled:NO];
    [mle setAutomaticTextReplacementEnabled:NO];
#endif
    /* [mle setTag:cid]; Why doesn't this work? */
    [mle autorelease];
    return mle;
}

/*
 * Adds text to an MLE box and returns the current point.
 * Parameters:
 *          handle: Handle to the MLE to be queried.
 *          buffer: Text buffer to be imported.
 *          startpoint: Point to start entering text.
 */
DW_FUNCTION_DEFINITION(dw_mle_import, unsigned int, HWND handle, const char *buffer, int startpoint)
DW_FUNCTION_ADD_PARAM3(handle, buffer, startpoint)
DW_FUNCTION_RETURN(dw_mle_import, unsigned int)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, buffer, const char *, startpoint, int)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    DWMLE *mle = handle;
    unsigned int retval;
    NSTextStorage *ts = [mle textStorage];
    NSString *nstr = [NSString stringWithUTF8String:buffer];
    NSColor *fgcolor = [ts foregroundColor];
    NSFont *font = [ts font];
    NSMutableDictionary *attributes = [[[NSMutableDictionary alloc] init] autorelease];
    [attributes setObject:(fgcolor ? fgcolor : [NSColor textColor]) forKey:NSForegroundColorAttributeName];
    if(font)
        [attributes setObject:font forKey:NSFontAttributeName];
    NSAttributedString *nastr = [[[NSAttributedString alloc] initWithString:nstr attributes:attributes] autorelease];
    NSUInteger length = [ts length];
    if(startpoint < 0)
        startpoint = 0;
    if(startpoint > length)
        startpoint = (int)length;
    [ts insertAttributedString:nastr atIndex:startpoint];
    retval = (unsigned int)strlen(buffer) + startpoint;
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Grabs text from an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be queried.
 *          buffer: Text buffer to be exported.
 *          startpoint: Point to start grabbing text.
 *          length: Amount of text to be grabbed.
 */
DW_FUNCTION_DEFINITION(dw_mle_export, void, HWND handle, char *buffer, int startpoint, int length)
DW_FUNCTION_ADD_PARAM4(handle, buffer, startpoint, length)
DW_FUNCTION_NO_RETURN(dw_mle_export)
DW_FUNCTION_RESTORE_PARAM4(handle, HWND, buffer, char *, startpoint, int, length, int)
{
    DW_FUNCTION_INIT;
    DWMLE *mle = handle;
    NSTextStorage *ts = [mle textStorage];
    NSMutableString *ms = [ts mutableString];
    const char *tmp = [ms UTF8String];
    strncpy(buffer, tmp+startpoint, length);
    buffer[length] = '\0';
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Obtains information about an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be queried.
 *          bytes: A pointer to a variable to return the total bytes.
 *          lines: A pointer to a variable to return the number of lines.
 */
DW_FUNCTION_DEFINITION(dw_mle_get_size, void, HWND handle, unsigned long *bytes, unsigned long *lines)
DW_FUNCTION_ADD_PARAM3(handle, bytes, lines)
DW_FUNCTION_NO_RETURN(dw_mle_get_size)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, bytes, unsigned long *, lines, unsigned long *)
{
    DW_FUNCTION_INIT;
    DWMLE *mle = handle;
    NSTextStorage *ts = [mle textStorage];
    NSMutableString *ms = [ts mutableString];
    NSUInteger numberOfLines, index, stringLength = [ms length];

    if(bytes)
        *bytes = stringLength;
    if(lines)
    {
        for(index=0, numberOfLines=0; index < stringLength; numberOfLines++)
            index = NSMaxRange([ms lineRangeForRange:NSMakeRange(index, 0)]);

        *lines = numberOfLines;
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Deletes text from an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be deleted from.
 *          startpoint: Point to start deleting text.
 *          length: Amount of text to be deleted.
 */
DW_FUNCTION_DEFINITION(dw_mle_delete, void, HWND handle, int startpoint, int length)
DW_FUNCTION_ADD_PARAM3(handle, startpoint, length)
DW_FUNCTION_NO_RETURN(dw_mle_delete)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, startpoint, int, length, int)
{
    DW_FUNCTION_INIT;
    DWMLE *mle = handle;
    NSTextStorage *ts = [mle textStorage];
    NSMutableString *ms = [ts mutableString];
    NSUInteger mslength = [ms length];
    if(startpoint < 0)
        startpoint = 0;
    if(startpoint > mslength)
        startpoint = (int)mslength;
    if(startpoint + length > mslength)
        length = (int)mslength - startpoint;
    [ms deleteCharactersInRange:NSMakeRange(startpoint, length)];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Clears all text from an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be cleared.
 */
DW_FUNCTION_DEFINITION(dw_mle_clear, void, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_NO_RETURN(dw_mle_clear)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    DWMLE *mle = handle;
    NSTextStorage *ts = [mle textStorage];
    NSMutableString *ms = [ts mutableString];
    NSUInteger length = [ms length];
    [ms deleteCharactersInRange:NSMakeRange(0, length)];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the visible line of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be positioned.
 *          line: Line to be visible.
 */
DW_FUNCTION_DEFINITION(dw_mle_set_visible, void, HWND handle, int line)
DW_FUNCTION_ADD_PARAM2(handle, line)
DW_FUNCTION_NO_RETURN(dw_mle_set_visible)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, line, int)
{
    DW_FUNCTION_INIT;
    DWMLE *mle = handle;
    NSTextStorage *ts = [mle textStorage];
    NSMutableString *ms = [ts mutableString];
    NSUInteger numberOfLines, index, stringLength = [ms length];

    for(index=0, numberOfLines=0; index < stringLength && numberOfLines < line; numberOfLines++)
        index = NSMaxRange([ms lineRangeForRange:NSMakeRange(index, 0)]);

    if(line == numberOfLines)
    {
        [mle scrollRangeToVisible:[ms lineRangeForRange:NSMakeRange(index, 0)]];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the editablity of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE.
 *          state: TRUE if it can be edited, FALSE for readonly.
 */
void API dw_mle_set_editable(HWND handle, int state)
{
    DWMLE *mle = handle;
    if(state)
    {
        [mle setEditable:YES];
    }
    else
    {
        [mle setEditable:NO];
    }
}

/*
 * Sets the word wrap state of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE.
 *          state: TRUE if it wraps, FALSE if it doesn't.
 */
void API dw_mle_set_word_wrap(HWND handle, int state)
{
    DWMLE *mle = handle;
    NSScrollView *sv = [mle scrollview];

    if(state)
    {
        NSSize newsize = NSMakeSize([sv contentSize].width,[mle maxSize].height);
        NSRect newrect = NSMakeRect(0, 0, [sv contentSize].width, 0);
        
        [[mle textContainer] setWidthTracksTextView:YES];
        [mle setFrame:newrect];
        [[mle textContainer] setContainerSize:newsize];
        [mle setHorizontallyResizable:NO];
        [sv setHasHorizontalScroller:NO];
    }
    else
    {
        [[mle textContainer] setWidthTracksTextView:NO];
        [[mle textContainer] setContainerSize:[mle maxSize]];
        [mle setHorizontallyResizable:YES];
        [sv setHasHorizontalScroller:YES];
    }
}

/*
 * Sets the word auto complete state of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE.
 *          state: Bitwise combination of DW_MLE_COMPLETE_TEXT/DASH/QUOTE
 */
void API dw_mle_set_auto_complete(HWND handle, int state)
{
#ifdef BUILDING_FOR_SNOW_LEOPARD
    DWMLE *mle = handle;
    [mle setAutomaticQuoteSubstitutionEnabled:(state & DW_MLE_COMPLETE_QUOTE ? YES : NO)];
    [mle setAutomaticDashSubstitutionEnabled:(state & DW_MLE_COMPLETE_DASH ? YES : NO)];
    [mle setAutomaticTextReplacementEnabled:(state & DW_MLE_COMPLETE_TEXT ? YES : NO)];
#endif
}

/*
 * Sets the current cursor position of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be positioned.
 *          point: Point to position cursor.
 */
DW_FUNCTION_DEFINITION(dw_mle_set_cursor, void, HWND handle, int point)
DW_FUNCTION_ADD_PARAM2(handle, point)
DW_FUNCTION_NO_RETURN(dw_mle_set_cursor)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, point, int)
{
    DW_FUNCTION_INIT;
    DWMLE *mle = handle;
    NSTextStorage *ts = [mle textStorage];
    NSMutableString *ms = [ts mutableString];
    NSUInteger length = [ms length];
    if(point < 0)
        point = 0;
    if(point > length)
        point = (int)length;
    [mle setSelectedRange: NSMakeRange(point,point)];
    [mle scrollRangeToVisible:NSMakeRange(point,point)];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Finds text in an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to be cleared.
 *          text: Text to search for.
 *          point: Start point of search.
 *          flags: Search specific flags.
 */
DW_FUNCTION_DEFINITION(dw_mle_search, int, HWND handle, const char *text, int point, unsigned long flags)
DW_FUNCTION_ADD_PARAM4(handle, text, point, flags)
DW_FUNCTION_RETURN(dw_mle_search, int)
DW_FUNCTION_RESTORE_PARAM4(handle, HWND, text, const char *, point, int, flags, unsigned long)
{
    DW_FUNCTION_INIT;
    DWMLE *mle = handle;
    NSTextStorage *ts = [mle textStorage];
    NSMutableString *ms = [ts mutableString];
    NSString *searchForMe = [NSString stringWithUTF8String:text];
    NSRange searchRange = NSMakeRange(point, [ms length] - point);
    NSRange range = NSMakeRange(NSNotFound, 0);
    NSUInteger options = flags ? flags : NSCaseInsensitiveSearch;
    int retval = -1;

    if(ms)
        range = [ms rangeOfString:searchForMe options:options range:searchRange];
    if(range.location == NSNotFound)
        retval = (int)range.location;
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Stops redrawing of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to freeze.
 */
void API dw_mle_freeze(HWND handle)
{
    /* Don't think this is necessary */
}

/*
 * Resumes redrawing of an MLE box.
 * Parameters:
 *          handle: Handle to the MLE to thaw.
 */
void API dw_mle_thaw(HWND handle)
{
    /* Don't think this is necessary */
}

/*
 * Create a new status text window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_status_text_new(const char *text, ULONG cid)
{
    NSBox *border = [[[NSBox alloc] init] retain];
    NSTextField *textfield = dw_text_new(text, cid);

#ifndef BUILDING_FOR_CATALINA
    [border setBorderType:NSGrooveBorder];
#endif
    [border setTitlePosition:NSNoTitle];
    [border setContentView:textfield];
    [textfield release];
    [border setContentViewMargins:NSMakeSize(1.5,1.5)];
    [textfield autorelease];
    [textfield setBackgroundColor:[NSColor clearColor]];
    [[textfield cell] setVCenter:YES];
    return border;
}

/*
 * Create a new static text window (widget) to be packed.
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_text_new(const char *text, ULONG cid)
{
    DWText *textfield = [[[DWText alloc] init] retain];
    [textfield setEditable:NO];
    [textfield setSelectable:NO];
    [textfield setBordered:NO];
    [textfield setDrawsBackground:NO];
    [textfield setStringValue:[ NSString stringWithUTF8String:text ]];
    [textfield setTag:cid];
    if(DWDefaultFont)
    {
        [[textfield cell] setFont:DWDefaultFont];
    }
    [[textfield cell] setWraps:NO];
    return textfield;
}

/*
 * Creates a rendering context widget (window) to be packed.
 * Parameters:
 *       id: An id to be used with dw_window_from_id.
 * Returns:
 *       A handle to the widget or NULL on failure.
 */
HWND API dw_render_new(unsigned long cid)
{
    DWRender *render = [[[DWRender alloc] init] retain];
    [render setTag:cid];
    return render;
}

/*
 * Invalidate the render widget triggering an expose event.
 * Parameters:
 *       handle: A handle to a render widget to be redrawn.
 */
DW_FUNCTION_DEFINITION(dw_render_redraw, void, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_NO_RETURN(dw_render_redraw)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    DWRender *render = (DWRender *)handle;

    [render setNeedsDisplay:YES];
    DW_FUNCTION_RETURN_NOTHING;
}

/* Sets the current foreground drawing color.
 * Parameters:
 *       red: red value.
 *       green: green value.
 *       blue: blue value.
 */
void API dw_color_foreground_set(unsigned long value)
{
    NSColor *oldcolor = pthread_getspecific(_dw_fg_color_key);
    NSColor *newcolor;
    DW_LOCAL_POOL_IN;

    _dw_foreground = _dw_get_color(value);

    newcolor = [[NSColor colorWithDeviceRed:    DW_RED_VALUE(_dw_foreground)/255.0 green:
                                                DW_GREEN_VALUE(_dw_foreground)/255.0 blue:
                                                DW_BLUE_VALUE(_dw_foreground)/255.0 alpha: 1] retain];
    pthread_setspecific(_dw_fg_color_key, newcolor);
    [oldcolor release];
    DW_LOCAL_POOL_OUT;
}

/* Sets the current background drawing color.
 * Parameters:
 *       red: red value.
 *       green: green value.
 *       blue: blue value.
 */
void API dw_color_background_set(unsigned long value)
{
    NSColor *oldcolor = pthread_getspecific(_dw_bg_color_key);
    NSColor *newcolor;
    DW_LOCAL_POOL_IN;

    if(value == DW_CLR_DEFAULT)
    {
        pthread_setspecific(_dw_bg_color_key, NULL);
    }
    else
    {
        _dw_background = _dw_get_color(value);

        newcolor = [[NSColor colorWithDeviceRed:    DW_RED_VALUE(_dw_background)/255.0 green:
                                                    DW_GREEN_VALUE(_dw_background)/255.0 blue:
                                                    DW_BLUE_VALUE(_dw_background)/255.0 alpha: 1] retain];
        pthread_setspecific(_dw_bg_color_key, newcolor);
    }
    [oldcolor release];
    DW_LOCAL_POOL_OUT;
}

/* Allows the user to choose a color using the system's color chooser dialog.
 * Parameters:
 *       value: current color
 * Returns:
 *       The selected color or the current color if cancelled.
 */
unsigned long API dw_color_choose(unsigned long value)
{
    /* Create the Color Chooser Dialog class. */
    DWColorChoose *colorDlg;
    DWDialog *dialog;
    DW_LOCAL_POOL_IN;

    if(![DWColorChoose sharedColorPanelExists])
    {
        colorDlg = (DWColorChoose *)[DWColorChoose sharedColorPanel];
        /* Set defaults for the dialog. */
        [colorDlg setContinuous:NO];
        [colorDlg setTarget:colorDlg];
        [colorDlg setAction:@selector(changeColor:)];
    }
    else
        colorDlg = (DWColorChoose *)[DWColorChoose sharedColorPanel];

    /* If someone is already waiting just return */
    if([colorDlg dialog])
    {
        DW_LOCAL_POOL_OUT;
        return value;
    }

    unsigned long tempcol = _dw_get_color(value);
    NSColor *color = [[NSColor colorWithDeviceRed: DW_RED_VALUE(tempcol)/255.0 green: DW_GREEN_VALUE(tempcol)/255.0 blue: DW_BLUE_VALUE(tempcol)/255.0 alpha: 1] retain];
    [colorDlg setColor:color];

    dialog = dw_dialog_new(colorDlg);
    [colorDlg setDialog:dialog];
    [colorDlg makeKeyAndOrderFront:nil];

    /* Wait for them to pick a color */
    color = (NSColor *)dw_dialog_wait(dialog);

    /* Figure out the value of what they returned */
    CGFloat red, green, blue;
    [color getRed:&red green:&green blue:&blue alpha:NULL];
    value = DW_RGB((int)(red * 255), (int)(green *255), (int)(blue *255));
    DW_LOCAL_POOL_OUT;
    return value;
}

/* Set the current context to be a flipped image,
 * On 10.10 or higher use CoreGraphics otherwise Graphics Port
 */
NSGraphicsContext *_dw_draw_context(NSBitmapImageRep *image)
{
#ifdef BUILDING_FOR_YOSEMITE
    return [NSGraphicsContext graphicsContextWithCGContext:[[NSGraphicsContext graphicsContextWithBitmapImageRep:image] CGContext] flipped:YES];
#else
    return [NSGraphicsContext graphicsContextWithGraphicsPort:[[NSGraphicsContext graphicsContextWithBitmapImageRep:image] graphicsPort] flipped:YES];
#endif
}

/* Draw a point on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       x: X coordinate.
 *       y: Y coordinate.
 */
DW_FUNCTION_DEFINITION(dw_draw_point, void, HWND handle, HPIXMAP pixmap, int x, int y)
DW_FUNCTION_ADD_PARAM4(handle, pixmap, x, y)
DW_FUNCTION_NO_RETURN(dw_draw_point)
DW_FUNCTION_RESTORE_PARAM4(handle, HWND, pixmap, HPIXMAP, x, int, y, int)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id image = handle;
    NSBitmapImageRep *bi = nil;
    bool bCanDraw = YES;

    if(pixmap)
        bi = (id)pixmap->image;
    else
    {
#ifdef BUILDING_FOR_MOJAVE
        if([image isMemberOfClass:[DWRender class]])
        {
            DWRender *render = image;

            bi = [render cachedDrawingRep];
        }
#else
        if((bCanDraw = [image lockFocusIfCanDraw]) == YES)
            _DWLastDrawable = handle;
#endif
    }
    if(bi)
    {
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:_dw_draw_context(bi)];
    }
    if(bCanDraw == YES)
    {
        NSBezierPath* aPath = [NSBezierPath bezierPath];
#ifndef BUILDING_FOR_MOJAVE
        NSColor *_dw_fg_color = pthread_getspecific(_dw_fg_color_key);
#endif

        [aPath setLineWidth: 0.5];
        [_dw_fg_color set];

        [aPath moveToPoint:NSMakePoint(x, y)];
        [aPath stroke];
    }
    if(bi)
        [NSGraphicsContext restoreGraphicsState];
#ifndef BUILDING_FOR_MOJAVE
    if(bCanDraw == YES && !pixmap)
        [image unlockFocus];
#endif
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/* Draw a line on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       x1: First X coordinate.
 *       y1: First Y coordinate.
 *       x2: Second X coordinate.
 *       y2: Second Y coordinate.
 */
DW_FUNCTION_DEFINITION(dw_draw_line, void, HWND handle, HPIXMAP pixmap, int x1, int y1, int x2, int y2)
DW_FUNCTION_ADD_PARAM6(handle, pixmap, x1, y1, x2, y2)
DW_FUNCTION_NO_RETURN(dw_draw_line)
DW_FUNCTION_RESTORE_PARAM6(handle, HWND, pixmap, HPIXMAP, x1, int, y1, int, x2, int, y2, int)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id image = handle;
    NSBitmapImageRep *bi = nil;
    bool bCanDraw = YES;

    if(pixmap)
        bi = (id)pixmap->image;
    else
    {
#ifdef BUILDING_FOR_MOJAVE
        if([image isMemberOfClass:[DWRender class]])
        {
            DWRender *render = image;

            bi = [render cachedDrawingRep];
        }
#else
        if((bCanDraw = [image lockFocusIfCanDraw]) == YES)
            _DWLastDrawable = handle;
#endif
    }
    if(bi)
    {
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:_dw_draw_context(bi)];
    }
    if(bCanDraw == YES)
    {
        NSBezierPath* aPath = [NSBezierPath bezierPath];
#ifndef BUILDING_FOR_MOJAVE
        NSColor *_dw_fg_color = pthread_getspecific(_dw_fg_color_key);
#endif

        [_dw_fg_color set];

        [aPath moveToPoint:NSMakePoint(x1 + 0.5, y1 + 0.5)];
        [aPath lineToPoint:NSMakePoint(x2 + 0.5, y2 + 0.5)];
        [aPath stroke];
    }

    if(bi)
        [NSGraphicsContext restoreGraphicsState];
#ifndef BUILDING_FOR_MOJAVE
    if(bCanDraw == YES && !pixmap)
        [image unlockFocus];
#endif
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/* Draw text on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       x: X coordinate.
 *       y: Y coordinate.
 *       text: Text to be displayed.
 */
DW_FUNCTION_DEFINITION(dw_draw_text, void, HWND handle, HPIXMAP pixmap, int x, int y, const char *text)
DW_FUNCTION_ADD_PARAM5(handle, pixmap, x, y, text)
DW_FUNCTION_NO_RETURN(dw_draw_text)
DW_FUNCTION_RESTORE_PARAM5(handle, HWND, pixmap, HPIXMAP, x, int, y, int, text, const char *)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id image = handle;
    NSString *nstr = [ NSString stringWithUTF8String:text ];
    NSBitmapImageRep *bi = nil;
    NSFont *font = nil;
    DWRender *render;
    bool bCanDraw = YES;

    if(pixmap)
    {
        bi = (id)pixmap->image;
        font = pixmap->font;
        render = pixmap->handle;
        if(!font && [render isMemberOfClass:[DWRender class]])
        {
            font = [render font];
        }
    }
    else if(image && [image isMemberOfClass:[DWRender class]])
    {
        render = image;
        font = [render font];
#ifdef BUILDING_FOR_MOJAVE
        bi = [render cachedDrawingRep];
#else
        if((bCanDraw = [image lockFocusIfCanDraw]) == YES)
            _DWLastDrawable = handle;
#endif
    }
    if(bi)
    {
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:_dw_draw_context(bi)];
    }

    if(bCanDraw == YES)
    {
#ifndef BUILDING_FOR_MOJAVE
        NSColor *_dw_fg_color = pthread_getspecific(_dw_fg_color_key);
        NSColor *_dw_bg_color = pthread_getspecific(_dw_bg_color_key);
#endif
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:_dw_fg_color, NSForegroundColorAttributeName, nil];
        if(_dw_bg_color)
            [dict setValue:_dw_bg_color forKey:NSBackgroundColorAttributeName];
        if(font)
            [dict setValue:font forKey:NSFontAttributeName];
        [nstr drawAtPoint:NSMakePoint(x, y) withAttributes:dict];
        [dict release];
    }

    if(bi)
        [NSGraphicsContext restoreGraphicsState];
#ifndef BUILDING_FOR_MOJAVE
    if(bCanDraw == YES && !pixmap)
        [image unlockFocus];
#endif
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/* Query the width and height of a text string.
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       text: Text to be queried.
 *       width: Pointer to a variable to be filled in with the width.
 *       height Pointer to a variable to be filled in with the height.
 */
void API dw_font_text_extents_get(HWND handle, HPIXMAP pixmap, const char *text, int *width, int *height)
{
    id object = handle;
    NSString *nstr;
    NSFont *font = nil;
    DW_LOCAL_POOL_IN;

    nstr = [NSString stringWithUTF8String:text];

    /* Check the pixmap for associated object or font */
    if(pixmap)
    {
        object = pixmap->handle;
        font = pixmap->font;
    }
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    /* If we didn't get a font from the pixmap... try the associated object */
    if(!font && ([object isMemberOfClass:[DWRender class]] || [object isKindOfClass:[NSControl class]]))
    {
        font = [object font];
    }
    /* If we got a font... add it to the dictionary */
    if(font)
    {
        [dict setValue:font forKey:NSFontAttributeName];
    }
    /* Calculate the size of the string */
    NSSize size = [nstr sizeWithAttributes:dict];
    [dict release];
    /* Return whatever information we can */
    if(width)
    {
        *width = size.width;
    }
    if(height)
    {
        *height = size.height;
    }
    DW_LOCAL_POOL_OUT;
}

/* Internal function to create an image graphics context...
 * with or without antialiasing enabled.
 */
id _dw_create_gc(id image, bool antialiased)
{
#ifdef BUILDING_FOR_YOSEMITE
    CGContextRef  context = (CGContextRef)[[NSGraphicsContext graphicsContextWithBitmapImageRep:image] CGContext];
    NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithCGContext:context flipped:YES];
#else
    CGContextRef  context = (CGContextRef)[[NSGraphicsContext graphicsContextWithBitmapImageRep:image] graphicsPort];
    NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES];
#endif
    [gc setShouldAntialias:antialiased];
    CGContextSetAllowsAntialiasing(context, antialiased);
    return gc;
}

/* Draw a polygon on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       flags: DW_DRAW_FILL (1) to fill the polygon or DW_DRAW_DEFAULT (0).
 *       x: X coordinate.
 *       y: Y coordinate.
 *       width: Width of rectangle.
 *       height: Height of rectangle.
 */
DW_FUNCTION_DEFINITION(dw_draw_polygon, void, HWND handle, HPIXMAP pixmap, int flags, int npoints, int *x, int *y)
DW_FUNCTION_ADD_PARAM6(handle, pixmap, flags, npoints, x, y)
DW_FUNCTION_NO_RETURN(dw_draw_polygon)
DW_FUNCTION_RESTORE_PARAM6(handle, HWND, pixmap, HPIXMAP, flags, int, npoints, int, x, int *, y, int *)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id image = handle;
    NSBitmapImageRep *bi = nil;
    bool bCanDraw = YES;
    int z;

    if(pixmap)
        bi = (id)pixmap->image;
    else
    {
#ifdef BUILDING_FOR_MOJAVE
        if([image isMemberOfClass:[DWRender class]])
        {
            DWRender *render = image;

            bi = [render cachedDrawingRep];
        }
#else
        if((bCanDraw = [image lockFocusIfCanDraw]) == YES)
        {
            _DWLastDrawable = handle;
            [[NSGraphicsContext currentContext] setShouldAntialias:(flags & DW_DRAW_NOAA ? NO : YES)];
        }
#endif
    }
    if(bi)
    {
        id gc = _dw_create_gc(bi, flags & DW_DRAW_NOAA ? NO : YES);
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:gc];
    }

    if(bCanDraw == YES)
    {
        NSBezierPath* aPath = [NSBezierPath bezierPath];
#ifndef BUILDING_FOR_MOJAVE
        NSColor *_dw_fg_color = pthread_getspecific(_dw_fg_color_key);
#endif

        [_dw_fg_color set];

        [aPath moveToPoint:NSMakePoint(*x + 0.5, *y + 0.5)];
        for(z=1;z<npoints;z++)
        {
            [aPath lineToPoint:NSMakePoint(x[z] + 0.5, y[z] + 0.5)];
        }
        [aPath closePath];
        if(flags & DW_DRAW_FILL)
        {
            [aPath fill];
        }
        [aPath stroke];
    }

    if(bi)
        [NSGraphicsContext restoreGraphicsState];
#ifndef BUILDING_FOR_MOJAVE
    if(bCanDraw == YES && !pixmap)
        [image unlockFocus];
#endif
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/* Draw a rectangle on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       flags: DW_DRAW_FILL (1) to fill the box or DW_DRAW_DEFAULT (0).
 *       x: X coordinate.
 *       y: Y coordinate.
 *       width: Width of rectangle.
 *       height: Height of rectangle.
 */
DW_FUNCTION_DEFINITION(dw_draw_rect, void, HWND handle, HPIXMAP pixmap, int flags, int x, int y, int width, int height)
DW_FUNCTION_ADD_PARAM7(handle, pixmap, flags, x, y, width, height)
DW_FUNCTION_NO_RETURN(dw_draw_rect)
DW_FUNCTION_RESTORE_PARAM7(handle, HWND, pixmap, HPIXMAP, flags, int, x, int, y, int, width, int, height, int)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id image = handle;
    NSBitmapImageRep *bi = nil;
    bool bCanDraw = YES;

    if(pixmap)
        bi = (id)pixmap->image;
    else
    {
#ifdef BUILDING_FOR_MOJAVE
        if([image isMemberOfClass:[DWRender class]])
        {
            DWRender *render = image;

            bi = [render cachedDrawingRep];
        }
#else
        if((bCanDraw = [image lockFocusIfCanDraw]) == YES)
        {
            _DWLastDrawable = handle;
            [[NSGraphicsContext currentContext] setShouldAntialias:(flags & DW_DRAW_NOAA ? NO : YES)];
        }
#endif
    }
    if(bi)
    {
        id gc = _dw_create_gc(bi, flags & DW_DRAW_NOAA ? NO : YES);
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:gc];
    }

    if(bCanDraw == YES)
    {
#ifndef BUILDING_FOR_MOJAVE
        NSColor *_dw_fg_color = pthread_getspecific(_dw_fg_color_key);
#endif

        [_dw_fg_color set];

        if(flags & DW_DRAW_FILL)
            [NSBezierPath fillRect:NSMakeRect(x, y, width, height)];
        else
            [NSBezierPath strokeRect:NSMakeRect(x, y, width, height)];
    }

    if(bi)
        [NSGraphicsContext restoreGraphicsState];
#ifndef BUILDING_FOR_MOJAVE
    if(bCanDraw == YES && !pixmap)
        [image unlockFocus];
#endif
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/* Draw an arc on a window (preferably a render window).
 * Parameters:
 *       handle: Handle to the window.
 *       pixmap: Handle to the pixmap. (choose only one of these)
 *       flags: DW_DRAW_FILL (1) to fill the arc or DW_DRAW_DEFAULT (0).
 *              DW_DRAW_FULL will draw a complete circle/elipse.
 *       xorigin: X coordinate of center of arc.
 *       yorigin: Y coordinate of center of arc.
 *       x1: X coordinate of first segment of arc.
 *       y1: Y coordinate of first segment of arc.
 *       x2: X coordinate of second segment of arc.
 *       y2: Y coordinate of second segment of arc.
 */
DW_FUNCTION_DEFINITION(dw_draw_arc, void, HWND handle, HPIXMAP pixmap, int flags, int xorigin, int yorigin, int x1, int y1, int x2, int y2)
DW_FUNCTION_ADD_PARAM9(handle, pixmap, flags, xorigin, yorigin, x1, y1, x2, y2)
DW_FUNCTION_NO_RETURN(dw_draw_arc)
DW_FUNCTION_RESTORE_PARAM9(handle, HWND, pixmap, HPIXMAP, flags, int, xorigin, int, yorigin, int, x1, int, y1, int, x2, int, y2, int)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id image = handle;
    NSBitmapImageRep *bi = nil;
    bool bCanDraw = YES;

    if(pixmap)
        bi = (id)pixmap->image;
    else
    {
#ifdef BUILDING_FOR_MOJAVE
        if([image isMemberOfClass:[DWRender class]])
        {
            DWRender *render = image;

            bi = [render cachedDrawingRep];
        }
#else
        if((bCanDraw = [image lockFocusIfCanDraw]) == YES)
        {
            _DWLastDrawable = handle;
            [[NSGraphicsContext currentContext] setShouldAntialias:(flags & DW_DRAW_NOAA ? NO : YES)];
        }
#endif
    }
    if(bi)
    {
        id gc = _dw_create_gc(bi, flags & DW_DRAW_NOAA ? NO : YES);
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:gc];
    }

    if(bCanDraw)
    {
        NSBezierPath* aPath = [NSBezierPath bezierPath];
#ifndef BUILDING_FOR_MOJAVE
        NSColor *_dw_fg_color = pthread_getspecific(_dw_fg_color_key);
#endif

        [_dw_fg_color set];

        /* Special case of a full circle/oval */
        if(flags & DW_DRAW_FULL)
        {
            [aPath appendBezierPathWithOvalInRect:NSMakeRect(x1, y1, x2 - x1, y2 - y1)];
        }
        else
        {
            double a1 = atan2((y1-yorigin), (x1-xorigin));
            double a2 = atan2((y2-yorigin), (x2-xorigin));
            double dx = xorigin - x1;
            double dy = yorigin - y1;
            double r = sqrt(dx*dx + dy*dy);

            /* Convert to degrees */
            a1 *= (180.0 / M_PI);
            a2 *= (180.0 / M_PI);

            /* Prepare to draw */
            [aPath appendBezierPathWithArcWithCenter:NSMakePoint(xorigin, yorigin)
                                              radius:r startAngle:a1 endAngle:a2];
        }
        /* If the fill flag is passed */
        if(flags & DW_DRAW_FILL)
        {
            [aPath fill];
        }
        /* Actually do the drawing */
        [aPath stroke];
    }

    if(bi)
        [NSGraphicsContext restoreGraphicsState];
#ifndef BUILDING_FOR_MOJAVE
    if(bCanDraw == YES && !pixmap)
        [image unlockFocus];
#endif
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Create a tree object to be packed.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
DW_FUNCTION_DEFINITION(dw_tree_new, HWND, ULONG cid)
DW_FUNCTION_ADD_PARAM1(cid)
DW_FUNCTION_RETURN(dw_tree_new, HWND)
DW_FUNCTION_RESTORE_PARAM1(cid, ULONG)
{
    DW_FUNCTION_INIT;
    NSScrollView *scrollview  = [[[NSScrollView alloc] init] retain];
    DWTree *tree = [[DWTree alloc] init];

    [tree setScrollview:scrollview];
    [scrollview setBorderType:NSBezelBorder];
    [scrollview setHasVerticalScroller:YES];
    [scrollview setAutohidesScrollers:YES];

    [tree setAllowsMultipleSelection:NO];
    [tree setDataSource:tree];
    [tree setDelegate:tree];
    [scrollview setDocumentView:tree];
    [tree setHeaderView:nil];
    [tree setTag:cid];
    [tree autorelease];
    DW_FUNCTION_RETURN_THIS(tree);
}

/*
 * Inserts an item into a tree window (widget) after another item.
 * Parameters:
 *          handle: Handle to the tree to be inserted.
 *          item: Handle to the item to be positioned after.
 *          title: The text title of the entry.
 *          icon: Handle to coresponding icon.
 *          parent: Parent handle or 0 if root.
 *          itemdata: Item specific data.
 */
DW_FUNCTION_DEFINITION(dw_tree_insert_after, HTREEITEM, HWND handle, HTREEITEM item, const char *title, HICN icon, HTREEITEM parent, void *itemdata)
DW_FUNCTION_ADD_PARAM6(handle, item, title, icon, parent, itemdata)
DW_FUNCTION_RETURN(dw_tree_insert_after, HTREEITEM)
DW_FUNCTION_RESTORE_PARAM6(handle, HWND, item, HTREEITEM, title, char *, icon, HICN, parent, HTREEITEM, itemdata, void *)
{
    DW_FUNCTION_INIT;
    DWTree *tree = handle;
    NSString *nstr = [[NSString stringWithUTF8String:title] retain];
    NSMutableArray *treenode = [[[NSMutableArray alloc] init] retain];
    if(icon)
        [treenode addObject:icon];
    else
        [treenode addObject:[NSNull null]];
    [treenode addObject:nstr];
    [treenode addObject:[NSValue valueWithPointer:itemdata]];
    [treenode addObject:[NSNull null]];
    [tree addTree:treenode and:parent after:item];
    if(parent)
        [tree reloadItem:parent reloadChildren:YES];
    else
        [tree reloadData];
    DW_FUNCTION_RETURN_THIS(treenode);
}

/*
 * Inserts an item into a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree to be inserted.
 *          title: The text title of the entry.
 *          icon: Handle to coresponding icon.
 *          parent: Parent handle or 0 if root.
 *          itemdata: Item specific data.
 */
HTREEITEM API dw_tree_insert(HWND handle, const char *title, HICN icon, HTREEITEM parent, void *itemdata)
{
    return dw_tree_insert_after(handle, NULL, title, icon, parent, itemdata);
}

/*
 * Gets the text an item in a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 */
DW_FUNCTION_DEFINITION(dw_tree_get_title, char *, HWND handle, HTREEITEM item)
DW_FUNCTION_ADD_PARAM2(handle, item)
DW_FUNCTION_RETURN(dw_tree_get_title, char *)
DW_FUNCTION_RESTORE_PARAM2(DW_UNUSED(handle), HWND, item, HTREEITEM)
{
    DW_FUNCTION_INIT;
    char *retval = NULL;
    NSMutableArray *array = (NSMutableArray *)item;
    NSString *nstr = (NSString *)[array objectAtIndex:1];
    retval = strdup([nstr UTF8String]);
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Gets the text an item in a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 */
DW_FUNCTION_DEFINITION(dw_tree_get_parent, HTREEITEM, HWND handle, HTREEITEM item)
DW_FUNCTION_ADD_PARAM2(handle, item)
DW_FUNCTION_RETURN(dw_tree_get_parent, HTREEITEM)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, item, HTREEITEM)
{
    DW_FUNCTION_INIT;
    HTREEITEM parent;
    DWTree *tree = handle;

    parent = [tree parentForItem:item];
    DW_FUNCTION_RETURN_THIS(parent);
}

/*
 * Sets the text and icon of an item in a tree window (widget).
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 *          title: The text title of the entry.
 *          icon: Handle to coresponding icon.
 */
DW_FUNCTION_DEFINITION(dw_tree_item_change, void, HWND handle, HTREEITEM item, const char *title, HICN icon)
DW_FUNCTION_ADD_PARAM4(handle, item, title, icon)
DW_FUNCTION_NO_RETURN(dw_tree_item_change)
DW_FUNCTION_RESTORE_PARAM4(handle, HWND, item, HTREEITEM, title, char *, icon, HICN)
{
    DW_FUNCTION_INIT;
    DWTree *tree = handle;
    NSMutableArray *array = (NSMutableArray *)item;
    DW_LOCAL_POOL_IN;

    if(title)
    {
        NSString *oldstr = [array objectAtIndex:1];
        NSString *nstr = [[NSString stringWithUTF8String:title] retain];
        [array replaceObjectAtIndex:1 withObject:nstr];
        [oldstr release];
    }
    if(icon)
    {
        [array replaceObjectAtIndex:0 withObject:icon];
    }
#ifdef BUILDING_FOR_SNOW_LEOPARD
    NSInteger row = [tree rowForItem:item];
    [tree reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:row]
                    columnIndexes:[NSIndexSet indexSetWithIndex:0]];
#else
    [tree reloadData];
#endif
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the item data of a tree item.
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 *          itemdata: User defined data to be associated with item.
 */
DW_FUNCTION_DEFINITION(dw_tree_item_set_data, void, HWND handle, HTREEITEM item, void *itemdata)
DW_FUNCTION_ADD_PARAM3(handle, item, itemdata)
DW_FUNCTION_NO_RETURN(dw_tree_item_set_data)
DW_FUNCTION_RESTORE_PARAM3(DW_UNUSED(handle), HWND, item, HTREEITEM, itemdata, void *)
{
    DW_FUNCTION_INIT;
    NSMutableArray *array = (NSMutableArray *)item;
    [array replaceObjectAtIndex:2 withObject:[NSValue valueWithPointer:itemdata]];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Gets the item data of a tree item.
 * Parameters:
 *          handle: Handle to the tree containing the item.
 *          item: Handle of the item to be modified.
 */
DW_FUNCTION_DEFINITION(dw_tree_item_get_data, void *, HWND handle, HTREEITEM item)
DW_FUNCTION_ADD_PARAM2(handle, item)
DW_FUNCTION_RETURN(dw_tree_item_get_data, void *)
DW_FUNCTION_RESTORE_PARAM2(DW_UNUSED(handle), HWND, item, HTREEITEM)
{
    DW_FUNCTION_INIT;
    void *result = NULL;
    NSMutableArray *array = (NSMutableArray *)item;
    NSValue *value = [array objectAtIndex:2];
    if(value)
        result = [value pointerValue];
    DW_FUNCTION_RETURN_THIS(result);
}

/*
 * Sets this item as the active selection.
 * Parameters:
 *       handle: Handle to the tree window (widget) to be selected.
 *       item: Handle to the item to be selected.
 */
DW_FUNCTION_DEFINITION(dw_tree_item_select, void, HWND handle, HTREEITEM item)
DW_FUNCTION_ADD_PARAM2(handle, item)
DW_FUNCTION_NO_RETURN(dw_tree_item_select)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, item, HTREEITEM)
{
    DW_FUNCTION_INIT;
    DWTree *tree = handle;
    NSInteger itemIndex = [tree rowForItem:item];
    if(itemIndex > -1)
    {
        [tree selectRowIndexes:[NSIndexSet indexSetWithIndex:itemIndex] byExtendingSelection:NO];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Removes all nodes from a tree.
 * Parameters:
 *       handle: Handle to the window (widget) to be cleared.
 */
DW_FUNCTION_DEFINITION(dw_tree_clear, void, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_NO_RETURN(dw_tree_clear)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    DWTree *tree = handle;
    [tree clear];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Expands a node on a tree.
 * Parameters:
 *       handle: Handle to the tree window (widget).
 *       item: Handle to node to be expanded.
 */
DW_FUNCTION_DEFINITION(dw_tree_item_expand, void, HWND handle, HTREEITEM item)
DW_FUNCTION_ADD_PARAM2(handle, item)
DW_FUNCTION_NO_RETURN(dw_tree_item_expand)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, item, HTREEITEM)
{
    DW_FUNCTION_INIT;
    DWTree *tree = handle;
    [tree expandItem:item];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Collapses a node on a tree.
 * Parameters:
 *       handle: Handle to the tree window (widget).
 *       item: Handle to node to be collapsed.
 */
DW_FUNCTION_DEFINITION(dw_tree_item_collapse, void, HWND handle, HTREEITEM item)
DW_FUNCTION_ADD_PARAM2(handle, item)
DW_FUNCTION_NO_RETURN(dw_tree_item_collapse)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, item, HTREEITEM)
{
    DW_FUNCTION_INIT;
    DWTree *tree = handle;
    [tree collapseItem:item];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Removes a node from a tree.
 * Parameters:
 *       handle: Handle to the window (widget) to be cleared.
 *       item: Handle to node to be deleted.
 */
DW_FUNCTION_DEFINITION(dw_tree_item_delete, void, HWND handle, HTREEITEM item)
DW_FUNCTION_ADD_PARAM2(handle, item)
DW_FUNCTION_NO_RETURN(dw_tree_item_delete)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, item, HTREEITEM)
{
    DW_FUNCTION_INIT;
    DWTree *tree = handle;
    [tree deleteNode:item];
    [tree reloadData];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Create a container object to be packed.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
DW_FUNCTION_DEFINITION(dw_container_new, HWND, ULONG cid, int multi)
DW_FUNCTION_ADD_PARAM2(cid, multi)
DW_FUNCTION_RETURN(dw_container_new, HWND)
DW_FUNCTION_RESTORE_PARAM2(cid, ULONG, multi, int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = _dw_cont_new(cid, multi);
    NSScrollView *scrollview = [cont scrollview];
    [scrollview setHasHorizontalScroller:YES];
    NSTableHeaderView *header = [[[NSTableHeaderView alloc] init] autorelease];
    [cont setHeaderView:header];
    [cont setTarget:cont];
    [cont setDoubleAction:@selector(doubleClicked:)];
    DW_FUNCTION_RETURN_THIS(cont);
}

/*
 * Sets up the container columns.
 * Parameters:
 *          handle: Handle to the container to be configured.
 *          flags: An array of unsigned longs with column flags.
 *          titles: An array of strings with column text titles.
 *          count: The number of columns (this should match the arrays).
 *          separator: The column number that contains the main separator.
 *                     (this item may only be used in OS/2)
 */
DW_FUNCTION_DEFINITION(dw_container_setup, int, HWND handle, unsigned long *flags, char **titles, int count, int separator)
DW_FUNCTION_ADD_PARAM5(handle, flags, titles, count, separator)
DW_FUNCTION_RETURN(dw_container_setup, int)
DW_FUNCTION_RESTORE_PARAM5(handle, HWND, flags, unsigned long *, titles, char **, count, int, DW_UNUSED(separator), int)
{
    DW_FUNCTION_INIT;
    int z, retval = DW_ERROR_NONE;
    DWContainer *cont = handle;

    [cont setup];

    for(z=0;z<count;z++)
    {
        NSString *title = [NSString stringWithUTF8String:titles[z]];
        NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:title];
#ifdef BUILDING_FOR_YOSEMITE
        [column setTitle:title];
#else
        [[column headerCell] setStringValue:title];
#endif
#ifndef DW_USE_NSVIEW
        if(flags[z] & DW_CFA_BITMAPORICON)
        {
            NSImageCell *imagecell = [[NSImageCell alloc] init];
            [column setDataCell:imagecell];
            [imagecell release];
        }
        else if(flags[z] & DW_CFA_STRINGANDICON)
        {
            DWImageAndTextCell *browsercell = [[DWImageAndTextCell alloc] init];
            [column setDataCell:browsercell];
            [browsercell release];
        }
#endif
        /* Defaults to left justified so just handle right and center */
        if(flags[z] & DW_CFA_RIGHT)
        {
#ifndef DW_USE_NSVIEW
            [(NSCell *)[column dataCell] setAlignment:DWTextAlignmentRight];
#endif
            [(NSCell *)[column headerCell] setAlignment:DWTextAlignmentRight];
        }
        else if(flags[z] & DW_CFA_CENTER)
        {
#ifndef DW_USE_NSVIEW
            [(NSCell *)[column dataCell] setAlignment:DWTextAlignmentCenter];
#endif
            [(NSCell *)[column headerCell] setAlignment:DWTextAlignmentCenter];
        }
        [column setEditable:NO];
        [cont addTableColumn:column];
        [cont addColumn:column andType:(int)flags[z]];
        [column release];
    }
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Configures the main filesystem columnn title for localization.
 * Parameters:
 *          handle: Handle to the container to be configured.
 *          title: The title to be displayed in the main column.
 */
void API dw_filesystem_set_column_title(HWND handle, const char *title)
{
	char *newtitle = strdup(title ? title : "");
	
	dw_window_set_data(handle, "_dw_coltitle", newtitle);
}

/*
 * Sets up the filesystem columns, note: filesystem always has an icon/filename field.
 * Parameters:
 *          handle: Handle to the container to be configured.
 *          flags: An array of unsigned longs with column flags.
 *          titles: An array of strings with column text titles.
 *          count: The number of columns (this should match the arrays).
 */
int API dw_filesystem_setup(HWND handle, unsigned long *flags, char **titles, int count)
{
    char **newtitles = malloc(sizeof(char *) * (count + 1));
    unsigned long *newflags = malloc(sizeof(unsigned long) * (count + 1));
    char *coltitle = (char *)dw_window_get_data(handle, "_dw_coltitle");
    DWContainer *cont = handle;

    newtitles[0] = coltitle ? coltitle : "Filename";

    newflags[0] = DW_CFA_STRINGANDICON | DW_CFA_LEFT | DW_CFA_HORZSEPARATOR;

    memcpy(&newtitles[1], titles, sizeof(char *) * count);
    memcpy(&newflags[1], flags, sizeof(unsigned long) * count);

    dw_container_setup(handle, newflags, newtitles, count + 1, 0);
    [cont setFilesystem:YES];

    if(coltitle)
    {
        dw_window_set_data(handle, "_dw_coltitle", NULL);
        free(coltitle);
    }
    free(newtitles);
    free(newflags);
    return DW_ERROR_NONE;
}

/*
 * Allocates memory used to populate a container.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          rowcount: The number of items to be populated.
 */
DW_FUNCTION_DEFINITION(dw_container_alloc, void *, HWND handle, int rowcount)
DW_FUNCTION_ADD_PARAM2(handle, rowcount)
DW_FUNCTION_RETURN(dw_container_alloc, void *)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, rowcount, int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    [cont addRows:rowcount];
    DW_FUNCTION_RETURN_THIS(cont);
}

/*
 * Sets an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
DW_FUNCTION_DEFINITION(dw_container_set_item, void, HWND handle, void *pointer, int column, int row, void *data)
DW_FUNCTION_ADD_PARAM5(handle, pointer, column, row, data)
DW_FUNCTION_NO_RETURN(dw_container_set_item)
DW_FUNCTION_RESTORE_PARAM5(handle, HWND, pointer, void *, column, int, row, int, data, void *)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    id icon = nil, text = nil;
    int type = [cont cellType:column];
    int lastadd = 0;

    /* If pointer is NULL we are getting a change request instead of set */
    if(pointer)
        lastadd = [cont lastAddPoint];

    if(data)
    {
        if(type & DW_CFA_BITMAPORICON)
        {
            icon = *((NSImage **)data);
        }
        else if(type & DW_CFA_STRING)
        {
            char *str = *((char **)data);
            text = [ NSString stringWithUTF8String:str ];
        }
        else
        {
            char textbuffer[101] = {0};

            if(type & DW_CFA_ULONG)
            {
                ULONG tmp = *((ULONG *)data);

                snprintf(textbuffer, 100, "%lu", tmp);
            }
            else if(type & DW_CFA_DATE)
            {
                struct tm curtm;
                CDATE cdate = *((CDATE *)data);

                memset( &curtm, 0, sizeof(curtm) );
                curtm.tm_mday = cdate.day;
                curtm.tm_mon = cdate.month - 1;
                curtm.tm_year = cdate.year - 1900;

                strftime(textbuffer, 100, "%x", &curtm);
            }
            else if(type & DW_CFA_TIME)
            {
                struct tm curtm;
                CTIME ctime = *((CTIME *)data);

                memset( &curtm, 0, sizeof(curtm) );
                curtm.tm_hour = ctime.hours;
                curtm.tm_min = ctime.minutes;
                curtm.tm_sec = ctime.seconds;

                strftime(textbuffer, 100, "%X", &curtm);
            }
            if(textbuffer[0])
                text = [ NSString stringWithUTF8String:textbuffer ];
        }
    }

#ifdef DW_USE_NSVIEW
    id object = [cont getRow:(row+lastadd) and:column];
    
    /* If it is a cell, change the content of the cell */
    if([object isMemberOfClass:[NSTableCellView class]])
    {
        NSTableCellView *cell = object;
        if(icon)
            [[cell imageView] setImage:icon];
        else
            [[cell textField] setStringValue:text];
    }
    else /* Otherwise replace it with a new cell */
        [cont editCell:_dw_table_cell_view_new(icon, text) at:(row+lastadd) and:column];
#else
    [cont editCell:(icon ? icon : text) at:(row+lastadd) and:column];
#endif
    [cont setNeedsDisplay:YES];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Changes an existing item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_container_change_item(HWND handle, int column, int row, void *data)
{
    dw_container_set_item(handle, NULL, column, row, data);
}

/*
 * Changes an existing item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_filesystem_change_item(HWND handle, int column, int row, void *data)
{
    dw_container_change_item(handle, column+1, row, data);
}

/*
 * Changes an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_filesystem_change_file(HWND handle, int row, const char *filename, HICN icon)
{
    dw_filesystem_set_file(handle, NULL, row, filename, icon);
}

/*
 * Sets an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
DW_FUNCTION_DEFINITION(dw_filesystem_set_file, void, HWND handle, void *pointer, int row, const char *filename, HICN icon)
DW_FUNCTION_ADD_PARAM5(handle, pointer, row, filename, icon)
DW_FUNCTION_NO_RETURN(dw_filesystem_set_file)
DW_FUNCTION_RESTORE_PARAM5(handle, HWND, pointer, void *, row, int, filename, char *, icon, HICN)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    NSString *text = filename ? [NSString stringWithUTF8String:filename] : nil;
    int lastadd = 0;

    /* If pointer is NULL we are getting a change request instead of set */
    if(pointer)
        lastadd = [cont lastAddPoint];

#ifdef DW_USE_NSVIEW
    id object = [cont getRow:(row+lastadd) and:0];
    
    /* If it is a cell, change the content of the cell */
    if([object isMemberOfClass:[NSTableCellView class]])
    {
        NSTableCellView *cell = object;
        
        if(icon)
            [[cell imageView] setImage:icon];
        if(text)
            [[cell textField] setStringValue:text];
    }
    else /* Otherwise replace it with a new cell */
        [cont editCell:_dw_table_cell_view_new(icon, text) at:(row+lastadd) and:0];
#else
    DWImageAndTextCell *browsercell = [[[DWImageAndTextCell alloc] init] autorelease];
    [browsercell setImage:icon];
    [browsercell setStringValue:text];
    [cont editCell:browsercell at:(row+lastadd) and:0];
#endif
    [cont setNeedsDisplay:YES];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets an item in specified row and column to the given data.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          column: Zero based column of data being set.
 *          row: Zero based row of data being set.
 *          data: Pointer to the data to be added.
 */
void API dw_filesystem_set_item(HWND handle, void *pointer, int column, int row, void *data)
{
    dw_container_set_item(handle, pointer, column+1, row, data);
}

/*
 * Gets column type for a container column
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column.
 */
DW_FUNCTION_DEFINITION(dw_container_get_column_type, int, HWND handle, int column)
DW_FUNCTION_ADD_PARAM2(handle, column)
DW_FUNCTION_RETURN(dw_container_get_column_type, int)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, column, int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    int rc;
    int flag = [cont cellType:column];
    if(flag & DW_CFA_BITMAPORICON)
        rc = DW_CFA_BITMAPORICON;
    else if(flag & DW_CFA_STRING)
        rc = DW_CFA_STRING;
    else if(flag & DW_CFA_ULONG)
        rc = DW_CFA_ULONG;
    else if(flag & DW_CFA_DATE)
        rc = DW_CFA_DATE;
    else if(flag & DW_CFA_TIME)
        rc = DW_CFA_TIME;
    else
        rc = 0;
    DW_FUNCTION_RETURN_THIS(rc);
}

/*
 * Gets column type for a filesystem container column
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          column: Zero based column.
 */
int API dw_filesystem_get_column_type(HWND handle, int column)
{
    return dw_container_get_column_type(handle, column+1);
}

/*
 * Sets the alternating row colors for container window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          oddcolor: Odd row background color in DW_RGB format or a default color index.
 *          evencolor: Even row background color in DW_RGB format or a default color index.
 *                    DW_RGB_TRANSPARENT will disable coloring rows.
 *                    DW_CLR_DEFAULT will use the system default alternating row colors.
 */
DW_FUNCTION_DEFINITION(dw_container_set_stripe, void, HWND handle, unsigned long oddcolor, unsigned long evencolor)
DW_FUNCTION_ADD_PARAM3(handle, oddcolor, evencolor)
DW_FUNCTION_NO_RETURN(dw_container_set_stripe)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, oddcolor, unsigned long, evencolor, unsigned long)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    [cont setRowBgOdd:oddcolor
              andEven:evencolor];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the width of a column in the container.
 * Parameters:
 *          handle: Handle to window (widget) of container.
 *          column: Zero based column of width being set.
 *          width: Width of column in pixels.
 */
DW_FUNCTION_DEFINITION(dw_container_set_column_width, void, HWND handle, int column, int width)
DW_FUNCTION_ADD_PARAM3(handle, column, width)
DW_FUNCTION_NO_RETURN(dw_container_set_column_width)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, column, int, width, int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    if([cont filesystem])
        column++;
    NSTableColumn *col = [cont getColumn:column];

    [col setWidth:width];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the title of a row in the container.
 * Parameters:
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          row: Zero based row of data being set.
 *          title: String title of the item.
 */
DW_FUNCTION_DEFINITION(dw_container_set_row_title, void, void *pointer, int row, const char *title)
DW_FUNCTION_ADD_PARAM3(pointer, row, title)
DW_FUNCTION_NO_RETURN(dw_container_set_row_title)
DW_FUNCTION_RESTORE_PARAM3(pointer, void *, row, int, title, char *)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = pointer;
    int lastadd = [cont lastAddPoint];
    [cont setRow:(row+lastadd) title:title];
    DW_FUNCTION_RETURN_NOTHING;
}


/*
 * Sets the data pointer of a row in the container.
 * Parameters:
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          row: Zero based row of data being set.
 *          data: Data pointer.
 */
DW_FUNCTION_DEFINITION(dw_container_set_row_data, void, void *pointer, int row, void *data)
DW_FUNCTION_ADD_PARAM3(pointer, row, data)
DW_FUNCTION_NO_RETURN(dw_container_set_row_data)
DW_FUNCTION_RESTORE_PARAM3(pointer, void *, row, int, data, void *)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = pointer;
    int lastadd = [cont lastAddPoint];
    [cont setRowData:(row+lastadd) title:data];
    DW_FUNCTION_RETURN_NOTHING;
}


/*
 * Sets the title of a row in the container.
 * Parameters:
 *          handle: Handle to window (widget) of container.
 *          row: Zero based row of data being set.
 *          title: String title of the item.
 */
DW_FUNCTION_DEFINITION(dw_container_change_row_title, void, HWND handle, int row, const char *title)
DW_FUNCTION_ADD_PARAM3(handle, row, title)
DW_FUNCTION_NO_RETURN(dw_container_change_row_title)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, row, int, title, char *)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    [cont setRow:row title:title];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the data pointer of a row in the container.
 * Parameters:
 *          handle: Handle to window (widget) of container.
 *          row: Zero based row of data being set.
 *          data: Data pointer.
 */
DW_FUNCTION_DEFINITION(dw_container_change_row_data, void, HWND handle, int row, void *data)
DW_FUNCTION_ADD_PARAM3(handle, row, data)
DW_FUNCTION_NO_RETURN(dw_container_change_row_data)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, row, int, data, void *)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    [cont setRowData:row title:data];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the title of a row in the container.
 * Parameters:
 *          handle: Handle to the container window (widget).
 *          pointer: Pointer to the allocated memory in dw_container_alloc().
 *          rowcount: The number of rows to be inserted.
 */
DW_FUNCTION_DEFINITION(dw_container_insert, void, HWND handle, void *pointer, int rowcount)
DW_FUNCTION_ADD_PARAM3(handle, pointer, rowcount)
DW_FUNCTION_NO_RETURN(dw_container_insert)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, DW_UNUSED(pointer), void *, DW_UNUSED(rowcount), int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    [cont reloadData];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Removes all rows from a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be cleared.
 *       redraw: TRUE to cause the container to redraw immediately.
 */
DW_FUNCTION_DEFINITION(dw_container_clear, void, HWND handle, int redraw)
DW_FUNCTION_ADD_PARAM2(handle, redraw)
DW_FUNCTION_NO_RETURN(dw_container_clear)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, redraw, int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    [cont clear];
    if(redraw)
        [cont reloadData];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Removes the first x rows from a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be deleted from.
 *       rowcount: The number of rows to be deleted.
 */
DW_FUNCTION_DEFINITION(dw_container_delete, void, HWND handle, int rowcount)
DW_FUNCTION_ADD_PARAM2(handle, rowcount)
DW_FUNCTION_NO_RETURN(dw_container_delete)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, rowcount, int)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    int x;

    for(x=0;x<rowcount;x++)
    {
        [cont removeRow:0];
    }
    [cont reloadData];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Scrolls container up or down.
 * Parameters:
 *       handle: Handle to the window (widget) to be scrolled.
 *       direction: DW_SCROLL_UP, DW_SCROLL_DOWN, DW_SCROLL_TOP or
 *                  DW_SCROLL_BOTTOM. (rows is ignored for last two)
 *       rows: The number of rows to be scrolled.
 */
DW_FUNCTION_DEFINITION(dw_container_scroll, void, HWND handle, int direction, long rows)
DW_FUNCTION_ADD_PARAM3(handle, direction, rows)
DW_FUNCTION_NO_RETURN(dw_container_scroll)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, direction, int, rows, long)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    NSScrollView *sv = [cont scrollview];
    NSScroller *scrollbar = [sv verticalScroller];
    int rowcount = (int)[cont numberOfRowsInTableView:cont];
    float currpos = [scrollbar floatValue];
    float change;

    /* Safety check */
    if(rowcount < 1)
    {
        return;
    }

    change = (float)rows/(float)rowcount;

    switch(direction)
    {
        case DW_SCROLL_TOP:
        {
            [scrollbar setFloatValue:0];
            break;
        }
        case DW_SCROLL_BOTTOM:
        {
            [scrollbar setFloatValue:1];
            break;
        }
        case DW_SCROLL_UP:
        {
            float newpos = currpos - change;
            if(newpos < 0)
            {
                newpos = 0;
            }
            [scrollbar setFloatValue:newpos];
            break;
        }
        case DW_SCROLL_DOWN:
        {
            float newpos = currpos + change;
            if(newpos > 1)
            {
                newpos = 1;
            }
            [scrollbar setFloatValue:newpos];
            break;
        }
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Starts a new query of a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       flags: If this parameter is DW_CRA_SELECTED it will only
 *              return items that are currently selected.  Otherwise
 *              it will return all records in the container.
 */
DW_FUNCTION_DEFINITION(dw_container_query_start, char *, HWND handle, unsigned long flags)
DW_FUNCTION_ADD_PARAM2(handle, flags)
DW_FUNCTION_RETURN(dw_container_query_start, char *)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, flags, unsigned long)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    NSIndexSet *selected = [cont selectedRowIndexes];
    NSUInteger result = [selected indexGreaterThanOrEqualToIndex:0];
    void *retval = NULL;

    if(result != NSNotFound)
    {
        if(flags & DW_CR_RETDATA)
            retval = [cont getRowData:(int)result];
        else
        {
            char *temp = [cont getRowTitle:(int)result];
            if(temp)
               retval = strdup(temp);
        }
        [cont setLastQueryPoint:(int)result];
    }
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Continues an existing query of a container.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       flags: If this parameter is DW_CRA_SELECTED it will only
 *              return items that are currently selected.  Otherwise
 *              it will return all records in the container.
 */
DW_FUNCTION_DEFINITION(dw_container_query_next, char *, HWND handle, unsigned long flags)
DW_FUNCTION_ADD_PARAM2(handle, flags)
DW_FUNCTION_RETURN(dw_container_query_next, char *)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, flags, unsigned long)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    int lastQueryPoint = [cont lastQueryPoint];
    NSIndexSet *selected = [cont selectedRowIndexes];
    NSUInteger result = [selected indexGreaterThanIndex:lastQueryPoint];
    void *retval = NULL;

    if(result != NSNotFound)
    {
        if(flags & DW_CR_RETDATA)
            retval = [cont getRowData:(int)result];
        else
        {
            char *temp = [cont getRowTitle:(int)result];
            if(temp)
               retval = strdup(temp);
        }
        [cont setLastQueryPoint:(int)result];
    }
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Cursors the item with the text speficied, and scrolls to that item.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       text:  Text usually returned by dw_container_query().
 */
DW_FUNCTION_DEFINITION(dw_container_cursor, void, HWND handle, const char *text)
DW_FUNCTION_ADD_PARAM2(handle, text)
DW_FUNCTION_NO_RETURN(dw_container_cursor)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, text, char *)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    DWContainer *cont = handle;
    char *thistext;
    int x, count = (int)[cont numberOfRowsInTableView:cont];

    for(x=0;x<count;x++)
    {
        thistext = [cont getRowTitle:x];

        if(thistext && strcmp(thistext, text) == 0)
        {
            NSIndexSet *selected = [[NSIndexSet alloc] initWithIndex:(NSUInteger)x];

            [cont selectRowIndexes:selected byExtendingSelection:YES];
            [selected release];
            [cont scrollRowToVisible:x];
            x=count;
            break;
        }
    }
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Cursors the item with the data speficied, and scrolls to that item.
 * Parameters:
 *       handle: Handle to the window (widget) to be queried.
 *       data: Data associated with the row.
 */
DW_FUNCTION_DEFINITION(dw_container_cursor_by_data, void, HWND handle, void *data)
DW_FUNCTION_ADD_PARAM2(handle, data)
DW_FUNCTION_NO_RETURN(dw_container_cursor_by_data)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, data, void *)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    DWContainer *cont = handle;
    void *thisdata;
    int x, count = (int)[cont numberOfRowsInTableView:cont];

    for(x=0;x<count;x++)
    {
        thisdata = [cont getRowData:x];

        if(thisdata == data)
        {
            NSIndexSet *selected = [[NSIndexSet alloc] initWithIndex:(NSUInteger)x];

            [cont selectRowIndexes:selected byExtendingSelection:YES];
            [selected release];
            [cont scrollRowToVisible:x];
            x=count;
            break;
        }
    }
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Deletes the item with the text speficied.
 * Parameters:
 *       handle: Handle to the window (widget).
 *       text:  Text usually returned by dw_container_query().
 */
DW_FUNCTION_DEFINITION(dw_container_delete_row, void, HWND handle, const char *text)
DW_FUNCTION_ADD_PARAM2(handle, text)
DW_FUNCTION_NO_RETURN(dw_container_delete_row)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, text, char *)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    char *thistext;
    int x, count = (int)[cont numberOfRowsInTableView:cont];

    for(x=0;x<count;x++)
    {
        thistext = [cont getRowTitle:x];

        if(thistext && strcmp(thistext, text) == 0)
        {
            [cont removeRow:x];
            [cont reloadData];
            x=count;
            break;
        }
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Deletes the item with the data speficied.
 * Parameters:
 *       handle: Handle to the window (widget).
 *       data: Data specified.
 */
DW_FUNCTION_DEFINITION(dw_container_delete_row_by_data, void, HWND handle, void *data)
DW_FUNCTION_ADD_PARAM2(handle, data)
DW_FUNCTION_NO_RETURN(dw_container_delete_row_by_data)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, data, void *)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    void *thisdata;
    int x, count = (int)[cont numberOfRowsInTableView:cont];

    for(x=0;x<count;x++)
    {
        thisdata = [cont getRowData:x];

        if(thisdata == data)
        {
            [cont removeRow:x];
            [cont reloadData];
            x=count;
            break;
        }
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Optimizes the column widths so that all data is visible.
 * Parameters:
 *       handle: Handle to the window (widget) to be optimized.
 */
DW_FUNCTION_DEFINITION(dw_container_optimize, void, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_NO_RETURN(dw_container_optimize)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    DWContainer *cont = handle;
    [cont optimize];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Inserts an icon into the taskbar.
 * Parameters:
 *       handle: Window handle that will handle taskbar icon messages.
 *       icon: Icon handle to display in the taskbar.
 *       bubbletext: Text to show when the mouse is above the icon.
 */
void API dw_taskbar_insert(HWND handle, HICN icon, const char *bubbletext)
{
#ifdef BUILDING_FOR_YOSEMITE
    NSStatusItem *item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
    NSStatusBarButton *button = [item button];
    NSImage *image = icon;
    [button setImage:image];
    if(bubbletext)
        [button setToolTip:[NSString stringWithUTF8String:bubbletext]];
    [button setTarget:handle];
    [button setEnabled:YES];
    [[button cell] setHighlighted:YES];
    [button sendActionOn:(DWEventMaskLeftMouseUp|DWEventMaskLeftMouseDown|DWEventMaskRightMouseUp|DWEventMaskRightMouseDown)];
    [button setAction:@selector(mouseDown:)];
    dw_window_set_data(handle, "_dw_taskbar", item);
#endif
}

/*
 * Deletes an icon from the taskbar.
 * Parameters:
 *       handle: Window handle that was used with dw_taskbar_insert().
 *       icon: Icon handle that was used with dw_taskbar_insert().
 */
void API dw_taskbar_delete(HWND handle, HICN icon)
{
#ifdef BUILDING_FOR_YOSEMITE
    NSStatusItem *item = dw_window_get_data(handle, "_dw_taskbar");
    DW_LOCAL_POOL_IN;
    [item release];
    DW_LOCAL_POOL_OUT;
#endif
}

/* Internal function to keep HICNs from getting too big */
void _icon_resize(NSImage *image)
{
    if(image)
    {
        NSSize size = [image size];
        if(size.width > 24 || size.height > 24)
        {
            if(size.width > 24)
                size.width = 24;
            if(size.height > 24)
                size.height = 24;
            [image setSize:size];
        }
    }
}

/* Internal version that does not resize the image */
HICN _dw_icon_load(unsigned long resid)
{
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *respath = [bundle resourcePath];
    NSString *filepath = [respath stringByAppendingFormat:@"/%lu.png", resid];
    NSImage *image = [[NSImage alloc] initWithContentsOfFile:filepath];
    return image;
}

/*
 * Obtains an icon from a module (or header in GTK).
 * Parameters:
 *          module: Handle to module (DLL) in OS/2 and Windows.
 *          id: A unsigned long id int the resources on OS/2 and
 *              Windows, on GTK this is converted to a pointer
 *              to an embedded XPM.
 */
HICN API dw_icon_load(unsigned long module, unsigned long resid)
{
    NSImage *image = _dw_icon_load(resid);
    _icon_resize(image);
    return image;
}

/*
 * Obtains an icon from a file.
 * Parameters:
 *       filename: Name of the file, omit extention to have
 *                 DW pick the appropriate file extension.
 *                 (ICO on OS/2 or Windows, XPM on Unix)
 */
HICN API dw_icon_load_from_file(const char *filename)
{
    char *ext = _dw_get_image_extension( filename );

    NSString *nstr = [ NSString stringWithUTF8String:filename ];
    NSImage *image = [[NSImage alloc] initWithContentsOfFile:nstr];
    if(!image && ext)
    {
        nstr = [nstr stringByAppendingString: [NSString stringWithUTF8String:ext]];
        image = [[NSImage alloc] initWithContentsOfFile:nstr];
    }
    _icon_resize(image);
    return image;
}

/*
 * Obtains an icon from data
 * Parameters:
 *       filename: Name of the file, omit extention to have
 *                 DW pick the appropriate file extension.
 *                 (ICO on OS/2 or Windows, XPM on Unix)
 */
HICN API dw_icon_load_from_data(const char *data, int len)
{
    NSData *thisdata = [NSData dataWithBytes:data length:len];
    NSImage *image = [[NSImage alloc] initWithData:thisdata];
    _icon_resize(image);
    return image;
}

/*
 * Frees a loaded resource in OS/2 and Windows.
 * Parameters:
 *          handle: Handle to icon returned by dw_icon_load().
 */
void API dw_icon_free(HICN handle)
{
    NSImage *image = handle;
    DW_LOCAL_POOL_IN;
    [image release];
    DW_LOCAL_POOL_OUT;
}

/*
 * Create a new MDI Frame to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id or 0L.
 */
HWND API dw_mdi_new(unsigned long cid)
{
    /* There isn't anything like quite like MDI on MacOS...
     * However we will make floating windows that hide
     * when the application is deactivated to simulate
     * similar behavior.
     */
    DWMDI *mdi = [[[DWMDI alloc] init] retain];
    /* [mdi setTag:cid]; Why doesn't this work? */
    return mdi;
}

/*
 * Creates a splitbar window (widget) with given parameters.
 * Parameters:
 *       type: Value can be DW_VERT or DW_HORZ.
 *       topleft: Handle to the window to be top or left.
 *       bottomright:  Handle to the window to be bottom or right.
 * Returns:
 *       A handle to a splitbar window or NULL on failure.
 */
DW_FUNCTION_DEFINITION(dw_splitbar_new, HWND, int type, HWND topleft, HWND bottomright, unsigned long cid)
DW_FUNCTION_ADD_PARAM4(type, topleft, bottomright, cid)
DW_FUNCTION_RETURN(dw_splitbar_new, HWND)
DW_FUNCTION_RESTORE_PARAM4(type, int, topleft, HWND, bottomright, HWND, cid, unsigned long)
{
    DW_FUNCTION_INIT;
    id tmpbox = dw_box_new(DW_VERT, 0);
    DWSplitBar *split = [[[DWSplitBar alloc] init] retain];
    [split setDelegate:split];
    dw_box_pack_start(tmpbox, topleft, 0, 0, TRUE, TRUE, 0);
    [split addSubview:tmpbox];
    [tmpbox release];
    [tmpbox autorelease];
    tmpbox = dw_box_new(DW_VERT, 0);
    dw_box_pack_start(tmpbox, bottomright, 0, 0, TRUE, TRUE, 0);
    [split addSubview:tmpbox];
    [tmpbox release];
    [tmpbox autorelease];
    if(type == DW_VERT)
    {
        [split setVertical:NO];
    }
    else
    {
        [split setVertical:YES];
    }
    /* Set the default percent to 50% split */
    [split setPercent:50.0];
    [split setTag:cid];
    DW_FUNCTION_RETURN_THIS(split);
}

/*
 * Sets the position of a splitbar (pecentage).
 * Parameters:
 *       handle: The handle to the splitbar returned by dw_splitbar_new().
 *       percent: The position of the splitbar.
 */
DW_FUNCTION_DEFINITION(dw_splitbar_set, void, HWND handle, float percent)
DW_FUNCTION_ADD_PARAM2(handle, percent)
DW_FUNCTION_NO_RETURN(dw_splitbar_set)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, percent, float)
{
    DW_FUNCTION_INIT;
    DWSplitBar *split = handle;
    NSRect rect = [split frame];
    float pos;
    /* Calculate the position based on the size */
    if([split isVertical])
    {
        pos = rect.size.width * (percent / 100.0);
    }
    else
    {
        pos = rect.size.height * (percent / 100.0);
    }
    if(pos > 0)
    {
        [split setPosition:pos ofDividerAtIndex:0];
    }
    else
    {
        /* If we have no size.. wait until the resize
         * event when we get an actual size to try
         * to set the splitbar again.
         */
        [split setPercent:percent];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Gets the position of a splitbar (pecentage).
 * Parameters:
 *       handle: The handle to the splitbar returned by dw_splitbar_new().
 */
float API dw_splitbar_get(HWND handle)
{
    DWSplitBar *split = handle;
    NSRect rect1 = [split frame];
    NSArray *subviews = [split subviews];
    NSView *view = [subviews objectAtIndex:0];
    NSRect rect2 = [view frame];
    float pos, total, retval = 0.0;
    if([split isVertical])
    {
        total = rect1.size.width;
        pos = rect2.size.width;
    }
    else
    {
        total = rect1.size.height;
        pos = rect2.size.height;
    }
    if(total > 0)
    {
        retval = pos / total;
    }
    return retval;
}

/* Internal function to convert fontname to NSFont */
NSFont *_dw_font_by_name(const char *fontname)
{
    NSFont *font = DWDefaultFont;
    
    if(fontname)
    {
        char *name = strchr(fontname, '.');

        if(name && (name++))
        {
            int size = atoi(fontname);
            char *Italic = strstr(name, " Italic");
            char *Bold = strstr(name, " Bold");
            size_t len = (Italic ? (Bold ? (Italic > Bold ? (Bold - name) : (Italic - name)) : (Italic - name)) : (Bold ? (Bold - name) : strlen(name)));
            char *newname = alloca(len+1);

            memset(newname, 0, len+1);
            strncpy(newname, name, len);
            
            font = [DWFontManager fontWithFamily:[NSString stringWithUTF8String:newname]
                                          traits:(Italic ? NSItalicFontMask : 0)|(Bold ? NSBoldFontMask : 0)
                                          weight:5
                                            size:(float)size];
        }
    }
    return font;
}

/*
 * Create a bitmap object to be packed.
 * Parameters:
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
DW_FUNCTION_DEFINITION(dw_bitmap_new, HWND, ULONG cid)
DW_FUNCTION_ADD_PARAM1(cid)
DW_FUNCTION_RETURN(dw_bitmap_new, HWND)
DW_FUNCTION_RESTORE_PARAM1(cid, ULONG)
{
    DW_FUNCTION_INIT;
    NSImageView *bitmap = [[[NSImageView alloc] init] retain];
    [bitmap setImageFrameStyle:NSImageFrameNone];
    [bitmap setImageScaling:NSImageScaleNone];
    [bitmap setEditable:NO];
    [bitmap setTag:cid];
    DW_FUNCTION_RETURN_THIS(bitmap);
}

/*
 * Creates a pixmap with given parameters.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       width: Width of the pixmap in pixels.
 *       height: Height of the pixmap in pixels.
 *       depth: Color depth of the pixmap.
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_new(HWND handle, unsigned long width, unsigned long height, int depth)
{
    HPIXMAP pixmap;

    if(!(pixmap = calloc(1,sizeof(struct _hpixmap))))
        return NULL;
    pixmap->width = width;
    pixmap->height = height;
    pixmap->handle = handle;
    pixmap->image = [[NSBitmapImageRep alloc]
                                    initWithBitmapDataPlanes:NULL
                                    pixelsWide:width
                                    pixelsHigh:height
                                    bitsPerSample:8
                                    samplesPerPixel:4
                                    hasAlpha:YES
                                    isPlanar:NO
                                    colorSpaceName:NSDeviceRGBColorSpace
                                    bytesPerRow:0
                                    bitsPerPixel:0];
    return pixmap;
}

/* Function takes an NSImage and copies it into a flipped NSBitmapImageRep */
void _dw_flip_image(NSImage *tmpimage, NSBitmapImageRep *image, NSSize size)
{
    NSCompositingOperation op = DWCompositingOperationSourceOver;
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext:_dw_draw_context(image)];
    [[[NSDictionary alloc] initWithObjectsAndKeys:image, NSGraphicsContextDestinationAttributeName, nil] autorelease];
    /* Make a new transform */
    NSAffineTransform *t = [NSAffineTransform transform];

    /* By scaling Y negatively, we effectively flip the image */
    [t scaleXBy:1.0 yBy:-1.0];

    /* But we also have to translate it back by its height */
    [t translateXBy:0.0 yBy:-size.height];

    /* Apply the transform */
    [t concat];
    [tmpimage drawAtPoint:NSMakePoint(0, 0) fromRect:NSMakeRect(0, 0, size.width, size.height)
                operation:op fraction:1.0];
    [NSGraphicsContext restoreGraphicsState];
}

/*
 * Creates a pixmap from a file.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       filename: Name of the file, omit extention to have
 *                 DW pick the appropriate file extension.
 *                 (BMP on OS/2 or Windows, XPM on Unix)
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_new_from_file(HWND handle, const char *filename)
{
    HPIXMAP pixmap;
    DW_LOCAL_POOL_IN;
    char *ext = _dw_get_image_extension( filename );

    if(!(pixmap = calloc(1,sizeof(struct _hpixmap))))
    {
        DW_LOCAL_POOL_OUT;
        return NULL;
    }
    NSString *nstr = [ NSString stringWithUTF8String:filename ];
    NSImage *tmpimage = [[[NSImage alloc] initWithContentsOfFile:nstr] autorelease];
    if(!tmpimage && ext)
    {
        nstr = [nstr stringByAppendingString: [NSString stringWithUTF8String:ext]];
        tmpimage = [[[NSImage alloc] initWithContentsOfFile:nstr] autorelease];
    }
    if(!tmpimage)
    {
        DW_LOCAL_POOL_OUT;
        return NULL;
    }
    NSSize size = [tmpimage size];
    NSBitmapImageRep *image = [[NSBitmapImageRep alloc]
                               initWithBitmapDataPlanes:NULL
                               pixelsWide:size.width
                               pixelsHigh:size.height
                               bitsPerSample:8
                               samplesPerPixel:4
                               hasAlpha:YES
                               isPlanar:NO
                               colorSpaceName:NSDeviceRGBColorSpace
                               bytesPerRow:0
                               bitsPerPixel:0];
    _dw_flip_image(tmpimage, image, size);
    pixmap->width = size.width;
    pixmap->height = size.height;
    pixmap->image = image;
    pixmap->handle = handle;
    DW_LOCAL_POOL_OUT;
    return pixmap;
}

/*
 * Creates a pixmap from memory.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       data: Source of the image data
 *                 (BMP on OS/2 or Windows, XPM on Unix)
 *       le: length of data
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_new_from_data(HWND handle, const char *data, int len)
{
    HPIXMAP pixmap;
    DW_LOCAL_POOL_IN;

    if(!(pixmap = calloc(1,sizeof(struct _hpixmap))))
    {
        DW_LOCAL_POOL_OUT;
        return NULL;
    }
    NSData *thisdata = [NSData dataWithBytes:data length:len];
    NSImage *tmpimage = [[[NSImage alloc] initWithData:thisdata] autorelease];
    if(!tmpimage)
    {
        DW_LOCAL_POOL_OUT;
        return NULL;
    }
    NSSize size = [tmpimage size];
    NSBitmapImageRep *image = [[NSBitmapImageRep alloc]
                               initWithBitmapDataPlanes:NULL
                               pixelsWide:size.width
                               pixelsHigh:size.height
                               bitsPerSample:8
                               samplesPerPixel:4
                               hasAlpha:YES
                               isPlanar:NO
                               colorSpaceName:NSDeviceRGBColorSpace
                               bytesPerRow:0
                               bitsPerPixel:0];
    _dw_flip_image(tmpimage, image, size);
    pixmap->width = size.width;
    pixmap->height = size.height;
    pixmap->image = image;
    pixmap->handle = handle;
    DW_LOCAL_POOL_OUT;
    return pixmap;
}

/*
 * Sets the transparent color for a pixmap
 * Parameters:
 *       pixmap: Handle to a pixmap returned by
 *               dw_pixmap_new..
 *       color:  transparent color
 * Note: This does nothing on Mac as transparency
 *       is handled automatically
 */
void API dw_pixmap_set_transparent_color( HPIXMAP pixmap, ULONG color )
{
    /* Don't do anything */
}

/*
 * Creates a pixmap from internal resource graphic specified by id.
 * Parameters:
 *       handle: Window handle the pixmap is associated with.
 *       id: Resource ID associated with requested pixmap.
 * Returns:
 *       A handle to a pixmap or NULL on failure.
 */
HPIXMAP API dw_pixmap_grab(HWND handle, ULONG resid)
{
    HPIXMAP pixmap;
    DW_LOCAL_POOL_IN;

    if(!(pixmap = calloc(1,sizeof(struct _hpixmap))))
    {
        DW_LOCAL_POOL_OUT;
        return NULL;
    }

    NSBundle *bundle = [NSBundle mainBundle];
    NSString *respath = [bundle resourcePath];
    NSString *filepath = [respath stringByAppendingFormat:@"/%lu.png", resid];
    NSImage *temp = [[NSImage alloc] initWithContentsOfFile:filepath];

    if(temp)
    {
        NSSize size = [temp size];
        NSBitmapImageRep *image = [[NSBitmapImageRep alloc]
                                   initWithBitmapDataPlanes:NULL
                                   pixelsWide:size.width
                                   pixelsHigh:size.height
                                   bitsPerSample:8
                                   samplesPerPixel:4
                                   hasAlpha:YES
                                   isPlanar:NO
                                   colorSpaceName:NSDeviceRGBColorSpace
                                   bytesPerRow:0
                                   bitsPerPixel:0];
        _dw_flip_image(temp, image, size);
        pixmap->width = size.width;
        pixmap->height = size.height;
        pixmap->image = image;
        pixmap->handle = handle;
        [temp release];
        DW_LOCAL_POOL_OUT;
        return pixmap;
    }
    free(pixmap);
    DW_LOCAL_POOL_OUT;
    return NULL;
}

/*
 * Sets the font used by a specified pixmap.
 * Normally the pixmap font is obtained from the associated window handle.
 * However this can be used to override that, or for pixmaps with no window.
 * Parameters:
 *          pixmap: Handle to a pixmap returned by dw_pixmap_new() or
 *                  passed to the application via a callback.
 *          fontname: Name and size of the font in the form "size.fontname"
 * Returns:
 *       DW_ERROR_NONE on success and DW_ERROR_GENERAL on failure.
 */
int API dw_pixmap_set_font(HPIXMAP pixmap, const char *fontname)
{
    if(pixmap)
    {
        NSFont *font = _dw_font_by_name(fontname);

        if(font)
        {
            DW_LOCAL_POOL_IN;
            NSFont *oldfont = pixmap->font;
            [font retain];
            pixmap->font = font;
            if(oldfont)
                [oldfont release];
            DW_LOCAL_POOL_OUT;
            return DW_ERROR_NONE;
        }
    }
    return DW_ERROR_GENERAL;
}

/*
 * Destroys an allocated pixmap.
 * Parameters:
 *       pixmap: Handle to a pixmap returned by
 *               dw_pixmap_new..
 */
void API dw_pixmap_destroy(HPIXMAP pixmap)
{
    if(pixmap)
    {
        NSBitmapImageRep *image = (NSBitmapImageRep *)pixmap->image;
        NSFont *font = pixmap->font;
        DW_LOCAL_POOL_IN;
        [image release];
        [font release];
        free(pixmap);
        DW_LOCAL_POOL_OUT;
    }
}

/*
 * Copies from one item to another.
 * Parameters:
 *       dest: Destination window handle.
 *       destp: Destination pixmap. (choose only one).
 *       xdest: X coordinate of destination.
 *       ydest: Y coordinate of destination.
 *       width: Width of area to copy.
 *       height: Height of area to copy.
 *       src: Source window handle.
 *       srcp: Source pixmap. (choose only one).
 *       xsrc: X coordinate of source.
 *       ysrc: Y coordinate of source.
 */
void API dw_pixmap_bitblt(HWND dest, HPIXMAP destp, int xdest, int ydest, int width, int height, HWND src, HPIXMAP srcp, int xsrc, int ysrc)
{
    dw_pixmap_stretch_bitblt(dest, destp, xdest, ydest, width, height, src, srcp, xsrc, ysrc, -1, -1);
}

/*
 * Copies from one surface to another allowing for stretching.
 * Parameters:
 *       dest: Destination window handle.
 *       destp: Destination pixmap. (choose only one).
 *       xdest: X coordinate of destination.
 *       ydest: Y coordinate of destination.
 *       width: Width of the target area.
 *       height: Height of the target area.
 *       src: Source window handle.
 *       srcp: Source pixmap. (choose only one).
 *       xsrc: X coordinate of source.
 *       ysrc: Y coordinate of source.
 *       srcwidth: Width of area to copy.
 *       srcheight: Height of area to copy.
 * Returns:
 *       DW_ERROR_NONE on success and DW_ERROR_GENERAL on failure.
 */
int API dw_pixmap_stretch_bitblt(HWND dest, HPIXMAP destp, int xdest, int ydest, int width, int height, HWND src, HPIXMAP srcp, int xsrc, int ysrc, int srcwidth, int srcheight)
{
    DWBitBlt *bltinfo;
    NSValue* bi;
    DW_LOCAL_POOL_IN;

    /* Sanity checks */
    if((!dest && !destp) || (!src && !srcp) ||
       ((srcwidth == -1 || srcheight == -1) && srcwidth != srcheight))
    {
        DW_LOCAL_POOL_OUT;
        return DW_ERROR_GENERAL;
    }

    bltinfo = calloc(1, sizeof(DWBitBlt));
    bi = [NSValue valueWithPointer:bltinfo];

    /* Fill in the information */
    bltinfo->dest = dest;
    bltinfo->src = src;
    bltinfo->xdest = xdest;
    bltinfo->ydest = ydest;
    bltinfo->width = width;
    bltinfo->height = height;
    bltinfo->xsrc = xsrc;
    bltinfo->ysrc = ysrc;
    bltinfo->srcwidth = srcwidth;
    bltinfo->srcheight = srcheight;

    if(destp)
    {
        bltinfo->dest = (id)destp->image;
    }
    if(srcp)
    {
        id object = bltinfo->src = (id)srcp->image;
        [object retain];
    }
    [DWObj safeCall:@selector(doBitBlt:) withObject:bi];
    DW_LOCAL_POOL_OUT;
    return DW_ERROR_NONE;
}

/*
 * Create a new static text window (widget) to be packed.
 * Not available under OS/2, eCS
 * Parameters:
 *       text: The text to be display by the static text widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
HWND API dw_calendar_new(ULONG cid)
{
    DWCalendar *calendar = [[[DWCalendar alloc] init] retain];
    [calendar setDatePickerMode:DWDatePickerModeSingle];
    [calendar setDatePickerStyle:DWDatePickerStyleClockAndCalendar];
    [calendar setDatePickerElements:DWDatePickerElementFlagYearMonthDay];
    [calendar setTag:cid];
    [calendar setDateValue:[NSDate date]];
    return calendar;
}

/*
 * Sets the current date of a calendar.
 * Parameters:
 *       handle: The handle to the calendar returned by dw_calendar_new().
 *       year...
 */
void dw_calendar_set_date(HWND handle, unsigned int year, unsigned int month, unsigned int day)
{
    DWCalendar *calendar = handle;
    NSDate *date;
    char buffer[101];
    DW_LOCAL_POOL_IN;

    snprintf(buffer, 100, "%04d-%02d-%02d", year, month, day);

    NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
    dateFormatter.dateFormat = @"yyyy-MM-dd";

    date = [dateFormatter dateFromString:[NSString stringWithUTF8String:buffer]];
    [calendar setDateValue:date];
    [date release];
    DW_LOCAL_POOL_OUT;
}

/*
 * Gets the current date of a calendar.
 * Parameters:
 *       handle: The handle to the calendar returned by dw_calendar_new().
 */
void dw_calendar_get_date(HWND handle, unsigned int *year, unsigned int *month, unsigned int *day)
{
    DWCalendar *calendar = handle;
    DW_LOCAL_POOL_IN;
    NSCalendar *mycalendar = [[NSCalendar alloc] initWithCalendarIdentifier:DWCalendarIdentifierGregorian];
    NSDate *date = [calendar dateValue];
    NSDateComponents* components = [mycalendar components:DWCalendarUnitDay|DWCalendarUnitMonth|DWCalendarUnitYear fromDate:date];
    *day = (unsigned int)[components day];
    *month = (unsigned int)[components month];
    *year = (unsigned int)[components year];
    [mycalendar release];
    DW_LOCAL_POOL_OUT;
}

/*
 * Causes the embedded HTML widget to take action.
 * Parameters:
 *       handle: Handle to the window.
 *       action: One of the DW_HTML_* constants.
 */
void API dw_html_action(HWND handle, int action)
{
    DWWebView *html = handle;
    switch(action)
    {
        case DW_HTML_GOBACK:
            [html goBack];
            break;
        case DW_HTML_GOFORWARD:
            [html goForward];
            break;
        case DW_HTML_GOHOME:
            dw_html_url(handle, DW_HOME_URL);
            break;
        case DW_HTML_SEARCH:
            break;
        case DW_HTML_RELOAD:
            [html reload:html];
            break;
        case DW_HTML_STOP:
            [html stopLoading:html];
            break;
        case DW_HTML_PRINT:
            break;
    }
}

/*
 * Render raw HTML code in the embedded HTML widget..
 * Parameters:
 *       handle: Handle to the window.
 *       string: String buffer containt HTML code to
 *               be rendered.
 * Returns:
 *       0 on success.
 */
int API dw_html_raw(HWND handle, const char *string)
{
    DWWebView *html = handle;
#if WK_API_ENABLED
    [html loadHTMLString:[ NSString stringWithUTF8String:string ] baseURL:nil];
#else
    [[html mainFrame] loadHTMLString:[ NSString stringWithUTF8String:string ] baseURL:nil];
#endif
    return DW_ERROR_NONE;
}

/*
 * Render file or web page in the embedded HTML widget..
 * Parameters:
 *       handle: Handle to the window.
 *       url: Universal Resource Locator of the web or
 *               file object to be rendered.
 * Returns:
 *       0 on success.
 */
int API dw_html_url(HWND handle, const char *url)
{
    DWWebView *html = handle;
#if WK_API_ENABLED
    [html loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[ NSString stringWithUTF8String:url ]]]];
#else
    [[html mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[ NSString stringWithUTF8String:url ]]]];
#endif
    return DW_ERROR_NONE;
}

/*
 * Executes the javascript contained in "script" in the HTML window.
 * Parameters:
 *       handle: Handle to the HTML window.
 *       script: Javascript code to execute.
 *       scriptdata: Data passed to the signal handler.
 * Notes: A DW_SIGNAL_HTML_RESULT event will be raised with scriptdata.
 * Returns:
 *       DW_ERROR_NONE (0) on success.
 */
int dw_html_javascript_run(HWND handle, const char *script, void *scriptdata)
{
    DWWebView *html = handle;
    DW_LOCAL_POOL_IN;

#if WK_API_ENABLED
    [html evaluateJavaScript:[NSString stringWithUTF8String:script] completionHandler:^(NSString *result, NSError *error)
    {
        void *params[2] = { result, scriptdata };
        _dw_event_handler(html, (NSEvent *)params, _DW_EVENT_HTML_RESULT);
    }];
#else
    NSString *result = [html stringByEvaluatingJavaScriptFromString:[NSString stringWithUTF8String:script]];
    void *params[2] = { result, scriptdata };
    _dw_event_handler(html, (NSEvent *)params, _DW_EVENT_HTML_RESULT);
#endif
    DW_LOCAL_POOL_OUT;
    return DW_ERROR_NONE;
}

/*
 * Create a new HTML window (widget) to be packed.
 * Not available under OS/2, eCS
 * Parameters:
 *       text: The default text to be in the entryfield widget.
 *       id: An ID to be used with dw_window_from_id() or 0L.
 */
DW_FUNCTION_DEFINITION(dw_html_new, HWND, ULONG cid)
DW_FUNCTION_ADD_PARAM1(cid)
DW_FUNCTION_RETURN(dw_html_new, HWND)
DW_FUNCTION_RESTORE_PARAM1(DW_UNUSED(cid), ULONG)
{
    DW_FUNCTION_INIT;
    DWWebView *web = [[[DWWebView alloc] init] retain];
#if WK_API_ENABLED
    web.navigationDelegate = web;
#else
    web.frameLoadDelegate = (id)web;
#endif
    /* [web setTag:cid]; Why doesn't this work? */
    DW_FUNCTION_RETURN_THIS(web);
}

/*
 * Returns the current X and Y coordinates of the mouse pointer.
 * Parameters:
 *       x: Pointer to variable to store X coordinate.
 *       y: Pointer to variable to store Y coordinate.
 */
void API dw_pointer_query_pos(long *x, long *y)
{
    NSPoint mouseLoc;
    mouseLoc = [NSEvent mouseLocation];
    if(x)
    {
        *x = mouseLoc.x;
    }
    if(y)
    {
        *y = [[NSScreen mainScreen] frame].size.height - mouseLoc.y;
    }
}

/*
 * Sets the X and Y coordinates of the mouse pointer.
 * Parameters:
 *       x: X coordinate.
 *       y: Y coordinate.
 */
void API dw_pointer_set_pos(long x, long y)
{
    /* From what I have read this isn't possible, agaist human interface rules */
}

/*
 * Create a menu object to be popped up.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
HMENUI API dw_menu_new(ULONG cid)
{
    NSMenu *menu = [[NSMenu alloc] init];
    [menu setAutoenablesItems:NO];
    /* [menu setTag:cid]; Why doesn't this work? */
    return menu;
}

/*
 * Create a menubar on a window.
 * Parameters:
 *       location: Handle of a window frame to be attached to.
 */
HMENUI API dw_menubar_new(HWND location)
{
    NSWindow *window = location;
    NSMenu *windowmenu = _dw_generate_main_menu();
    [[window contentView] setMenu:windowmenu];
    return (HMENUI)windowmenu;
}

/*
 * Destroys a menu created with dw_menubar_new or dw_menu_new.
 * Parameters:
 *       menu: Handle of a menu.
 */
void API dw_menu_destroy(HMENUI *menu)
{
    if(menu)
    {
        NSMenu *thismenu = *menu;
        DW_LOCAL_POOL_IN;
        [thismenu release];
        DW_LOCAL_POOL_OUT;
        *menu = NULL;
    }
}

/* Handle deprecation of convertScreenToBase in 10.10 yet still supporting
 * 10.6 and earlier since convertRectFromScreen was introduced in 10.7.
 */
NSPoint _dw_windowPointFromScreen(id window, NSPoint p)
{
    SEL crfs = NSSelectorFromString(@"convertRectFromScreen:");

    if([window respondsToSelector:crfs])
    {
        NSRect (* icrfs)(id, SEL, NSRect) = (NSRect (*)(id, SEL, NSRect))[window methodForSelector:crfs];
        NSRect rect = icrfs(window, crfs, NSMakeRect(p.x, p.y, 1, 1));
        return rect.origin;
    }
    else
    {
        SEL cstb = NSSelectorFromString(@"convertScreenToBase:");

        if([window respondsToSelector:cstb])
        {
            NSPoint (* icstb)(id, SEL, NSPoint) = (NSPoint (*)(id, SEL, NSPoint))[window methodForSelector:cstb];
            return icstb(window, cstb, p);
        }
    }
    return NSMakePoint(0,0);
}

/*
 * Pops up a context menu at given x and y coordinates.
 * Parameters:
 *       menu: The handle the the existing menu.
 *       parent: Handle to the window initiating the popup.
 *       x: X coordinate.
 *       y: Y coordinate.
 */
void API dw_menu_popup(HMENUI *menu, HWND parent, int x, int y)
{
    if(menu)
    {
        NSMenu *thismenu = (NSMenu *)*menu;
        id object = parent;
        NSView *view = [object isKindOfClass:[NSWindow class]] ? [object contentView] : parent;
        NSWindow *window = [view window];
        NSEvent *event = [DWApp currentEvent];
        if(!window)
            window = [event window];
        [thismenu autorelease];
        NSPoint p = NSMakePoint(x, [[NSScreen mainScreen] frame].size.height - y);
        NSEvent* fake = [NSEvent mouseEventWithType:DWEventTypeRightMouseDown
                                           location:_dw_windowPointFromScreen(window, p)
                                      modifierFlags:0
                                          timestamp:[event timestamp]
                                       windowNumber:[window windowNumber]
                                            context:[NSGraphicsContext currentContext]
                                        eventNumber:1
                                         clickCount:1
                                           pressure:0.0];
        [NSMenu popUpContextMenu:thismenu withEvent:fake forView:view];
        *menu = NULL;
    }
}

char _dw_removetilde(char *dest, const char *src)
{
    int z, cur=0;
    char accel = '\0';

    for(z=0;z<strlen(src);z++)
    {
        if(src[z] != '~')
        {
            dest[cur] = src[z];
            cur++;
        }
        else
        {
            accel = src[z+1];
        }
    }
    dest[cur] = 0;
    return accel;
}

/*
 * Adds a menuitem or submenu to an existing menu.
 * Parameters:
 *       menu: The handle the the existing menu.
 *       title: The title text on the menu item to be added.
 *       id: An ID to be used for message passing.
 *       flags: Extended attributes to set on the menu.
 *       end: If TRUE memu is positioned at the end of the menu.
 *       check: If TRUE menu is "check"able.
 *       flags: Extended attributes to set on the menu.
 *       submenu: Handle to an existing menu to be a submenu or NULL.
 */
HWND API dw_menu_append_item(HMENUI menux, const char *title, ULONG itemid, ULONG flags, int end, int check, HMENUI submenux)
{
    NSMenu *menu = menux;
    NSMenu *submenu = submenux;
    DWMenuItem *item = NULL;
    if(strlen(title) == 0)
    {
        [menu addItem:[DWMenuItem separatorItem]];
    }
    else
    {
        char accel[2];
        char *newtitle = malloc(strlen(title)+1);
        NSString *nstr;

        accel[0] = _dw_removetilde(newtitle, title);
        accel[1] = 0;

        nstr = [ NSString stringWithUTF8String:newtitle ];
        free(newtitle);

        item = [[[DWMenuItem alloc] initWithTitle:nstr
                            action:@selector(menuHandler:)
                            keyEquivalent:[ NSString stringWithUTF8String:accel ]] autorelease];
        [menu addItem:item];

        [item setTag:itemid];
        if(check)
        {
            [item setCheck:YES];
            if(flags & DW_MIS_CHECKED)
            {
                [item setState:DWControlStateValueOn];
            }
        }
        if(flags & DW_MIS_DISABLED)
        {
            [item setEnabled:NO];
        }

        if(submenux)
        {
            [submenu setTitle:nstr];
            [menu setSubmenu:submenu forItem:item];
        }
        return item;
    }
    return item;
}

/*
 * Sets the state of a menu item check.
 * Deprecated; use dw_menu_item_set_state()
 * Parameters:
 *       menu: The handle the the existing menu.
 *       id: Menuitem id.
 *       check: TRUE for checked FALSE for not checked.
 */
void API dw_menu_item_set_check(HMENUI menux, unsigned long itemid, int check)
{
    id menu = menux;
    NSMenuItem *menuitem = (NSMenuItem *)[menu itemWithTag:itemid];

    if(menuitem != nil)
    {
        if(check)
        {
            [menuitem setState:DWControlStateValueOn];
        }
        else
        {
            [menuitem setState:DWControlStateValueOff];
        }
    }
}

/*
 * Deletes the menu item specified.
 * Parameters:
 *       menu: The handle to the  menu in which the item was appended.
 *       id: Menuitem id.
 * Returns:
 *       DW_ERROR_NONE (0) on success or DW_ERROR_UNKNOWN on failure.
 */
int API dw_menu_delete_item(HMENUI menux, unsigned long itemid)
{
    id menu = menux;
    NSMenuItem *menuitem = (NSMenuItem *)[menu itemWithTag:itemid];

    if(menuitem != nil)
    {
        [menu removeItem:menuitem];
        return DW_ERROR_NONE;
    }
    return DW_ERROR_UNKNOWN;
}

/*
 * Sets the state of a menu item.
 * Parameters:
 *       menu: The handle to the existing menu.
 *       id: Menuitem id.
 *       flags: DW_MIS_ENABLED/DW_MIS_DISABLED
 *              DW_MIS_CHECKED/DW_MIS_UNCHECKED
 */
void API dw_menu_item_set_state(HMENUI menux, unsigned long itemid, unsigned long state)
{
    id menu = menux;
    NSMenuItem *menuitem = (NSMenuItem *)[menu itemWithTag:itemid];

    if(menuitem != nil)
    {
        if(state & DW_MIS_CHECKED)
        {
            [menuitem setState:DWControlStateValueOn];
        }
        else if(state & DW_MIS_UNCHECKED)
        {
            [menuitem setState:DWControlStateValueOff];
        }
        if(state & DW_MIS_ENABLED)
        {
            [menuitem setEnabled:YES];
        }
        else if(state & DW_MIS_DISABLED)
        {
            [menuitem setEnabled:NO];
        }
    }
}

/* Gets the notebook page from associated ID */
DWNotebookPage *_dw_notepage_from_id(DWNotebook *notebook, unsigned long pageid)
{
    NSArray *pages = [notebook tabViewItems];
    for(DWNotebookPage *notepage in pages)
    {
        if([notepage pageid] == pageid)
        {
            return notepage;
        }
    }
    return nil;
}

/*
 * Create a notebook object to be packed.
 * Parameters:
 *       id: An ID to be used for getting the resource from the
 *           resource file.
 */
HWND API dw_notebook_new(ULONG cid, int top)
{
    DWNotebook *notebook = [[[DWNotebook alloc] init] retain];
    [notebook setDelegate:notebook];
    /* [notebook setTag:cid]; Why doesn't this work? */
    return notebook;
}

/*
 * Adds a new page to specified notebook.
 * Parameters:
 *          handle: Window (widget) handle.
 *          flags: Any additional page creation flags.
 *          front: If TRUE page is added at the beginning.
 */
unsigned long API dw_notebook_page_new(HWND handle, ULONG flags, int front)
{
    DWNotebook *notebook = handle;
    NSInteger page = [notebook pageid];
    DWNotebookPage *notepage = [[DWNotebookPage alloc] initWithIdentifier:[NSString stringWithFormat: @"pageid:%d", (int)page]];
    [notepage setPageid:(int)page];
    if(front)
    {
        [notebook insertTabViewItem:notepage atIndex:(NSInteger)0];
    }
    else
    {
        [notebook addTabViewItem:notepage];
    }
    [notepage autorelease];
    [notebook setPageid:(int)(page+1)];
    return (unsigned long)page;
}

/*
 * Remove a page from a notebook.
 * Parameters:
 *          handle: Handle to the notebook widget.
 *          pageid: ID of the page to be destroyed.
 */
void API dw_notebook_page_destroy(HWND handle, unsigned long pageid)
{
    DWNotebook *notebook = handle;
    DWNotebookPage *notepage = _dw_notepage_from_id(notebook, pageid);
    DW_LOCAL_POOL_IN;

    if(notepage != nil)
    {
        [notebook removeTabViewItem:notepage];
    }
    DW_LOCAL_POOL_OUT;
}

/*
 * Queries the currently visible page ID.
 * Parameters:
 *          handle: Handle to the notebook widget.
 */
unsigned long API dw_notebook_page_get(HWND handle)
{
    DWNotebook *notebook = handle;
    DWNotebookPage *notepage = (DWNotebookPage *)[notebook selectedTabViewItem];
    return [notepage pageid];
}

/*
 * Sets the currently visibale page ID.
 * Parameters:
 *          handle: Handle to the notebook widget.
 *          pageid: ID of the page to be made visible.
 */
void API dw_notebook_page_set(HWND handle, unsigned long pageid)
{
    DWNotebook *notebook = handle;
    DWNotebookPage *notepage = _dw_notepage_from_id(notebook, pageid);

    if(notepage != nil)
    {
        [notebook selectTabViewItem:notepage];
    }
}

/*
 * Sets the text on the specified notebook tab.
 * Parameters:
 *          handle: Notebook handle.
 *          pageid: Page ID of the tab to set.
 *          text: Pointer to the text to set.
 */
void API dw_notebook_page_set_text(HWND handle, ULONG pageid, const char *text)
{
    DWNotebook *notebook = handle;
    DWNotebookPage *notepage = _dw_notepage_from_id(notebook, pageid);

    if(notepage != nil)
    {
        [notepage setLabel:[ NSString stringWithUTF8String:text ]];
    }
}

/*
 * Sets the text on the specified notebook tab status area.
 * Parameters:
 *          handle: Notebook handle.
 *          pageid: Page ID of the tab to set.
 *          text: Pointer to the text to set.
 */
void API dw_notebook_page_set_status_text(HWND handle, ULONG pageid, const char *text)
{
    /* Note supported here... do nothing */
}

/*
 * Packs the specified box into the notebook page.
 * Parameters:
 *          handle: Handle to the notebook to be packed.
 *          pageid: Page ID in the notebook which is being packed.
 *          page: Box handle to be packed.
 */
void API dw_notebook_pack(HWND handle, ULONG pageid, HWND page)
{
    DWNotebook *notebook = handle;
    DWNotebookPage *notepage = _dw_notepage_from_id(notebook, pageid);

    if(notepage != nil)
    {
        HWND tmpbox = dw_box_new(DW_VERT, 0);
        DWBox *box = tmpbox;

        dw_box_pack_start(tmpbox, page, 0, 0, TRUE, TRUE, 0);
        [notepage setView:box];
        [box release];
        [box autorelease];
    }
}

#ifndef NSWindowCollectionBehaviorFullScreenPrimary
#define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7)
#endif

/*
 * Create a new Window Frame.
 * Parameters:
 *       owner: The Owner's window handle or HWND_DESKTOP.
 *       title: The Window title.
 *       flStyle: Style flags, see the PM reference.
 */
DW_FUNCTION_DEFINITION(dw_window_new, HWND, HWND hwndOwner, const char *title, ULONG flStyle)
DW_FUNCTION_ADD_PARAM3(hwndOwner, title, flStyle)
DW_FUNCTION_RETURN(dw_window_new, HWND)
DW_FUNCTION_RESTORE_PARAM3(hwndOwner, HWND, title, char *, flStyle, ULONG)
{
    DW_FUNCTION_INIT;
    NSRect frame = NSMakeRect(1,1,1,1);
    DWWindow *window = [[DWWindow alloc]
                        initWithContentRect:frame
                        styleMask:(flStyle)
                        backing:NSBackingStoreBuffered
                        defer:false];

    [window setTitle:[ NSString stringWithUTF8String:title ]];

    DWView *view = [[DWView alloc] init];

    [window setContentView:view];
    [window setDelegate:view];
    [window setAutorecalculatesKeyViewLoop:YES];
    [window setAcceptsMouseMovedEvents:YES];
    [window setReleasedWhenClosed:YES];
    [view autorelease];

    /* Enable full screen mode on resizeable windows */
    if(flStyle & DW_FCF_SIZEBORDER)
    {
        [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
    }

    /* If it isn't a toplevel window... */
    if(hwndOwner)
    {
        id object = hwndOwner;

        /* Check to see if the parent is an MDI window */
        if([object isMemberOfClass:[DWMDI class]])
        {
            /* Set the window level to be floating */
            [window setLevel:NSFloatingWindowLevel];
            [window setHidesOnDeactivate:YES];
        }
    }
    DW_FUNCTION_RETURN_THIS(window);
}

/*
 * Call a function from the window (widget)'s context.
 * Parameters:
 *       handle: Window handle of the widget.
 *       function: Function pointer to be called.
 *       data: Pointer to the data to be passed to the function.
 */
void API dw_window_function(HWND handle, void *function, void *data)
{
    void **params = calloc(2, sizeof(void *));
    NSValue *v;
    DW_LOCAL_POOL_IN;
    v = [NSValue valueWithPointer:params];
    params[0] = function;
    params[1] = data;
    [DWObj performSelectorOnMainThread:@selector(doWindowFunc:) withObject:v waitUntilDone:YES];
    free(params);
    DW_LOCAL_POOL_OUT;
}


/*
 * Changes the appearance of the mouse pointer.
 * Parameters:
 *       handle: Handle to widget for which to change.
 *       cursortype: ID of the pointer you want.
 */
void API dw_window_set_pointer(HWND handle, int pointertype)
{
    id object = handle;

    if([ object isKindOfClass:[ NSView class ] ])
    {
        NSView *view = handle;

        if(pointertype == DW_POINTER_DEFAULT)
        {
            [view discardCursorRects];
        }
        else if(pointertype == DW_POINTER_ARROW)
        {
            NSRect rect = [view frame];
            NSCursor *cursor = [NSCursor arrowCursor];

            [view addCursorRect:rect cursor:cursor];
        }
        /* No cursor for DW_POINTER_CLOCK? */
    }
}

/*
 * Makes the window visible.
 * Parameters:
 *           handle: The window handle to make visible.
 */
int API dw_window_show(HWND handle)
{
    NSObject *object = handle;

    if([ object isMemberOfClass:[ DWWindow class ] ])
    {
        DWWindow *window = handle;
        NSRect rect = [[window contentView] frame];
        id defaultitem = [window initialFirstResponder];

        if([window isMiniaturized])
        {
            [window deminiaturize:nil];
        }
        /* If we haven't been sized by a call.. */
        if(rect.size.width <= 1 || rect.size.height <= 1)
        {
            /* Determine the contents size */
            dw_window_set_size(handle, 0, 0);
        }
        /* If the position was not set... generate a default
         * default one in a similar pattern to SHELLPOSITION.
         */
        if(![window shown])
        {
            static int defaultx = 0, defaulty = 0;
            int cx = dw_screen_width(), cy = dw_screen_height();
            int maxx = cx / 4, maxy = cy / 4;
            NSPoint point;

            rect = [window frame];

            defaultx += 20;
            defaulty += 20;
            if(defaultx > maxx)
                defaultx = 20;
            if(defaulty > maxy)
                defaulty = 20;

            point.x = defaultx;
            /* Take into account menu bar and inverted Y */
            point.y = cy - defaulty - (int)rect.size.height - 22;

            [window setFrameOrigin:point];
            [window setShown:YES];
        }
        [[window contentView] showWindow];
        [window makeKeyAndOrderFront:nil];

        if(!([window styleMask] & DWWindowStyleMaskResizable))
        {
            /* Fix incorrect repeat in displaying textured windows */
            [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge];
            [window setContentBorderThickness:0.0 forEdge:NSMinYEdge];
        }
        if(defaultitem)
        {
            /* If there is a default item set, make it first responder */
            [window makeFirstResponder:defaultitem];
        }
    }
    return 0;
}

/*
 * Makes the window invisible.
 * Parameters:
 *           handle: The window handle to make visible.
 */
int API dw_window_hide(HWND handle)
{
    NSObject *object = handle;

    if([ object isKindOfClass:[ NSWindow class ] ])
    {
        NSWindow *window = handle;

        [window orderOut:nil];
    }
    return 0;
}

/*
 * Sets the colors used by a specified window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          fore: Foreground color in DW_RGB format or a default color index.
 *          back: Background color in DW_RGB format or a default color index.
 */
int API dw_window_set_color(HWND handle, ULONG fore, ULONG back)
{
    id object = handle;
    unsigned long _fore = _dw_get_color(fore);
    unsigned long _back = _dw_get_color(back);
    NSColor *fg = NULL;
    NSColor *bg = NULL;

    /* Get the NSColor for non-default colors */
    if(fore != DW_CLR_DEFAULT)
    {
        fg = [NSColor colorWithDeviceRed: DW_RED_VALUE(_fore)/255.0 green: DW_GREEN_VALUE(_fore)/255.0 blue: DW_BLUE_VALUE(_fore)/255.0 alpha: 1];
    }
    if(back != DW_CLR_DEFAULT)
    {
        bg = [NSColor colorWithDeviceRed: DW_RED_VALUE(_back)/255.0 green: DW_GREEN_VALUE(_back)/255.0 blue: DW_BLUE_VALUE(_back)/255.0 alpha: 1];
    }

    /* Get the textfield from the spinbutton */
    if([object isMemberOfClass:[DWSpinButton class]])
    {
        object = [object textfield];
    }
    /* Get the cell on classes using NSCell */
    if([object isKindOfClass:[NSTextField class]])
    {
        id cell = [object cell];

        [object setTextColor:(fg ? fg : [NSColor controlTextColor])];
        [cell setTextColor:(fg ? fg : [NSColor controlTextColor])];
    }
    if([object isMemberOfClass:[DWButton class]])
    {
        [object setTextColor:(fg ? fg : [NSColor controlTextColor])];
    }
    if([object isKindOfClass:[NSTextField class]] || [object isKindOfClass:[NSButton class]])
    {
        id cell = [object cell];

        [cell setBackgroundColor:(bg ? bg : [NSColor controlColor])];
    }
    else if([object isMemberOfClass:[DWBox class]])
    {
        DWBox *box = object;

        [box setColor:_back];
    }
    else if([object isKindOfClass:[NSTableView class]])
    {
        DWContainer *cont = handle;

        [cont setBackgroundColor:(bg ? bg : [NSColor controlBackgroundColor])];
        [cont setForegroundColor:(fg ? fg : [NSColor controlTextColor])];
    }
    else if([object isMemberOfClass:[DWMLE class]])
    {
        DWMLE *mle = handle;
        [mle setBackgroundColor:(bg ? bg : [NSColor controlBackgroundColor])];
        NSTextStorage *ts = [mle textStorage];
        [ts setForegroundColor:(fg ? fg : [NSColor controlTextColor])];
    }
    return 0;
}

/*
 * Sets the font used by a specified window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          border: Size of the window border in pixels.
 */
int API dw_window_set_border(HWND handle, int border)
{
    return 0;
}

/*
 * Sets the style of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          width: New width in pixels.
 *          height: New height in pixels.
 */
void API dw_window_set_style(HWND handle, ULONG style, ULONG mask)
{
    id object = _dw_text_handle(handle);

    if([object isMemberOfClass:[DWWindow class]])
    {
        DWWindow *window = object;
        SEL sssm = NSSelectorFromString(@"setStyleMask");

        if([window respondsToSelector:sssm])
        {
            DWIMP issm = (DWIMP)[window methodForSelector:sssm];
            int currentstyle = (int)[window styleMask];
            int tmp;

            tmp = currentstyle | (int)mask;
            tmp ^= mask;
            tmp |= style;

            issm(window, sssm, tmp);
        }
    }
    else if([object isKindOfClass:[NSTextField class]])
    {
        NSTextField *tf = object;
        DWTextFieldCell *cell = [tf cell];

        [cell setAlignment:(style & 0xF)];
        if(mask & DW_DT_VCENTER && [cell isMemberOfClass:[DWTextFieldCell class]])
        {
            [cell setVCenter:(style & DW_DT_VCENTER ? YES : NO)];
        }
        if(mask & DW_DT_WORDBREAK && [cell isMemberOfClass:[DWTextFieldCell class]])
        {
            [cell setWraps:(style & DW_DT_WORDBREAK ? YES : NO)];
        }
    }
    else if([object isMemberOfClass:[NSTextView class]])
    {
        NSTextView *tv = handle;
        [tv setAlignment:(style & mask & (DWTextAlignmentLeft|DWTextAlignmentCenter|DWTextAlignmentRight))];
    }
    else if([object isMemberOfClass:[DWButton class]])
    {
        DWButton *button = handle;

        if(mask & DW_BS_NOBORDER)
        {
            if(style & DW_BS_NOBORDER)
                [button setBordered:NO];
            else
                [button setBordered:YES];
        }
    }
    else if([object isMemberOfClass:[DWMenuItem class]])
    {
        if(mask & (DW_MIS_CHECKED | DW_MIS_UNCHECKED))
        {
            if(style & DW_MIS_CHECKED)
                [object setState:DWControlStateValueOn];
            else if(style & DW_MIS_UNCHECKED)
                [object setState:DWControlStateValueOff];
        }
        if(mask & (DW_MIS_ENABLED | DW_MIS_DISABLED))
        {
            if(style & DW_MIS_ENABLED)
                [object setEnabled:YES];
            else if(style & DW_MIS_DISABLED)
                [object setEnabled:NO];
        }
    }
}

/*
 * Sets the current focus item for a window/dialog.
 * Parameters:
 *         handle: Handle to the dialog item to be focused.
 * Remarks:
 *          This is for use after showing the window/dialog.
 */
void API dw_window_set_focus(HWND handle)
{
    id object = handle;

    [[object window] makeFirstResponder:object];
}

/*
 * Sets the default focus item for a window/dialog.
 * Parameters:
 *         window: Toplevel window or dialog.
 *         defaultitem: Handle to the dialog item to be default.
 * Remarks:
 *          This is for use before showing the window/dialog.
 */
void API dw_window_default(HWND handle, HWND defaultitem)
{
    DWWindow *window = handle;
    id object = defaultitem;

    if([window isKindOfClass:[NSWindow class]] && [object isKindOfClass:[NSControl class]])
    {
        [window setInitialFirstResponder:defaultitem];
    }
}

/*
 * Sets window to click the default dialog item when an ENTER is pressed.
 * Parameters:
 *         window: Window (widget) to look for the ENTER press.
 *         next: Window (widget) to move to next (or click)
 */
void API dw_window_click_default(HWND handle, HWND next)
{
    id object = handle;
    id control = next;

    if([object isMemberOfClass:[DWWindow class]])
    {
        if([control isMemberOfClass:[DWButton class]])
        {
            NSWindow *window = object;

            [window setDefaultButtonCell:[control cell]];
        }
    }
    else
    {
        if([control isMemberOfClass:[DWSpinButton class]])
        {
            control = [control textfield];
        }
        else if([control isMemberOfClass:[DWComboBox class]])
        {
            /* TODO: Figure out why the combobox can't be
             * focused using makeFirstResponder method.
             */
            control = [control textfield];
        }
        [object setClickDefault:control];
    }
}

/*
 * Captures the mouse input to this window.
 * Parameters:
 *       handle: Handle to receive mouse input.
 */
void API dw_window_capture(HWND handle)
{
    /* Don't do anything for now */
}

/*
 * Releases previous mouse capture.
 */
void API dw_window_release(void)
{
    /* Don't do anything for now */
}

/*
 * Changes a window's parent to newparent.
 * Parameters:
 *           handle: The window handle to destroy.
 *           newparent: The window's new parent window.
 */
void API dw_window_reparent(HWND handle, HWND newparent)
{
    id object = handle;

    if([object isMemberOfClass:[DWWindow class]])
    {
        /* We can't actually reparent on MacOS but if the
         * new parent is an MDI window, change to be a
         * floating window... otherwise set it to normal.
         */
        NSWindow *window = handle;

        /* If it isn't a toplevel window... */
        if(newparent)
        {
            object = newparent;

            /* Check to see if the parent is an MDI window */
            if([object isMemberOfClass:[DWMDI class]])
            {
                /* Set the window level to be floating */
                [window setLevel:NSFloatingWindowLevel];
                [window setHidesOnDeactivate:YES];
                return;
            }
        }
        /* Set the window back to a normal window */
        [window setLevel:NSNormalWindowLevel];
        [window setHidesOnDeactivate:NO];
    }
}

/* Allows the user to choose a font using the system's font chooser dialog.
 * Parameters:
 *       currfont: current font
 * Returns:
 *       A malloced buffer with the selected font or NULL on error.
 */
char * API dw_font_choose(const char *currfont)
{
    /* Create the Font Chooser Dialog class. */
    static DWFontChoose *fontDlg = nil;
    DWDialog *dialog;
    NSFont *font = nil;

    if(currfont)
        font = _dw_font_by_name(currfont);

    if(fontDlg)
    {
        dialog = [fontDlg dialog];
        /* If someone is already waiting just return */
        if(dialog)
        {
            return NULL;
        }
    }
    else
    {
        [NSFontManager setFontPanelFactory:[DWFontChoose class]];
        fontDlg = (DWFontChoose *)[DWFontManager fontPanel:YES];
    }

    dialog = dw_dialog_new(fontDlg);
    if(font)
        [DWFontManager setSelectedFont:font isMultiple:NO];
    else
        [DWFontManager setSelectedFont:[NSFont fontWithName:@"Helvetica" size:9.0] isMultiple:NO];
    [fontDlg setDialog:dialog];
    [DWFontManager orderFrontFontPanel:DWFontManager];


    /* Wait for them to pick a color */
    font = (NSFont *)dw_dialog_wait(dialog);
    if(font)
    {
        NSString *fontname = [font displayName];
        NSString *output = [NSString stringWithFormat:@"%d.%s", (int)[font pointSize], [fontname UTF8String]];
        return strdup([output UTF8String]);
    }
    return NULL;
}

/* Internal function to return a pointer to an item struct
 * with information about the packing information regarding object.
 */
Item *_dw_box_item(id object)
{
    /* Find the item within the box it is packed into */
    if([object isKindOfClass:[DWBox class]] || [object isKindOfClass:[DWGroupBox class]] || [object isKindOfClass:[NSControl class]])
    {
        DWBox *parent = (DWBox *)[object superview];

        /* Some controls are embedded in scrollviews...
         * so get the parent of the scrollview in that case.
         */
        if([object isKindOfClass:[NSTableView class]] && [parent isMemberOfClass:[NSClipView class]])
        {
            object = [parent superview];
            parent = (DWBox *)[object superview];
        }

        if([parent isKindOfClass:[DWBox class]] || [parent isKindOfClass:[DWGroupBox class]])
        {
            Box *thisbox = [parent box];
            Item *thisitem = thisbox->items;
            int z;

            for(z=0;z<thisbox->count;z++)
            {
                if(thisitem[z].hwnd == object)
                    return &thisitem[z];
            }
        }
    }
    return NULL;
}

/*
 * Sets the font used by a specified window (widget) handle.
 * Parameters:
 *          handle: The window (widget) handle.
 *          fontname: Name and size of the font in the form "size.fontname"
 */
int API dw_window_set_font(HWND handle, const char *fontname)
{
    NSFont *font = fontname ? _dw_font_by_name(fontname) :
    (DWDefaultFont ? DWDefaultFont : [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]);

    if(font)
    {
        id object = _dw_text_handle(handle);
        if([object window])
        {
            [object lockFocus];
            [font set];
            [object unlockFocus];
        }
        if([object isMemberOfClass:[DWGroupBox class]])
        {
            [object setTitleFont:font];
        }
        else if([object isMemberOfClass:[DWMLE class]])
        {
            DWMLE *mle = object;
            
            [[mle textStorage] setFont:font];
        }
        else if([object isKindOfClass:[NSControl class]])
        {
            [object setFont:font];
            [[object cell] setFont:font];
        }
        else if([object isMemberOfClass:[DWRender class]])
        {
            DWRender *render = object;

            [render setFont:font];
        }
        else
            return DW_ERROR_UNKNOWN;
        /* If we changed the text... */
        Item *item = _dw_box_item(handle);

        /* Check to see if any of the sizes need to be recalculated */
        if(item && (item->origwidth == DW_SIZE_AUTO || item->origheight == DW_SIZE_AUTO))
        {
            _dw_control_size(handle, item->origwidth == DW_SIZE_AUTO ? &item->width : NULL, item->origheight == DW_SIZE_AUTO ? &item->height : NULL);
            /* Queue a redraw on the top-level window */
            _dw_redraw([object window], TRUE);
        }
        return DW_ERROR_NONE;
    }
    return DW_ERROR_UNKNOWN;
}

/*
 * Returns the current font for the specified window
 * Parameters:
 *           handle: The window handle from which to obtain the font.
 */
char * API dw_window_get_font(HWND handle)
{
    id object = _dw_text_handle(handle);
    NSFont *font = nil;

    if([object isMemberOfClass:[DWGroupBox class]])
    {
        font = [object titleFont];
    }
    else if([object isKindOfClass:[NSControl class]] || [object isMemberOfClass:[DWRender class]])
    {
         font = [object font];
    }
    if(font)
    {
        NSString *fontname = [font displayName];
        NSString *output = [NSString stringWithFormat:@"%d.%s", (int)[font pointSize], [fontname UTF8String]];
        return strdup([output UTF8String]);
    }
    return NULL;
}

/*
 * Destroys a window and all of it's children.
 * Parameters:
 *           handle: The window handle to destroy.
 */
DW_FUNCTION_DEFINITION(dw_window_destroy, int, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_RETURN(dw_window_destroy, int)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    DW_LOCAL_POOL_IN;
    id object = handle;
    int retval = 0;

    /* Handle destroying a top-level window */
    if([ object isKindOfClass:[ NSWindow class ] ])
    {
        DWWindow *window = handle;
        [window close];
    }
    /* Handle removing menu items from menus */
    else if([ object isKindOfClass:[NSMenuItem class]])
    {
        NSMenu *menu = [object menu];

        [menu removeItem:object];
    }
    /* Handle destroying a control or box */
    else if([object isKindOfClass:[NSView class]] || [object isKindOfClass:[NSControl class]])
    {
        DWBox *parent = (DWBox *)[object superview];

        /* Some controls are embedded in scrollviews...
         * so get the parent of the scrollview in that case.
         */
        if(([object isKindOfClass:[NSTableView class]] || [object isMemberOfClass:[DWMLE class]])
            && [parent isMemberOfClass:[NSClipView class]])
        {
            object = [parent superview];
            parent = (DWBox *)[object superview];
        }

        if([parent isKindOfClass:[DWBox class]] || [parent isKindOfClass:[DWGroupBox class]])
        {
            id window = [object window];
            Box *thisbox = [parent box];
            int z, index = -1;
            Item *tmpitem = NULL, *thisitem = thisbox->items;

            if(!thisitem)
                thisbox->count = 0;

            for(z=0;z<thisbox->count;z++)
            {
                if(thisitem[z].hwnd == object)
                    index = z;
            }

            if(index != -1)
            {
                [object removeFromSuperview];

                if(thisbox->count > 1)
                {
                    tmpitem = calloc(sizeof(Item), (thisbox->count-1));

                    /* Copy all but the current entry to the new list */
                    for(z=0;z<index;z++)
                    {
                        tmpitem[z] = thisitem[z];
                    }
                    for(z=index+1;z<thisbox->count;z++)
                    {
                        tmpitem[z-1] = thisitem[z];
                    }
                }

                thisbox->items = tmpitem;
                if(thisitem)
                    free(thisitem);
                if(tmpitem)
                    thisbox->count--;
                else
                    thisbox->count = 0;

                /* Queue a redraw on the top-level window */
                _dw_redraw(window, TRUE);
            }
        }
    }
    DW_LOCAL_POOL_OUT;
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Gets the text used for a given window.
 * Parameters:
 *       handle: Handle to the window.
 * Returns:
 *       text: The text associsated with a given window.
 */
DW_FUNCTION_DEFINITION(dw_window_get_text, char *, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_RETURN(dw_window_get_text, char *)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    id object = _dw_text_handle(handle);
    char *retval = NULL;

    if([ object isKindOfClass:[ NSWindow class ] ] || [ object isKindOfClass:[ NSButton class ] ])
    {
        id window = object;
        NSString *nsstr = [ window title];

        retval = strdup([ nsstr UTF8String ]);
    }
    else if([ object isKindOfClass:[ NSControl class ] ])
    {
        NSControl *control = object;
        NSString *nsstr = [ control stringValue];

        retval = strdup([ nsstr UTF8String ]);
    }
    DW_FUNCTION_RETURN_THIS(retval);
}

/*
 * Sets the text used for a given window.
 * Parameters:
 *       handle: Handle to the window.
 *       text: The text associsated with a given window.
 */
DW_FUNCTION_DEFINITION(dw_window_set_text, void, HWND handle, const char *text)
DW_FUNCTION_ADD_PARAM2(handle, text)
DW_FUNCTION_NO_RETURN(dw_window_set_text)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, text, char *)
{
    DW_FUNCTION_INIT;
    id object = _dw_text_handle(handle);

    if([ object isKindOfClass:[ NSWindow class ] ] || [ object isKindOfClass:[ NSButton class ] ])
        [object setTitle:[ NSString stringWithUTF8String:text ]];
    else if([ object isKindOfClass:[ NSControl class ] ])
    {
        NSControl *control = object;
        [control setStringValue:[ NSString stringWithUTF8String:text ]];
    }
    else if([object isMemberOfClass:[DWGroupBox class]])
    {
       DWGroupBox *groupbox = object;
       [groupbox setTitle:[NSString stringWithUTF8String:text]];
    }
    else
        return;
    /* If we changed the text... */
    Item *item = _dw_box_item(handle);

    /* Check to see if any of the sizes need to be recalculated */
    if(item && (item->origwidth == DW_SIZE_AUTO || item->origheight == DW_SIZE_AUTO))
    {
      int newwidth, newheight;

      _dw_control_size(handle, &newwidth, &newheight);

      /* Only update the item and redraw the window if it changed */
      if((item->origwidth == DW_SIZE_AUTO && item->width != newwidth) ||
         (item->origheight == DW_SIZE_AUTO && item->height != newheight))
      {
         if(item->origwidth == DW_SIZE_AUTO)
            item->width = newwidth;
         if(item->origheight == DW_SIZE_AUTO)
            item->height = newheight;
         /* Queue a redraw on the top-level window */
         _dw_redraw([object window], TRUE);
      }
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the text used for a given window's floating bubble help.
 * Parameters:
 *       handle: Handle to the window (widget).
 *       bubbletext: The text in the floating bubble tooltip.
 */
DW_FUNCTION_DEFINITION(dw_window_set_tooltip, void, HWND handle, const char *bubbletext)
DW_FUNCTION_ADD_PARAM2(handle, bubbletext)
DW_FUNCTION_NO_RETURN(dw_window_set_tooltip)
DW_FUNCTION_RESTORE_PARAM2(handle, HWND, bubbletext, char *)
{
    DW_FUNCTION_INIT;
    id object = handle;
    if(bubbletext && *bubbletext)
        [object setToolTip:[NSString stringWithUTF8String:bubbletext]];
    else
        [object setToolTip:nil];
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Disables given window (widget).
 * Parameters:
 *       handle: Handle to the window.
 */
DW_FUNCTION_DEFINITION(dw_window_disable, void, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_NO_RETURN(dw_window_disable)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[NSScrollView class]])
    {
        NSScrollView *sv = handle;
        object = [sv documentView];
    }
    if([object isKindOfClass:[NSControl class]] || [object isKindOfClass:[NSMenuItem class]])
    {
        [object setEnabled:NO];
    }
    if([object isKindOfClass:[NSTextView class]])
    {
        NSTextView *mle = object;

        [mle setEditable:NO];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Enables given window (widget).
 * Parameters:
 *       handle: Handle to the window.
 */
DW_FUNCTION_DEFINITION(dw_window_enable, void, HWND handle)
DW_FUNCTION_ADD_PARAM1(handle)
DW_FUNCTION_NO_RETURN(dw_window_enable)
DW_FUNCTION_RESTORE_PARAM1(handle, HWND)
{
    DW_FUNCTION_INIT;
    id object = handle;

    if([object isMemberOfClass:[NSScrollView class]])
    {
        NSScrollView *sv = handle;
        object = [sv documentView];
    }
    if([object isKindOfClass:[NSControl class]] || [object isKindOfClass:[NSMenuItem class]])
    {
        [object setEnabled:YES];
    }
    if([object isKindOfClass:[NSTextView class]])
    {
        NSTextView *mle = object;

        [mle setEditable:YES];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the bitmap used for a given static window.
 * Parameters:
 *       handle: Handle to the window.
 *       id: An ID to be used to specify the icon,
 *           (pass 0 if you use the filename param)
 *       filename: a path to a file (Bitmap on OS/2 or
 *                 Windows and a pixmap on Unix, pass
 *                 NULL if you use the id param)
 * Returns:
 *        DW_ERROR_NONE on success.
 *        DW_ERROR_UNKNOWN if the parameters were invalid.
 *        DW_ERROR_GENERAL if the bitmap was unable to be loaded.
 */
int API dw_window_set_bitmap_from_data(HWND handle, unsigned long cid, const char *data, int len)
{
    id object = handle;
    int retval = DW_ERROR_UNKNOWN;

    if([object isKindOfClass:[NSImageView class]] || [object isKindOfClass:[NSButton class]])
    {
        if(data)
        {
            DW_LOCAL_POOL_IN;
            NSData *thisdata = [NSData dataWithBytes:data length:len];
            NSImage *pixmap = [[[NSImage alloc] initWithData:thisdata] autorelease];

            if(pixmap)
            {
                [object setImage:pixmap];
                /* If we changed the bitmap... */
                Item *item = _dw_box_item(handle);

                /* Check to see if any of the sizes need to be recalculated */
                if(item && (item->origwidth == DW_SIZE_AUTO || item->origheight == DW_SIZE_AUTO))
                {
                    _dw_control_size(handle, item->origwidth == DW_SIZE_AUTO ? &item->width : NULL, item->origheight == DW_SIZE_AUTO ? &item->height : NULL);
                    /* Queue a redraw on the top-level window */
                    _dw_redraw([object window], TRUE);
                }
                retval = DW_ERROR_NONE;
            }
            else
                retval = DW_ERROR_GENERAL;
            DW_LOCAL_POOL_OUT;
        }
        else
            return dw_window_set_bitmap(handle, cid, NULL);
    }
    return retval;
}

/*
 * Sets the bitmap used for a given static window.
 * Parameters:
 *       handle: Handle to the window.
 *       id: An ID to be used to specify the icon,
 *           (pass 0 if you use the filename param)
 *       filename: a path to a file (Bitmap on OS/2 or
 *                 Windows and a pixmap on Unix, pass
 *                 NULL if you use the id param)
 * Returns:
 *        DW_ERROR_NONE on success.
 *        DW_ERROR_UNKNOWN if the parameters were invalid.
 *        DW_ERROR_GENERAL if the bitmap was unable to be loaded.
 */
int API dw_window_set_bitmap(HWND handle, unsigned long resid, const char *filename)
{
    id object = handle;
    int retval = DW_ERROR_UNKNOWN;
    DW_LOCAL_POOL_IN;

    if([object isKindOfClass:[NSImageView class]] || [object isKindOfClass:[NSButton class]])
    {
        NSImage *bitmap = nil;

        if(filename)
        {
            char *ext = _dw_get_image_extension( filename );
            NSString *nstr = [ NSString stringWithUTF8String:filename ];

            bitmap = [[[NSImage alloc] initWithContentsOfFile:nstr] autorelease];

            if(!bitmap && ext)
            {
                nstr = [nstr stringByAppendingString: [NSString stringWithUTF8String:ext]];
                bitmap = [[[NSImage alloc] initWithContentsOfFile:nstr] autorelease];
            }
            if(!bitmap)
                retval = DW_ERROR_GENERAL;
        }
        if(!bitmap && resid > 0 && resid < 65536)
        {
            bitmap = _dw_icon_load(resid);
            if(!bitmap)
                retval = DW_ERROR_GENERAL;
        }

        if(bitmap)
        {
            [object setImage:bitmap];

            /* If we changed the bitmap... */
            Item *item = _dw_box_item(handle);

            /* Check to see if any of the sizes need to be recalculated */
            if(item && (item->origwidth == DW_SIZE_AUTO || item->origheight == DW_SIZE_AUTO))
            {
                _dw_control_size(handle, item->origwidth == DW_SIZE_AUTO ? &item->width : NULL, item->origheight == DW_SIZE_AUTO ? &item->height : NULL);
                /* Queue a redraw on the top-level window */
                _dw_redraw([object window], TRUE);
            }
            retval = DW_ERROR_NONE;
        }
    }
    DW_LOCAL_POOL_OUT;
    return retval;
}

/*
 * Sets the icon used for a given window.
 * Parameters:
 *       handle: Handle to the window.
 *       id: An ID to be used to specify the icon.
 */
void API dw_window_set_icon(HWND handle, HICN icon)
{
    /* This isn't needed, it is loaded from the bundle */
}

/*
 * Gets the child window handle with specified ID.
 * Parameters:
 *       handle: Handle to the parent window.
 *       id: Integer ID of the child.
 */
HWND API dw_window_from_id(HWND handle, int cid)
{
    NSObject *object = handle;
    NSView *view = handle;
    if([ object isKindOfClass:[ NSWindow class ] ])
    {
        NSWindow *window = handle;
        view = [window contentView];
    }
    return [view viewWithTag:cid];
}

/*
 * Minimizes or Iconifies a top-level window.
 * Parameters:
 *           handle: The window handle to minimize.
 */
int API dw_window_minimize(HWND handle)
{
    NSWindow *window = handle;
    [window miniaturize:nil];
    return 0;
}

/* Causes entire window to be invalidated and redrawn.
 * Parameters:
 *           handle: Toplevel window handle to be redrawn.
 */
void API dw_window_redraw(HWND handle)
{
    DWWindow *window = handle;
    [window setRedraw:YES];
    [[window contentView] showWindow];
#ifndef BUILDING_FOR_MOJAVE
    [window flushWindow];
#endif
    [window setRedraw:NO];
}

/*
 * Makes the window topmost.
 * Parameters:
 *           handle: The window handle to make topmost.
 */
int API dw_window_raise(HWND handle)
{
    NSWindow *window = handle;
    [window orderFront:nil];
    return DW_ERROR_NONE;
}

/*
 * Makes the window bottommost.
 * Parameters:
 *           handle: The window handle to make bottommost.
 */
int API dw_window_lower(HWND handle)
{
    NSWindow *window = handle;
    [window orderBack:nil];
    return DW_ERROR_NONE;
}

/*
 * Sets the size of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          width: New width in pixels.
 *          height: New height in pixels.
 */
DW_FUNCTION_DEFINITION(dw_window_set_size, void, HWND handle, ULONG width, ULONG height)
DW_FUNCTION_ADD_PARAM3(handle, width, height)
DW_FUNCTION_NO_RETURN(dw_window_set_size)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, width, ULONG, height, ULONG)
{
    DW_FUNCTION_INIT;
    NSObject *object = handle;

    if([ object isMemberOfClass:[ DWWindow class ] ])
    {
        DWWindow *window = handle;
        Box *thisbox;
        NSRect content, frame = NSMakeRect(0, 0, width, height);

        /* Convert the external frame size to internal content size */
        content = [NSWindow contentRectForFrameRect:frame styleMask:[window styleMask]];

        /*
         * The following is an attempt to dynamically size a window based on the size of its
         * children before realization. Only applicable when width or height is less than one.
         */
        if((width < 1 || height < 1) && (thisbox = (Box *)[[window contentView] box]))
        {
            int depth = 0;

            /* Calculate space requirements */
            _dw_resize_box(thisbox, &depth, (int)width, (int)height, 1);

            /* Update components that need auto-sizing */
            if(width < 1) content.size.width = thisbox->minwidth;
            if(height < 1) content.size.height = thisbox->minheight;
        }

        /* Finally set the size */
        [window setContentSize:content.size];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Gets the size the system thinks the widget should be.
 * Parameters:
 *       handle: Window handle of the item to be back.
 *       width: Width in pixels of the item or NULL if not needed.
 *       height: Height in pixels of the item or NULL if not needed.
 */
void API dw_window_get_preferred_size(HWND handle, int *width, int *height)
{
    id object = handle;

    if([object isMemberOfClass:[DWWindow class]])
    {
        Box *thisbox;

        if((thisbox = (Box *)[[object contentView] box]))
        {
            int depth = 0;
            NSRect frame;

            /* Calculate space requirements */
            _dw_resize_box(thisbox, &depth, 0, 0, 1);

            /* Figure out the border size */
            frame = [NSWindow frameRectForContentRect:NSMakeRect(0, 0, thisbox->minwidth, thisbox->minheight) styleMask:[object styleMask]];

            /* Return what was requested */
            if(width) *width = frame.size.width;
            if(height) *height = frame.size.height;
        }
    }
    else if([object isMemberOfClass:[DWBox class]])
    {
        Box *thisbox;

        if((thisbox = (Box *)[object box]))
        {
            int depth = 0;

            /* Calculate space requirements */
            _dw_resize_box(thisbox, &depth, 0, 0, 1);

            /* Return what was requested */
            if(width) *width = thisbox->minwidth;
            if(height) *height = thisbox->minheight;
        }
    }
    else
        _dw_control_size(handle, width, height);
}

/*
 * Sets the gravity of a given window (widget).
 * Gravity controls which corner of the screen and window the position is relative to.
 * Parameters:
 *          handle: Window (widget) handle.
 *          horz: DW_GRAV_LEFT (default), DW_GRAV_RIGHT or DW_GRAV_CENTER.
 *          vert: DW_GRAV_TOP (default), DW_GRAV_BOTTOM or DW_GRAV_CENTER.
 */
void API dw_window_set_gravity(HWND handle, int horz, int vert)
{
    dw_window_set_data(handle, "_dw_grav_horz", DW_INT_TO_POINTER(horz));
    dw_window_set_data(handle, "_dw_grav_vert", DW_INT_TO_POINTER(vert));
}

/* Convert the coordinates based on gravity */
void _handle_gravity(HWND handle, long *x, long *y, unsigned long width, unsigned long height)
{
    int horz = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_grav_horz"));
    int vert = DW_POINTER_TO_INT(dw_window_get_data(handle, "_dw_grav_vert"));
    id object = handle;

    /* Do any gravity calculations */
    if(horz || (vert & 0xf) != DW_GRAV_BOTTOM)
    {
        long newx = *x, newy = *y;

        /* Handle horizontal center gravity */
        if((horz & 0xf) == DW_GRAV_CENTER)
            newx += ((dw_screen_width() / 2) - (width / 2));
        /* Handle right gravity */
        else if((horz & 0xf) == DW_GRAV_RIGHT)
            newx = dw_screen_width() - width - *x;
        /* Handle vertical center gravity */
        if((vert & 0xf) == DW_GRAV_CENTER)
            newy += ((dw_screen_height() / 2) - (height / 2));
        else if((vert & 0xf) == DW_GRAV_TOP)
            newy = dw_screen_height() - height - *y;

        /* Save the new values */
        *x = newx;
        *y = newy;
    }
    /* Adjust the values to avoid Dock/Menubar if requested */
    if((horz | vert) & DW_GRAV_OBSTACLES)
    {
        NSRect visiblerect = [[object screen] visibleFrame];
        NSRect totalrect = [[object screen] frame];

        if(horz & DW_GRAV_OBSTACLES)
        {
            if((horz & 0xf) == DW_GRAV_LEFT)
                *x += visiblerect.origin.x;
            else if((horz & 0xf) == DW_GRAV_RIGHT)
                *x -= (totalrect.origin.x + totalrect.size.width) - (visiblerect.origin.x + visiblerect.size.width);
        }
        if(vert & DW_GRAV_OBSTACLES)
        {
            if((vert & 0xf) == DW_GRAV_BOTTOM)
                *y += visiblerect.origin.y;
            else if((vert & 0xf) == DW_GRAV_TOP)
                *y -= (totalrect.origin.y + totalrect.size.height) - (visiblerect.origin.y + visiblerect.size.height);
        }
    }
}

/*
 * Sets the position of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          x: X location from the bottom left.
 *          y: Y location from the bottom left.
 */
DW_FUNCTION_DEFINITION(dw_window_set_pos, void, HWND handle, LONG x, LONG y)
DW_FUNCTION_ADD_PARAM3(handle, x, y)
DW_FUNCTION_NO_RETURN(dw_window_set_pos)
DW_FUNCTION_RESTORE_PARAM3(handle, HWND, x, LONG, y, LONG)
{
    DW_FUNCTION_INIT;
    NSObject *object = handle;

    if([ object isMemberOfClass:[ DWWindow class ] ])
    {
        DWWindow *window = handle;
        NSPoint point;
        NSSize size = [[window contentView] frame].size;

        /* Can't position an unsized window, so attempt to auto-size */
        if(size.width <= 1 || size.height <= 1)
        {
            /* Determine the contents size */
            dw_window_set_size(handle, 0, 0);
        }

        size = [window frame].size;
        _handle_gravity(handle, &x, &y, (unsigned long)size.width, (unsigned long)size.height);

        point.x = x;
        point.y = y;

        [window setFrameOrigin:point];
        /* Position set manually... don't auto-position */
        [window setShown:YES];
    }
    DW_FUNCTION_RETURN_NOTHING;
}

/*
 * Sets the position and size of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          x: X location from the bottom left.
 *          y: Y location from the bottom left.
 *          width: Width of the widget.
 *          height: Height of the widget.
 */
void API dw_window_set_pos_size(HWND handle, LONG x, LONG y, ULONG width, ULONG height)
{
    dw_window_set_size(handle, width, height);
    dw_window_set_pos(handle, x, y);
}

/*
 * Gets the position and size of a given window (widget).
 * Parameters:
 *          handle: Window (widget) handle.
 *          x: X location from the bottom left.
 *          y: Y location from the bottom left.
 *          width: Width of the widget.
 *          height: Height of the widget.
 */
void API dw_window_get_pos_size(HWND handle, LONG *x, LONG *y, ULONG *width, ULONG *height)
{
    NSObject *object = handle;

    if([ object isKindOfClass:[ NSWindow class ] ])
    {
        NSWindow *window = handle;
        NSRect rect = [window frame];
        if(x)
            *x = rect.origin.x;
        if(y)
            *y = [[window screen] frame].size.height - rect.origin.y - rect.size.height;
        if(width)
            *width = rect.size.width;
        if(height)
            *height = rect.size.height;
        return;
    }
    else if([ object isKindOfClass:[ NSControl class ] ])
    {
        NSControl *control = handle;
        NSRect rect = [control frame];
        if(x)
            *x = rect.origin.x;
        if(y)
            *y = rect.origin.y;
        if(width)
            *width = rect.size.width;
        if(height)
            *height = rect.size.height;
        return;
    }
}

/*
 * Returns the width of the screen.
 */
int API dw_screen_width(void)
{
    NSRect screenRect = [[NSScreen mainScreen] frame];
    return screenRect.size.width;
}

/*
 * Returns the height of the screen.
 */
int API dw_screen_height(void)
{
    NSRect screenRect = [[NSScreen mainScreen] frame];
    return screenRect.size.height;
}

/* This should return the current color depth */
unsigned long API dw_color_depth_get(void)
{
    NSWindowDepth screenDepth = [[NSScreen mainScreen] depth];
    return NSBitsPerPixelFromDepth(screenDepth);
}

/*
 * Creates a new system notification if possible.
 * Parameters:
 *         title: The short title of the notification.
 *         imagepath: Path to an image to display or NULL if none.
 *         description: A longer description of the notification,
 *                      or NULL if none is necessary.
 * Returns:
 *         A handle to the notification which can be used to attach a "clicked" event if desired,
 *         or NULL if it fails or notifications are not supported by the system.
 * Remarks:
 *          This will create a system notification that will show in the notifaction panel
 *          on supported systems, which may be clicked to perform another task.
 */
HWND dw_notification_new(const char *title, const char *imagepath, const char *description, ...)
{
#ifdef BUILDING_FOR_MOUNTAIN_LION
    char outbuf[1025] = {0};
    HWND retval = NULL;

    if(description)
    {
        va_list args;

        va_start(args, description);
        vsnprintf(outbuf, 1024, description, args);
        va_end(args);
    }

#ifdef BUILDING_FOR_MOJAVE
    /* Configure the notification's payload. */
    if (@available(macOS 10.14, *))
    {
        if([[NSBundle mainBundle] bundleIdentifier] != nil)
        {
            UNMutableNotificationContent* notification = [[UNMutableNotificationContent alloc] init];

            if(notification)
            {
                notification.title = [NSString stringWithUTF8String:title];
                if(description)
                    notification.body = [NSString stringWithUTF8String:outbuf];
                if(imagepath)
                {
                    NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:imagepath]];
                    NSError *error;
                    UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"imageID"
                                                                URL:url
                                                            options:nil
                                                              error:&error];
                    if(attachment)
                        notification.attachments = @[attachment];
                }
                retval = notification;
            }
        }
    }
    else
#endif
    {
#ifndef BUILDING_FOR_BIG_SUR
        /* Fallback on earlier versions */
        NSUserNotification *notification = [[NSUserNotification alloc] init];

        if(notification)
        {
            notification.title = [NSString stringWithUTF8String:title];
            notification.informativeText = [NSString stringWithUTF8String:outbuf];
            if(imagepath)
                notification.contentImage = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:imagepath]];
            retval = notification;
        }
#endif
    }
    return retval;
#else
   return NULL;
#endif
}

/*
 * Sends a notification created by dw_notification_new() after attaching signal handler.
 * Parameters:
 *         notification: The handle to the notification returned by dw_notification_new().
 * Returns:
 *         DW_ERROR_NONE on success, DW_ERROR_UNKNOWN on error or not supported.
 */
int dw_notification_send(HWND notification)
{
#ifdef BUILDING_FOR_MOUNTAIN_LION
    if(notification)
    {
        NSString *notid = [NSString stringWithFormat:@"dw-notification-%llu", DW_POINTER_TO_ULONGLONG(notification)];
        
#ifdef BUILDING_FOR_MOJAVE
        /* Schedule the notification. */
        if (@available(macOS 10.14, *))
        {
            if([[NSBundle mainBundle] bundleIdentifier] != nil)
            {
                UNMutableNotificationContent* content = (UNMutableNotificationContent *)notification;
                UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:notid content:content trigger:nil];

                UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
                [center addNotificationRequest:request withCompletionHandler:nil];
            }
        }
        else
#endif
        {
#ifndef BUILDING_FOR_BIG_SUR
            /* Fallback on earlier versions */
            NSUserNotification *request = (NSUserNotification *)notification;
            request.identifier = notid;
            [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:request];
#endif
        }
        return DW_ERROR_NONE;
    }
#endif
    return DW_ERROR_UNKNOWN;
}


/*
 * Returns some information about the current operating environment.
 * Parameters:
 *       env: Pointer to a DWEnv struct.
 */
void dw_environment_query(DWEnv *env)
{
    memset(env, '\0', sizeof(DWEnv));
    strcpy(env->osName, "MacOS");

    strncpy(env->buildDate, __DATE__, sizeof(env->buildDate)-1);
    strncpy(env->buildTime, __TIME__, sizeof(env->buildTime)-1);
#ifdef WK_API_ENABLED
   strncpy(env->htmlEngine, "WEBKIT", sizeof(env->htmlEngine)-1);
#else
   strncpy(env->htmlEngine, "WEBVIEW", sizeof(env->htmlEngine)-1);
#endif
    env->DWMajorVersion = DW_MAJOR_VERSION;
    env->DWMinorVersion = DW_MINOR_VERSION;
#ifdef VER_REV
    env->DWSubVersion = VER_REV;
#else
    env->DWSubVersion = DW_SUB_VERSION;
#endif

    env->MajorVersion = DWOSMajor;
    env->MinorVersion = DWOSMinor;
    env->MajorBuild = DWOSBuild;
}

/*
 * Emits a beep.
 * Parameters:
 *       freq: Frequency.
 *       dur: Duration.
 */
void API dw_beep(int freq, int dur)
{
    NSBeep();
}

/* Call this after drawing to the screen to make sure
 * anything you have drawn is visible.
 */
void API dw_flush(void)
{
    /* This may need to be thread specific */
    [DWObj performSelectorOnMainThread:@selector(doFlush:) withObject:nil waitUntilDone:NO];
}

/* Functions for managing the user data lists that are associated with
 * a given window handle.  Used in dw_window_set_data() and
 * dw_window_get_data().
 */
UserData *_dw_find_userdata(UserData **root, const char *varname)
{
    UserData *tmp = *root;

    while(tmp)
    {
        if(strcasecmp(tmp->varname, varname) == 0)
            return tmp;
        tmp = tmp->next;
    }
    return NULL;
}

int _dw_new_userdata(UserData **root, const char *varname, void *data)
{
    UserData *new = _dw_find_userdata(root, varname);

    if(new)
    {
        new->data = data;
        return TRUE;
    }
    else
    {
        new = malloc(sizeof(UserData));
        if(new)
        {
            new->varname = strdup(varname);
            new->data = data;

            new->next = NULL;

            if (!*root)
                *root = new;
            else
            {
                UserData *prev = *root, *tmp = prev->next;

                while(tmp)
                {
                    prev = tmp;
                    tmp = tmp->next;
                }
                prev->next = new;
            }
            return TRUE;
        }
    }
    return FALSE;
}

int _dw_remove_userdata(UserData **root, const char *varname, int all)
{
    UserData *prev = NULL, *tmp = *root;

    while(tmp)
    {
        if(all || strcasecmp(tmp->varname, varname) == 0)
        {
            if(!prev)
            {
                *root = tmp->next;
                free(tmp->varname);
                free(tmp);
                if(!all)
                    return 0;
                tmp = *root;
            }
            else
            {
                /* If all is true we should
                 * never get here.
                 */
                prev->next = tmp->next;
                free(tmp->varname);
                free(tmp);
                return 0;
            }
        }
        else
        {
            prev = tmp;
            tmp = tmp->next;
        }
    }
    return 0;
}

/*
 * Add a named user data item to a window handle.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       dataname: A string pointer identifying which signal to be hooked.
 *       data: User data to be passed to the handler function.
 */
void dw_window_set_data(HWND window, const char *dataname, void *data)
{
    id object = window;
    if([object isMemberOfClass:[DWWindow class]])
    {
        NSWindow *win = window;
        object = [win contentView];
    }
    else if([object isMemberOfClass:[NSScrollView class]])
    {
        NSScrollView *sv = window;
        object = [sv documentView];
    }
    WindowData *blah = (WindowData *)[object userdata];

    if(!blah)
    {
        if(!dataname)
            return;

        blah = calloc(1, sizeof(WindowData));
        [object setUserdata:blah];
    }

    if(data)
        _dw_new_userdata(&(blah->root), dataname, data);
    else
    {
        if(dataname)
            _dw_remove_userdata(&(blah->root), dataname, FALSE);
        else
            _dw_remove_userdata(&(blah->root), NULL, TRUE);
    }
}

/*
 * Gets a named user data item to a window handle.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       dataname: A string pointer identifying which signal to be hooked.
 *       data: User data to be passed to the handler function.
 */
void *dw_window_get_data(HWND window, const char *dataname)
{
    id object = window;
    if([object isMemberOfClass:[DWWindow class]])
    {
        NSWindow *win = window;
        object = [win contentView];
    }
    else if([object isMemberOfClass:[NSScrollView class]])
    {
        NSScrollView *sv = window;
        object = [sv documentView];
    }
    WindowData *blah = (WindowData *)[object userdata];

    if(blah && blah->root && dataname)
    {
        UserData *ud = _dw_find_userdata(&(blah->root), dataname);
        if(ud)
            return ud->data;
    }
    return NULL;
}

/*
 * Compare two window handles.
 * Parameters:
 *       window1: First window handle to compare.
 *       window2: Second window handle to compare.
 * Returns:
 *       TRUE if the windows are the same object, FALSE if not.
 */
int API dw_window_compare(HWND window1, HWND window2)
{
    /* If anything special is require to compare... do it
     * here otherwise just compare the handles.
     */
    if(window1 && window2 && window1 == window2)
        return TRUE;
    return FALSE;
}

#define DW_TIMER_MAX 64
static NSTimer *DWTimers[DW_TIMER_MAX];

/*
 * Add a callback to a timer event.
 * Parameters:
 *       interval: Milliseconds to delay between calls.
 *       sigfunc: The pointer to the function to be used as the callback.
 *       data: User data to be passed to the handler function.
 * Returns:
 *       Timer ID for use with dw_timer_disconnect(), 0 on error.
 */
HTIMER API dw_timer_connect(int interval, void *sigfunc, void *data)
{
    int z;

    for(z=0;z<DW_TIMER_MAX;z++)
    {
        if(!DWTimers[z])
        {
            break;
        }
    }

    if(sigfunc && !DWTimers[z])
    {
        NSTimeInterval seconds = (double)interval / 1000.0;
        NSTimer *thistimer = DWTimers[z] = [NSTimer scheduledTimerWithTimeInterval:seconds target:DWHandler selector:@selector(runTimer:) userInfo:nil repeats:YES];
        _dw_new_signal(0, thistimer, z+1, sigfunc, NULL, data);
        return z+1;
    }
    return 0;
}

/*
 * Removes timer callback.
 * Parameters:
 *       id: Timer ID returned by dw_timer_connect().
 */
void API dw_timer_disconnect(HTIMER timerid)
{
    DWSignalHandler *prev = NULL, *tmp = DWRoot;
    NSTimer *thistimer;

    /* 0 is an invalid timer ID */
    if(timerid < 1 || !DWTimers[timerid-1])
        return;

    thistimer = DWTimers[timerid-1];
    DWTimers[timerid-1] = nil;

    [thistimer invalidate];

    while(tmp)
    {
        if(tmp->id == timerid && tmp->window == thistimer)
        {
            if(prev)
            {
                prev->next = tmp->next;
                free(tmp);
                tmp = prev->next;
            }
            else
            {
                DWRoot = tmp->next;
                free(tmp);
                tmp = DWRoot;
            }
        }
        else
        {
            prev = tmp;
            tmp = tmp->next;
        }
    }
}

/*
 * Add a callback to a window event.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       signame: A string pointer identifying which signal to be hooked.
 *       sigfunc: The pointer to the function to be used as the callback.
 *       data: User data to be passed to the handler function.
 */
void API dw_signal_connect(HWND window, const char *signame, void *sigfunc, void *data)
{
    dw_signal_connect_data(window, signame, sigfunc, NULL, data);
}

/*
 * Add a callback to a window event with a closure callback.
 * Parameters:
 *       window: Window handle of signal to be called back.
 *       signame: A string pointer identifying which signal to be hooked.
 *       sigfunc: The pointer to the function to be used as the callback.
 *       discfunc: The pointer to the function called when this handler is removed.
 *       data: User data to be passed to the handler function.
 */
void API dw_signal_connect_data(HWND window, const char *signame, void *sigfunc, void *discfunc, void *data)
{
    ULONG message = 0, msgid = 0;

    /* Handle special case of application delete signal */
    if(!window && signame && strcmp(signame, DW_SIGNAL_DELETE) == 0)
    {
        window = DWApp;
    }

    if(window && signame && sigfunc)
    {
        if((message = _dw_findsigmessage(signame)) != 0)
        {
            _dw_new_signal(message, window, (int)msgid, sigfunc, discfunc, data);
        }
    }
}

/*
 * Removes callbacks for a given window with given name.
 * Parameters:
 *       window: Window handle of callback to be removed.
 */
void API dw_signal_disconnect_by_name(HWND window, const char *signame)
{
    DWSignalHandler *prev = NULL, *tmp = DWRoot;
    ULONG message;

    if(!window || !signame || (message = _dw_findsigmessage(signame)) == 0)
        return;

    while(tmp)
    {
        if(tmp->window == window && tmp->message == message)
        {
            void (*discfunc)(HWND, void *) = tmp->discfunction;

            if(discfunc)
            {
                discfunc(tmp->window, tmp->data);
            }

            if(prev)
            {
                prev->next = tmp->next;
                free(tmp);
                tmp = prev->next;
            }
            else
            {
                DWRoot = tmp->next;
                free(tmp);
                tmp = DWRoot;
            }
        }
        else
        {
            prev = tmp;
            tmp = tmp->next;
        }
    }
}

/*
 * Removes all callbacks for a given window.
 * Parameters:
 *       window: Window handle of callback to be removed.
 */
void API dw_signal_disconnect_by_window(HWND window)
{
    DWSignalHandler *prev = NULL, *tmp = DWRoot;

    while(tmp)
    {
        if(tmp->window == window)
        {
            void (*discfunc)(HWND, void *) = tmp->discfunction;

            if(discfunc)
            {
                discfunc(tmp->window, tmp->data);
            }

            if(prev)
            {
                prev->next = tmp->next;
                free(tmp);
                tmp = prev->next;
            }
            else
            {
                DWRoot = tmp->next;
                free(tmp);
                tmp = DWRoot;
            }
        }
        else
        {
            prev = tmp;
            tmp = tmp->next;
        }
    }
}

/*
 * Removes all callbacks for a given window with specified data.
 * Parameters:
 *       window: Window handle of callback to be removed.
 *       data: Pointer to the data to be compared against.
 */
void API dw_signal_disconnect_by_data(HWND window, void *data)
{
    DWSignalHandler *prev = NULL, *tmp = DWRoot;

    while(tmp)
    {
        if(tmp->window == window && tmp->data == data)
        {
            void (*discfunc)(HWND, void *) = tmp->discfunction;

            if(discfunc)
            {
                discfunc(tmp->window, tmp->data);
            }

            if(prev)
            {
                prev->next = tmp->next;
                free(tmp);
                tmp = prev->next;
            }
            else
            {
                DWRoot = tmp->next;
                free(tmp);
                tmp = DWRoot;
            }
        }
        else
        {
            prev = tmp;
            tmp = tmp->next;
        }
    }
}

void _dw_my_strlwr(char *buf)
{
   int z, len = (int)strlen(buf);

   for(z=0;z<len;z++)
   {
      if(buf[z] >= 'A' && buf[z] <= 'Z')
         buf[z] -= 'A' - 'a';
   }
}

/* Open a shared library and return a handle.
 * Parameters:
 *         name: Base name of the shared library.
 *         handle: Pointer to a module handle,
 *                 will be filled in with the handle.
 */
int dw_module_load(const char *name, HMOD *handle)
{
   int len;
   char *newname;
   char errorbuf[1025];


   if(!handle)
      return -1;

   if((len = (int)strlen(name)) == 0)
      return   -1;

   /* Lenth + "lib" + ".dylib" + NULL */
   newname = malloc(len + 10);

   if(!newname)
      return -1;

   sprintf(newname, "lib%s.dylib", name);
   _dw_my_strlwr(newname);

   *handle = dlopen(newname, RTLD_NOW);
   if(*handle == NULL)
   {
      strncpy(errorbuf, dlerror(), 1024);
      printf("%s\n", errorbuf);
      sprintf(newname, "lib%s.dylib", name);
      *handle = dlopen(newname, RTLD_NOW);
   }

   free(newname);

   return (NULL == *handle) ? -1 : 0;
}

/* Queries the address of a symbol within open handle.
 * Parameters:
 *         handle: Module handle returned by dw_module_load()
 *         name: Name of the symbol you want the address of.
 *         func: A pointer to a function pointer, to obtain
 *               the address.
 */
int dw_module_symbol(HMOD handle, const char *name, void**func)
{
   if(!func || !name)
      return   -1;

   if(strlen(name) == 0)
      return   -1;

   *func = (void*)dlsym(handle, name);
   return   (NULL == *func);
}

/* Frees the shared library previously opened.
 * Parameters:
 *         handle: Module handle returned by dw_module_load()
 */
int dw_module_close(HMOD handle)
{
   if(handle)
      return dlclose(handle);
   return 0;
}

/*
 * Returns the handle to an unnamed mutex semaphore.
 */
HMTX dw_mutex_new(void)
{
    HMTX mutex = malloc(sizeof(pthread_mutex_t));

    pthread_mutex_init(mutex, NULL);
    return mutex;
}

/*
 * Closes a semaphore created by dw_mutex_new().
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 */
void dw_mutex_close(HMTX mutex)
{
   if(mutex)
   {
      pthread_mutex_destroy(mutex);
      free(mutex);
   }
}

/*
 * Tries to gain access to the semaphore, if it can't it blocks.
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 */
void dw_mutex_lock(HMTX mutex)
{
    /* We need to handle locks from the main thread differently...
     * since we can't stop message processing... otherwise we
     * will deadlock... so try to acquire the lock and continue
     * processing messages in between tries.
     */
    if(DWThread == pthread_self())
    {
        while(pthread_mutex_trylock(mutex) != 0)
        {
            /* Process any pending events */
            while(_dw_main_iteration([NSDate dateWithTimeIntervalSinceNow:0.01]))
            {
                /* Just loop */
            }
        }
    }
    else
    {
        pthread_mutex_lock(mutex);
    }
}

/*
 * Tries to gain access to the semaphore.
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 * Returns:
 *       DW_ERROR_NONE on success, DW_ERROR_TIMEOUT if it is already locked.
 */
int API dw_mutex_trylock(HMTX mutex)
{
    if(pthread_mutex_trylock(mutex) == 0)
        return DW_ERROR_NONE;
    return DW_ERROR_TIMEOUT;
}

/*
 * Reliquishes the access to the semaphore.
 * Parameters:
 *       mutex: The handle to the mutex returned by dw_mutex_new().
 */
void dw_mutex_unlock(HMTX mutex)
{
   pthread_mutex_unlock(mutex);
}

/*
 * Returns the handle to an unnamed event semaphore.
 */
HEV dw_event_new(void)
{
   HEV eve = (HEV)malloc(sizeof(struct _dw_unix_event));

   if(!eve)
      return NULL;

   /* We need to be careful here, mutexes on Linux are
    * FAST by default but are error checking on other
    * systems such as FreeBSD and OS/2, perhaps others.
    */
   pthread_mutex_init (&(eve->mutex), NULL);
   pthread_mutex_lock (&(eve->mutex));
   pthread_cond_init (&(eve->event), NULL);

   pthread_mutex_unlock (&(eve->mutex));
   eve->alive = 1;
   eve->posted = 0;

   return eve;
}

/*
 * Resets a semaphore created by dw_event_new().
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int dw_event_reset (HEV eve)
{
   if(!eve)
      return DW_ERROR_NON_INIT;

   pthread_mutex_lock (&(eve->mutex));
   pthread_cond_broadcast (&(eve->event));
   pthread_cond_init (&(eve->event), NULL);
   eve->posted = 0;
   pthread_mutex_unlock (&(eve->mutex));
   return DW_ERROR_NONE;
}

/*
 * Posts a semaphore created by dw_event_new(). Causing all threads
 * waiting on this event in dw_event_wait to continue.
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int dw_event_post (HEV eve)
{
   if(!eve)
      return FALSE;

   pthread_mutex_lock (&(eve->mutex));
   pthread_cond_broadcast (&(eve->event));
   eve->posted = 1;
   pthread_mutex_unlock (&(eve->mutex));
   return 0;
}

/*
 * Waits on a semaphore created by dw_event_new(), until the
 * event gets posted or until the timeout expires.
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int dw_event_wait(HEV eve, unsigned long timeout)
{
    int rc;

    if(!eve)
        return DW_ERROR_NON_INIT;

    pthread_mutex_lock (&(eve->mutex));

    if(eve->posted)
    {
        pthread_mutex_unlock (&(eve->mutex));
        return DW_ERROR_NONE;
    }

    if(timeout != -1)
    {
        struct timeval now;
        struct timespec timeo;

        gettimeofday(&now, 0);
        timeo.tv_sec = now.tv_sec + (timeout / 1000);
        timeo.tv_nsec = now.tv_usec * 1000;
        rc = pthread_cond_timedwait(&(eve->event), &(eve->mutex), &timeo);
    }
    else
        rc = pthread_cond_wait(&(eve->event), &(eve->mutex));
    pthread_mutex_unlock (&(eve->mutex));
    if(!rc)
        return DW_ERROR_NONE;
    if(rc == ETIMEDOUT)
        return DW_ERROR_TIMEOUT;
    return DW_ERROR_GENERAL;
}

/*
 * Closes a semaphore created by dw_event_new().
 * Parameters:
 *       eve: The handle to the event returned by dw_event_new().
 */
int dw_event_close(HEV *eve)
{
   if(!eve || !(*eve))
      return DW_ERROR_NON_INIT;

   pthread_mutex_lock (&((*eve)->mutex));
   pthread_cond_destroy (&((*eve)->event));
   pthread_mutex_unlock (&((*eve)->mutex));
   pthread_mutex_destroy (&((*eve)->mutex));
   free(*eve);
   *eve = NULL;

   return DW_ERROR_NONE;
}

struct _dw_seminfo {
   int fd;
   int waiting;
};

static void _dw_handle_sem(int *tmpsock)
{
   fd_set rd;
   struct _dw_seminfo *array = (struct _dw_seminfo *)malloc(sizeof(struct _dw_seminfo));
   int listenfd = tmpsock[0];
   int bytesread, connectcount = 1, maxfd, z, posted = 0;
   char command;
   sigset_t mask;

   sigfillset(&mask); /* Mask all allowed signals */
   pthread_sigmask(SIG_BLOCK, &mask, NULL);

   /* problems */
   if(tmpsock[1] == -1)
   {
      free(array);
      return;
   }

   array[0].fd = tmpsock[1];
   array[0].waiting = 0;

   /* Free the memory allocated in dw_named_event_new. */
   free(tmpsock);

   while(1)
   {
      FD_ZERO(&rd);
      FD_SET(listenfd, &rd);

      maxfd = listenfd;

      /* Added any connections to the named event semaphore */
      for(z=0;z<connectcount;z++)
      {
         if(array[z].fd > maxfd)
            maxfd = array[z].fd;

         FD_SET(array[z].fd, &rd);
      }

      if(select(maxfd+1, &rd, NULL, NULL, NULL) == -1)
      {
          free(array);
          return;
      }

      if(FD_ISSET(listenfd, &rd))
      {
         struct _dw_seminfo *newarray;
            int newfd = accept(listenfd, 0, 0);

         if(newfd > -1)
         {
            /* Add new connections to the set */
            newarray = (struct _dw_seminfo *)malloc(sizeof(struct _dw_seminfo)*(connectcount+1));
            memcpy(newarray, array, sizeof(struct _dw_seminfo)*(connectcount));

            newarray[connectcount].fd = newfd;
            newarray[connectcount].waiting = 0;

            connectcount++;

            /* Replace old array with new one */
            free(array);
            array = newarray;
         }
      }

      /* Handle any events posted to the semaphore */
      for(z=0;z<connectcount;z++)
      {
         if(FD_ISSET(array[z].fd, &rd))
         {
            if((bytesread = (int)read(array[z].fd, &command, 1)) < 1)
            {
               struct _dw_seminfo *newarray = NULL;

               /* Remove this connection from the set */
               if(connectcount > 1)
               {
                   newarray = (struct _dw_seminfo *)malloc(sizeof(struct _dw_seminfo)*(connectcount-1));
                   if(!z)
                       memcpy(newarray, &array[1], sizeof(struct _dw_seminfo)*(connectcount-1));
                   else
                   {
                       memcpy(newarray, array, sizeof(struct _dw_seminfo)*z);
                       if(z!=(connectcount-1))
                           memcpy(&newarray[z], &array[z+1], sizeof(struct _dw_seminfo)*(connectcount-(z+1)));
                   }
               }
               connectcount--;

               /* Replace old array with new one */
               free(array);
               array = newarray;
            }
            else if(bytesread == 1)
            {
               switch(command)
               {
               case 0:
                  {
                  /* Reset */
                  posted = 0;
                  }
                  break;
               case 1:
                  /* Post */
                  {
                     int s;
                     char tmp = (char)0;

                     posted = 1;

                     for(s=0;s<connectcount;s++)
                     {
                        /* The semaphore has been posted so
                         * we tell all the waiting threads to
                         * continue.
                         */
                        if(array[s].waiting)
                           write(array[s].fd, &tmp, 1);
                     }
                  }
                  break;
               case 2:
                  /* Wait */
                  {
                     char tmp = (char)0;

                     array[z].waiting = 1;

                     /* If we are posted exit immeditately */
                     if(posted)
                        write(array[z].fd, &tmp, 1);
                  }
                  break;
               case 3:
                  {
                     /* Done Waiting */
                     array[z].waiting = 0;
                  }
                  break;
               }
            }
         }
      }
   }
}

/* Using domain sockets on unix for IPC */
/* Create a named event semaphore which can be
 * opened from other processes.
 * Parameters:
 *         eve: Pointer to an event handle to receive handle.
 *         name: Name given to semaphore which can be opened
 *               by other processes.
 */
HEV dw_named_event_new(const char *name)
{
    struct sockaddr_un un;
    int ev, *tmpsock = (int *)malloc(sizeof(int)*2);
    HEV eve;
    DWTID dwthread;

    if(!tmpsock)
        return NULL;

    eve = (HEV)malloc(sizeof(struct _dw_unix_event));

    if(!eve)
    {
        free(tmpsock);
        return NULL;
    }

    tmpsock[0] = socket(AF_UNIX, SOCK_STREAM, 0);
    ev = socket(AF_UNIX, SOCK_STREAM, 0);
    memset(&un, 0, sizeof(un));
    un.sun_family=AF_UNIX;
    mkdir("/tmp/.dw", S_IWGRP|S_IWOTH);
    strcpy(un.sun_path, "/tmp/.dw/");
    strcat(un.sun_path, name);

    /* just to be safe, this should be changed
     * to support multiple instances.
     */
    remove(un.sun_path);

    bind(tmpsock[0], (struct sockaddr *)&un, sizeof(un));
    listen(tmpsock[0], 0);
    connect(ev, (struct sockaddr *)&un, sizeof(un));
    tmpsock[1] = accept(tmpsock[0], 0, 0);

    if(tmpsock[0] < 0 || tmpsock[1] < 0 || ev < 0)
    {
        if(tmpsock[0] > -1)
            close(tmpsock[0]);
        if(tmpsock[1] > -1)
            close(tmpsock[1]);
        if(ev > -1)
            close(ev);
        free(tmpsock);
        free(eve);
        return NULL;
    }

    /* Create a thread to handle this event semaphore */
    pthread_create(&dwthread, NULL, (void *)_dw_handle_sem, (void *)tmpsock);
    eve->alive = ev;
    return eve;
}

/* Open an already existing named event semaphore.
 * Parameters:
 *         eve: Pointer to an event handle to receive handle.
 *         name: Name given to semaphore which can be opened
 *               by other processes.
 */
HEV dw_named_event_get(const char *name)
{
    struct sockaddr_un un;
    HEV eve;
    int ev = socket(AF_UNIX, SOCK_STREAM, 0);
    if(ev < 0)
        return NULL;

    eve = (HEV)malloc(sizeof(struct _dw_unix_event));

    if(!eve)
    {
        close(ev);
        return NULL;
    }

    un.sun_family=AF_UNIX;
    mkdir("/tmp/.dw", S_IWGRP|S_IWOTH);
    strcpy(un.sun_path, "/tmp/.dw/");
    strcat(un.sun_path, name);
    connect(ev, (struct sockaddr *)&un, sizeof(un));
    eve->alive = ev;
    return eve;
}

/* Resets the event semaphore so threads who call wait
 * on this semaphore will block.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 */
int dw_named_event_reset(HEV eve)
{
   /* signal reset */
   char tmp = (char)0;

   if(!eve || eve->alive < 0)
      return 0;

   if(write(eve->alive, &tmp, 1) == 1)
      return 0;
   return 1;
}

/* Sets the posted state of an event semaphore, any threads
 * waiting on the semaphore will no longer block.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 */
int dw_named_event_post(HEV eve)
{

   /* signal post */
   char tmp = (char)1;

   if(!eve || eve->alive < 0)
      return 0;

   if(write(eve->alive, &tmp, 1) == 1)
      return 0;
   return 1;
}

/* Waits on the specified semaphore until it becomes
 * posted, or returns immediately if it already is posted.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 *         timeout: Number of milliseconds before timing out
 *                  or -1 if indefinite.
 */
int dw_named_event_wait(HEV eve, unsigned long timeout)
{
   fd_set rd;
   struct timeval tv, *useme = NULL;
   int retval = 0;
   char tmp;

   if(!eve || eve->alive < 0)
      return DW_ERROR_NON_INIT;

   /* Set the timout or infinite */
   if(timeout != -1)
   {
      tv.tv_sec = timeout / 1000;
      tv.tv_usec = (int)timeout % 1000;

      useme = &tv;
   }

   FD_ZERO(&rd);
   FD_SET(eve->alive, &rd);

   /* Signal wait */
   tmp = (char)2;
   write(eve->alive, &tmp, 1);

   retval = select(eve->alive+1, &rd, NULL, NULL, useme);

   /* Signal done waiting. */
   tmp = (char)3;
   write(eve->alive, &tmp, 1);

   if(retval == 0)
      return DW_ERROR_TIMEOUT;
   else if(retval == -1)
      return DW_ERROR_INTERRUPT;

   /* Clear the entry from the pipe so
    * we don't loop endlessly. :)
    */
   read(eve->alive, &tmp, 1);
   return 0;
}

/* Release this semaphore, if there are no more open
 * handles on this semaphore the semaphore will be destroyed.
 * Parameters:
 *         eve: Handle to the semaphore obtained by
 *              an open or create call.
 */
int dw_named_event_close(HEV eve)
{
   /* Finally close the domain socket,
    * cleanup will continue in _dw_handle_sem.
    */
    if(eve)
    {
        close(eve->alive);
        free(eve);
    }
    return 0;
}

/* Mac specific function to cause garbage collection */
void _dw_pool_drain(void)
{
#if !defined(GARBAGE_COLLECT)
    NSAutoreleasePool *pool = pthread_getspecific(_dw_pool_key);
    [pool drain];
    pool = [[NSAutoreleasePool alloc] init];
    pthread_setspecific(_dw_pool_key, pool);
#endif
}

/*
 * Generally an internal function called from a newly created
 * thread to setup the Dynamic Windows environment for the thread.
 * However it is exported so language bindings can call it when
 * they create threads that require access to Dynamic Windows.
 */
void API _dw_init_thread(void)
{
    /* If we aren't using garbage collection we need autorelease pools */
#if !defined(GARBAGE_COLLECT)
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    pthread_setspecific(_dw_pool_key, pool);
#endif
    _dw_init_colors();
}

/*
 * Generally an internal function called from a terminating
 * thread to cleanup the Dynamic Windows environment for the thread.
 * However it is exported so language bindings can call it when
 * they exit threads that require access to Dynamic Windows.
 */
void API _dw_deinit_thread(void)
{
    NSColor *color;

    /* Release the pool when we are done so we don't leak */
    color = pthread_getspecific(_dw_fg_color_key);
    [color release];
    color = pthread_getspecific(_dw_bg_color_key);
    [color release];
#if !defined(GARBAGE_COLLECT)
    NSAutoreleasePool *pool = pthread_getspecific(_dw_pool_key);
    [pool drain];
#endif
}

/*
 * Setup thread independent pools.
 */
void _dwthreadstart(void *data)
{
    void (*threadfunc)(void *) = NULL;
    void **tmp = (void **)data;

    _dw_init_thread();

    threadfunc = (void (*)(void *))tmp[0];

    /* Start our thread function */
    threadfunc(tmp[1]);

    free(tmp);

    _dw_deinit_thread();
}

/*
 * Sets the default font used on text based widgets.
 * Parameters:
 *           fontname: Font name in Dynamic Windows format.
 */
void API dw_font_set_default(const char *fontname)
{
    NSFont *oldfont = DWDefaultFont;
    DWDefaultFont = nil;
    if(fontname)
    {
        DWDefaultFont = _dw_font_by_name(fontname);
        [DWDefaultFont retain];
    }
    [oldfont release];
}

/* If DWApp is uninitialized, initialize it */
void _dw_app_init(void)
{
    if(!DWApp)
    {
        DWApp = [NSApplication sharedApplication];
        DWAppDel *del = [[DWAppDel alloc] init];
        [DWApp setDelegate:del];
    }
}

/*
 * Initializes the Dynamic Windows engine.
 * Parameters:
 *           newthread: True if this is the only thread.
 *                      False if there is already a message loop running.
 */
int API dw_init(int newthread, int argc, char *argv[])
{
    char *lang = getenv("LANG");

    /* Correct the startup path if run from a bundle */
    if(argc > 0 && argv[0])
    {
        char *pathcopy = strdup(argv[0]);
        char *app = strstr(pathcopy, ".app/");
        char *binname = strrchr(pathcopy, '/');

        if(binname && (binname++) && !_dw_app_id[0])
        {
            /* If we have a binary name, use that for the Application ID instead. */
            snprintf(_dw_app_id, _DW_APP_ID_SIZE, "%s.%s", DW_APP_DOMAIN_DEFAULT, binname);
        }
        if(app)
        {
            char pathbuf[PATH_MAX+1] = { 0 };
            size_t len = (size_t)(app - pathcopy);

            if(len > 0)
            {
                strncpy(_dw_bundle_path, pathcopy, len + 4);
                strcat(_dw_bundle_path, "/Contents/Resources");
            }
            *app = 0;

            getcwd(pathbuf, PATH_MAX);

            /* If run from a bundle the path seems to be / */
            if(strcmp(pathbuf, "/") == 0)
            {
                char *pos = strrchr(pathcopy, '/');

                if(pos)
                {
                    strncpy(pathbuf, pathcopy, (size_t)(pos - pathcopy));
                    chdir(pathbuf);
                }
            }
        }
        if(pathcopy)
            free(pathcopy);
    }

    /* Just in case we can't obtain a path */
    if(!_dw_bundle_path[0])
        getcwd(_dw_bundle_path, PATH_MAX);

    /* Set the locale... if it is UTF-8 pass it
     * directly, otherwise specify UTF-8 explicitly.
     */
    setlocale(LC_ALL, lang && strstr(lang, ".UTF-8") ? lang : "UTF-8");

    /* If we aren't using garbage collection we need autorelease pools */
#if !defined(GARBAGE_COLLECT)
    pthread_key_create(&_dw_pool_key, NULL);
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    pthread_setspecific(_dw_pool_key, pool);
#endif

    /* Get the operating system version */
    NSString *version = [[NSProcessInfo processInfo] operatingSystemVersionString];
    const char *versionstr = [version UTF8String];
    sscanf(versionstr, "Version %d.%d.%d", &DWOSMajor, &DWOSMinor, &DWOSBuild);

    /* Create the application object */
    _dw_app_init();
    /* Create object for handling timers */
    DWHandler = [[DWTimerHandler alloc] init];

    pthread_key_create(&_dw_fg_color_key, NULL);
    pthread_key_create(&_dw_bg_color_key, NULL);
    _dw_init_colors();
    /* Create a default main menu, with just the application menu */
    DWMainMenu = _dw_generate_main_menu();
    [DWMainMenu retain];
    [DWApp setMainMenu:DWMainMenu];
    DWObj = [[DWObject alloc] init];
    DWDefaultFont = nil;
    DWFontManager = [NSFontManager sharedFontManager];
#ifdef BUILDING_FOR_MOJAVE
    if (@available(macOS 10.14, *))
    {
        if([[NSBundle mainBundle] bundleIdentifier] != nil)
        {
            UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
            if(center)
            {
                [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert|UNAuthorizationOptionSound
                        completionHandler:^(BOOL granted, NSError * _Nullable error) {
                            if (granted)
                            {
                                center.delegate = [[[DWUserNotificationCenterDelegate alloc] init] autorelease];
                            }
                            else
                            {
                                NSLog(@"WARNING: Unable to get notification permission. %@", error.localizedDescription);
                            }
                }];
            }
        }
    }
    _DWDirtyDrawables = [[NSMutableArray alloc] init];
#else
    /* Create mutexes for thread safety */
    DWRunMutex = dw_mutex_new();
    DWThreadMutex = dw_mutex_new();
    DWThreadMutex2 = dw_mutex_new();
#endif
    /* Use NSThread to start a dummy thread to initialize the threading subsystem */
    NSThread *thread = [[NSThread alloc] initWithTarget:DWObj selector:@selector(uselessThread:) object:nil];
    [thread start];
    [thread release];
    [NSTextField setCellClass:[DWTextFieldCell class]];
    if(!_dw_app_id[0])
    {
        /* Generate an Application ID based on the PID if all else fails. */
        snprintf(_dw_app_id, _DW_APP_ID_SIZE, "%s.pid.%d", DW_APP_DOMAIN_DEFAULT, getpid());
    }
    return DW_ERROR_NONE;
}

/*
 * Allocates a shared memory region with a name.
 * Parameters:
 *         handle: A pointer to receive a SHM identifier.
 *         dest: A pointer to a pointer to receive the memory address.
 *         size: Size in bytes of the shared memory region to allocate.
 *         name: A string pointer to a unique memory name.
 */
HSHM dw_named_memory_new(void **dest, int size, const char *name)
{
   char namebuf[1025] = {0};
   struct _dw_unix_shm *handle = malloc(sizeof(struct _dw_unix_shm));

   mkdir("/tmp/.dw", S_IWGRP|S_IWOTH);
   snprintf(namebuf, 1024, "/tmp/.dw/%s", name);

   if((handle->fd = open(namebuf, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0)
   {
      free(handle);
      return NULL;
   }

   ftruncate(handle->fd, size);

   /* attach the shared memory segment to our process's address space. */
   *dest = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0);

   if(*dest == MAP_FAILED)
   {
      close(handle->fd);
      *dest = NULL;
      free(handle);
      return NULL;
   }

   handle->size = size;
   handle->sid = getsid(0);
   handle->path = strdup(namebuf);

   return handle;
}

/*
 * Aquires shared memory region with a name.
 * Parameters:
 *         dest: A pointer to a pointer to receive the memory address.
 *         size: Size in bytes of the shared memory region to requested.
 *         name: A string pointer to a unique memory name.
 */
HSHM dw_named_memory_get(void **dest, int size, const char *name)
{
   char namebuf[1025];
   struct _dw_unix_shm *handle = malloc(sizeof(struct _dw_unix_shm));

   mkdir("/tmp/.dw", S_IWGRP|S_IWOTH);
   snprintf(namebuf, 1024, "/tmp/.dw/%s", name);

   if((handle->fd = open(namebuf, O_RDWR)) < 0)
   {
      free(handle);
      return NULL;
   }

   /* attach the shared memory segment to our process's address space. */
   *dest = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0);

   if(*dest == MAP_FAILED)
   {
      close(handle->fd);
      *dest = NULL;
      free(handle);
      return NULL;
   }

   handle->size = size;
   handle->sid = -1;
   handle->path = NULL;

   return handle;
}

/*
 * Frees a shared memory region previously allocated.
 * Parameters:
 *         handle: Handle obtained from DB_named_memory_allocate.
 *         ptr: The memory address aquired with DB_named_memory_allocate.
 */
int dw_named_memory_free(HSHM handle, void *ptr)
{
   struct _dw_unix_shm *h = handle;
   int rc = munmap(ptr, h->size);

   close(h->fd);
   if(h->path)
   {
      /* Only remove the actual file if we are the
       * creator of the file.
       */
      if(h->sid != -1 && h->sid == getsid(0))
         remove(h->path);
      free(h->path);
   }
   return rc;
}

/*
 * Creates a new thread with a starting point of func.
 * Parameters:
 *       func: Function which will be run in the new thread.
 *       data: Parameter(s) passed to the function.
 *       stack: Stack size of new thread (OS/2 and Windows only).
 */
DWTID dw_thread_new(void *func, void *data, int stack)
{
    DWTID thread;
    void **tmp = malloc(sizeof(void *) * 2);
    int rc;

   tmp[0] = func;
   tmp[1] = data;

   rc = pthread_create(&thread, NULL, (void *)_dwthreadstart, (void *)tmp);
   if(rc == 0)
      return thread;
   return (DWTID)-1;
}

/*
 * Ends execution of current thread immediately.
 */
void dw_thread_end(void)
{
   pthread_exit(NULL);
}

/*
 * Returns the current thread's ID.
 */
DWTID dw_thread_id(void)
{
   return (DWTID)pthread_self();
}

#ifdef BUILDING_FOR_CATALINA
NSURL *_dw_url_from_program(NSString *nsprogram, NSWorkspace *ws)
{
    NSURL *retval = [ws URLForApplicationWithBundleIdentifier:nsprogram];
    
    if(!retval)
    {
        SEL sfpfa = NSSelectorFromString(@"fullPathForApplication:");

        if([ws respondsToSelector:sfpfa])
        {
            DWIMP ifpfa = (DWIMP)[ws methodForSelector:sfpfa];
            NSString *apppath = ifpfa(ws, sfpfa, nsprogram);
            
            if(apppath)
                retval = [NSURL fileURLWithPath:apppath];
        }
        if(!retval)
            retval = [NSURL fileURLWithPath:nsprogram];
    }
    return retval;
}
#endif

/*
 * Execute and external program in a seperate session.
 * Parameters:
 *       program: Program name with optional path.
 *       type: Either DW_EXEC_CON or DW_EXEC_GUI.
 *       params: An array of pointers to string arguements.
 * Returns:
 *       DW_ERROR_UNKNOWN (-1) on error.
 */
int dw_exec(const char *program, int type, char **params)
{
    int ret = DW_ERROR_UNKNOWN;

    if(type == DW_EXEC_GUI)
    {
        NSString *nsprogram = [NSString stringWithUTF8String:program];
        NSWorkspace *ws = [NSWorkspace sharedWorkspace];

        if(params && params[0] && params[1])
        {
#ifdef BUILDING_FOR_CATALINA
            if(@available(macOS 10.15, *))
            {
                NSURL *url = _dw_url_from_program(nsprogram, ws);
                NSMutableArray *array = [[NSMutableArray alloc] init];
                __block DWDialog *dialog = dw_dialog_new(NULL);
                int z = 1;

                while(params[z])
                {
                    NSString *thisfile = [NSString stringWithUTF8String:params[z]];
                    NSURL *nsfile = [NSURL fileURLWithPath:thisfile];

                    [array addObject:nsfile];
                    z++;
                }

                [ws openURLs:array withApplicationAtURL:url
                                          configuration:[NSWorkspaceOpenConfiguration configuration]
                                      completionHandler:^(NSRunningApplication *app, NSError *error) {
                    int pid = DW_ERROR_UNKNOWN;

                    if(error)
                        NSLog(@"openURLs: %@", [error localizedDescription]);
                    else
                        pid = [app processIdentifier];
                    dw_dialog_dismiss(dialog, DW_INT_TO_POINTER(pid));
                }];
                ret = DW_POINTER_TO_INT(dw_dialog_wait(dialog));
            }
            else
#endif
            {
                SEL sofwa = NSSelectorFromString(@"openFile:withApplication:");

                if([ws respondsToSelector:sofwa])
                {
                    int z = 1;

                    while(params[z])
                    {
                        NSString *file = [NSString stringWithUTF8String:params[z]];
                        DWIMP iofwa = (DWIMP)[ws methodForSelector:sofwa];

                        iofwa(ws, sofwa, file, nsprogram);
                        ret = DW_ERROR_NONE;
                        z++;
                    }
                }
            }
        }
        else
        {
#ifdef BUILDING_FOR_CATALINA
            if(@available(macOS 10.15, *))
            {
                NSURL *url = _dw_url_from_program(nsprogram, ws);
                __block DWDialog *dialog = dw_dialog_new(NULL);

                [ws openApplicationAtURL:url
                           configuration:[NSWorkspaceOpenConfiguration configuration]
                       completionHandler:^(NSRunningApplication *app, NSError *error) {
                    int pid = DW_ERROR_UNKNOWN;

                    if(error)
                        NSLog(@"openApplicationAtURL: %@", [error localizedDescription]);
                    else
                        pid = [app processIdentifier];
                    dw_dialog_dismiss(dialog, DW_INT_TO_POINTER(pid));
                }];
                ret = DW_POINTER_TO_INT(dw_dialog_wait(dialog));
            }
            else
#endif
            {
                SEL sla = NSSelectorFromString(@"launchApplication:");

                if([ws respondsToSelector:sla])
                {
                    DWIMP ila = (DWIMP)[ws methodForSelector:sla];
                    ila(ws, sla, nsprogram);
                    ret = DW_ERROR_NONE;
                }
            }
        }
    }
    else
    {
        /* Use AppleScript to Open Terminal.app or contact an existing copy,
         * and execute the command passed in params[].
         */
        char *commandline;
        char *format = "osascript -e \'tell app \"Terminal\" to do script \"";
        int len = (int)strlen(format) + 3;

        /* Generate a command line from the parameters */
        if(params && *params)
        {
            int z = 0;

            while(params[z])
            {
                len+=strlen(params[z]) + 1;
                z++;
            }
            z=1;
            commandline = calloc(1, len);
            strcpy(commandline, format);
            strcat(commandline, params[0]);
            while(params[z])
            {
                strcat(commandline, " ");
                strcat(commandline, params[z]);
                z++;
            }
        }
        else
        {
            len += strlen(program);
            commandline = calloc(1, len);
            strcpy(commandline, format);
            strcat(commandline, program);
        }
        strcat(commandline, "\"\'");

        /* Attempt to execute the commmand, DW_ERROR_NONE on success */
        if(system(commandline) != -1)
            ret = DW_ERROR_NONE;
        free(commandline);
    }
    return ret;
}

/*
 * Loads a web browser pointed at the given URL.
 * Parameters:
 *       url: Uniform resource locator.
 */
int dw_browse(const char *url)
{
    NSURL *myurl = [NSURL URLWithString:[NSString stringWithUTF8String:url]];
    [[NSWorkspace sharedWorkspace] openURL:myurl];
    return DW_ERROR_NONE;
}

typedef struct _dwprint
{
    NSPrintInfo *pi;
    int (*drawfunc)(HPRINT, HPIXMAP, int, void *);
    void *drawdata;
    unsigned long flags;
} DWPrint;

/*
 * Creates a new print object.
 * Parameters:
 *       jobname: Name of the print job to show in the queue.
 *       flags: Flags to initially configure the print object.
 *       pages: Number of pages to print.
 *       drawfunc: The pointer to the function to be used as the callback.
 *       drawdata: User data to be passed to the handler function.
 * Returns:
 *       A handle to the print object or NULL on failure.
 */
HPRINT API dw_print_new(const char *jobname, unsigned long flags, unsigned int pages, void *drawfunc, void *drawdata)
{
    DWPrint *print;
    NSPrintPanel *panel;
    PMPrintSettings settings;
    NSPrintInfo *pi;

    if(!drawfunc || !(print = calloc(1, sizeof(DWPrint))))
    {
        return NULL;
    }

    if(!jobname)
        jobname = "Dynamic Windows Print Job";

    print->drawfunc = drawfunc;
    print->drawdata = drawdata;
    print->flags = flags;

    /* Get the page range */
    pi = [NSPrintInfo sharedPrintInfo];
    [pi setHorizontalPagination:DWPrintingPaginationModeFit];
    [pi setHorizontallyCentered:YES];
    [pi setVerticalPagination:DWPrintingPaginationModeFit];
    [pi setVerticallyCentered:YES];
    [pi setOrientation:DWPaperOrientationPortrait];
    [pi setLeftMargin:0.0];
    [pi setRightMargin:0.0];
    [pi setTopMargin:0.0];
    [pi setBottomMargin:0.0];

    settings = [pi PMPrintSettings];
    PMSetPageRange(settings, 1, pages);
    PMSetFirstPage(settings, 1, true);
    PMSetLastPage(settings, pages, true);
    PMPrintSettingsSetJobName(settings, (CFStringRef)[NSString stringWithUTF8String:jobname]);
    [pi updateFromPMPrintSettings];

    /* Create and show the print panel */
    panel = [NSPrintPanel printPanel];
    if(!panel || [panel runModalWithPrintInfo:pi] == DWModalResponseCancel)
    {
        free(print);
        return NULL;
    }
    /* Put the print info from the panel into the operation */
    print->pi = pi;

    return print;
}

/*
 * Runs the print job, causing the draw page callbacks to fire.
 * Parameters:
 *       print: Handle to the print object returned by dw_print_new().
 *       flags: Flags to run the print job.
 * Returns:
 *       DW_ERROR_UNKNOWN on error or DW_ERROR_NONE on success.
 */
int API dw_print_run(HPRINT print, unsigned long flags)
{
    DWPrint *p = print;
    NSBitmapImageRep *rep, *rep2;
    NSPrintInfo *pi;
    NSPrintOperation *po;
    HPIXMAP pixmap, pixmap2;
    NSImage *image, *flipped;
    NSImageView *iv;
    NSSize size;
    PMPrintSettings settings;
    int x, result = DW_ERROR_UNKNOWN;
    UInt32 start, end;

    if(!p)
        return result;

    DW_LOCAL_POOL_IN;

    /* Figure out the printer/paper size */
    pi = p->pi;
    size = [pi paperSize];

    /* Get the page range */
    settings = [pi PMPrintSettings];
    PMGetFirstPage(settings, &start);
    if(start > 0)
        start--;
    PMGetLastPage(settings, &end);
    PMSetPageRange(settings, 1, 1);
    PMSetFirstPage(settings, 1, true);
    PMSetLastPage(settings, 1, true);
    [pi updateFromPMPrintSettings];

    /* Create an image view to print and a pixmap to draw into */
    iv = [[NSImageView alloc] init];
    pixmap = dw_pixmap_new(iv, (int)size.width, (int)size.height, 8);
    rep = pixmap->image;
    pixmap2 = dw_pixmap_new(iv, (int)size.width, (int)size.height, 8);
    rep2 = pixmap2->image;

    /* Create an image with the data from the pixmap
     * to go into the image view.
     */
    image = [[NSImage alloc] initWithSize:[rep size]];
    flipped = [[NSImage alloc] initWithSize:[rep size]];
    [image addRepresentation:rep];
    [flipped addRepresentation:rep2];
    [iv setImage:flipped];
    [iv setImageScaling:NSImageScaleProportionallyDown];
    [iv setFrameOrigin:NSMakePoint(0,0)];
    [iv setFrameSize:size];

    /* Create the print operation using the image view and
     * print info obtained from the panel in the last call.
     */
    po = [NSPrintOperation printOperationWithView:iv printInfo:pi];
    [po setShowsPrintPanel:NO];

    /* Cycle through each page */
    for(x=start; x<end && p->drawfunc; x++)
    {
        /* Call the application's draw function */
        p->drawfunc(print, pixmap, x, p->drawdata);
        if(p->drawfunc)
        {
           /* Internal representation is flipped... so flip again so we can print */
           _dw_flip_image(image, rep2, size);
   #ifdef DEBUG_PRINT
           /* Save it to file to see what we have */
           NSData *data = [rep2 representationUsingType: NSPNGFileType properties: nil];
           [data writeToFile: @"print.png" atomically: NO];
   #endif
           /* Print the image view */
           [po runOperation];
           /* Fill the pixmap with white in case we are printing more pages */
           dw_color_foreground_set(DW_CLR_WHITE);
           dw_draw_rect(0, pixmap, TRUE, 0, 0, (int)size.width, (int)size.height);
        }
    }
    if(p->drawfunc)
        result = DW_ERROR_NONE;
    /* Free memory */
    [image release];
    [flipped release];
    dw_pixmap_destroy(pixmap);
    dw_pixmap_destroy(pixmap2);
    free(p);
    [iv release];
    DW_LOCAL_POOL_OUT;
    return result;
}

/*
 * Cancels the print job, typically called from a draw page callback.
 * Parameters:
 *       print: Handle to the print object returned by dw_print_new().
 */
void API dw_print_cancel(HPRINT print)
{
    DWPrint *p = print;

    if(p)
        p->drawfunc = NULL;
}

/*
 * Converts a UTF-8 encoded string into a wide string.
 * Parameters:
 *       utf8string: UTF-8 encoded source string.
 * Returns:
 *       Wide string that needs to be freed with dw_free()
 *       or NULL on failure.
 */
wchar_t * API dw_utf8_to_wchar(const char *utf8string)
{
    size_t buflen = strlen(utf8string) + 1;
    wchar_t *temp = malloc(buflen * sizeof(wchar_t));
    if(temp)
        mbstowcs(temp, utf8string, buflen);
    return temp;
}

/*
 * Converts a wide string into a UTF-8 encoded string.
 * Parameters:
 *       wstring: Wide source string.
 * Returns:
 *       UTF-8 encoded string that needs to be freed with dw_free()
 *       or NULL on failure.
 */
char * API dw_wchar_to_utf8(const wchar_t *wstring)
{
    size_t bufflen = 8 * wcslen(wstring) + 1;
    char *temp = malloc(bufflen);
    if(temp)
        wcstombs(temp, wstring, bufflen);
    return temp;
}

/*
 * Gets the state of the requested library feature.
 * Parameters:
 *       feature: The requested feature for example DW_FEATURE_DARK_MODE
 * Returns:
 *       DW_FEATURE_UNSUPPORTED if the library or OS does not support the feature.
 *       DW_FEATURE_DISABLED if the feature is supported but disabled.
 *       DW_FEATURE_ENABLED if the feature is supported and enabled.
 *       Other value greater than 1, same as enabled but with extra info.
 */
int API dw_feature_get(DWFEATURE feature)
{
    switch(feature)
    {
#ifdef BUILDING_FOR_MOUNTAIN_LION
        case DW_FEATURE_NOTIFICATION:
#endif
#ifdef BUILDING_FOR_YOSEMITE
        case DW_FEATURE_TASK_BAR:
#endif
#ifdef BUILDING_FOR_SNOW_LEOPARD
        case DW_FEATURE_MLE_AUTO_COMPLETE:
#endif
        case DW_FEATURE_HTML:
        case DW_FEATURE_HTML_RESULT:
        case DW_FEATURE_CONTAINER_STRIPE:
        case DW_FEATURE_MLE_WORD_WRAP:
        case DW_FEATURE_UTF8_UNICODE:
        case DW_FEATURE_TREE:
        case DW_FEATURE_WINDOW_PLACEMENT:
            return DW_FEATURE_ENABLED;
#ifdef BUILDING_FOR_MOJAVE
        case DW_FEATURE_DARK_MODE:
        {
            if(@available(macOS 10.14, *))
            {
                /* Make sure DWApp is initialized */
                _dw_app_init();
                /* Get the current appearance */
                NSAppearance *appearance = [DWApp appearance];
                NSAppearanceName basicAppearance;

                if(appearance)
                {
                    basicAppearance = [appearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua,
                                                                                      NSAppearanceNameDarkAqua]];

                    if([basicAppearance isEqualToString:NSAppearanceNameDarkAqua])
                        return DW_DARK_MODE_FORCED;
                    if([basicAppearance isEqualToString:NSAppearanceNameAqua])
                        return DW_FEATURE_DISABLED;
                }
#ifdef BUILDING_FOR_BIG_SUR
                /* Configure the notification's payload. */
                if (@available(macOS 11.0, *)) {
                    appearance = [NSAppearance currentDrawingAppearance];
                } else
#endif
                {
                    _DW_ELSE_AVAILABLE
                    appearance = [NSAppearance currentAppearance];
                    _DW_END_AVAILABLE
                }
                basicAppearance = [appearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua,
                                                                                  NSAppearanceNameDarkAqua]];
                if([basicAppearance isEqualToString:NSAppearanceNameDarkAqua])
                    return DW_DARK_MODE_FULL;
                return DW_DARK_MODE_BASIC;
            }
            return DW_FEATURE_UNSUPPORTED;
        }
#endif
        default:
            return DW_FEATURE_UNSUPPORTED;
    }
}

/*
 * Sets the state of the requested library feature.
 * Parameters:
 *       feature: The requested feature for example DW_FEATURE_DARK_MODE
 *       state: DW_FEATURE_DISABLED, DW_FEATURE_ENABLED or any value greater than 1.
 * Returns:
 *       DW_FEATURE_UNSUPPORTED if the library or OS does not support the feature.
 *       DW_ERROR_NONE if the feature is supported and successfully configured.
 *       DW_ERROR_GENERAL if the feature is supported but could not be configured.
 * Remarks: 
 *       These settings are typically used during dw_init() so issue before 
 *       setting up the library with dw_init().
 */
int API dw_feature_set(DWFEATURE feature, int state)
{
    switch(feature)
    {
        /* These features are supported but not configurable */
#ifdef BUILDING_FOR_MOUNTAIN_LION
        case DW_FEATURE_NOTIFICATION:
#endif
#ifdef BUILDING_FOR_YOSEMITE
        case DW_FEATURE_TASK_BAR:
#endif
#ifdef BUILDING_FOR_SNOW_LEOPARD
        case DW_FEATURE_MLE_AUTO_COMPLETE:
#endif
        case DW_FEATURE_HTML:
        case DW_FEATURE_HTML_RESULT:
        case DW_FEATURE_CONTAINER_STRIPE:
        case DW_FEATURE_MLE_WORD_WRAP:
        case DW_FEATURE_UTF8_UNICODE:
        case DW_FEATURE_TREE:
        case DW_FEATURE_WINDOW_PLACEMENT:
            return DW_ERROR_GENERAL;
        /* These features are supported and configurable */
#ifdef BUILDING_FOR_MOJAVE
        case DW_FEATURE_DARK_MODE:
        {
            if(@available(macOS 10.14, *))
            {
                /* Make sure DWApp is initialized */
                _dw_app_init();
                /* Disabled forces the non-dark aqua theme */
                if(state == DW_FEATURE_DISABLED)
                   [DWApp setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]];
                /* Enabled lets the OS decide the mode */
                else if(state == DW_FEATURE_ENABLED || state == DW_DARK_MODE_FULL)
                   [DWApp setAppearance:nil];
                /* 2 forces dark mode aqua appearance */
                else if(state == DW_DARK_MODE_FORCED)
                    [DWApp setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]];
                else
                    return DW_ERROR_GENERAL;
                return DW_ERROR_NONE;
            }
            return DW_FEATURE_UNSUPPORTED;
        }
#endif
        default:
            return DW_FEATURE_UNSUPPORTED;
    }
}