//
//  GTMCarbonEvent.h
//
//  Copyright 2006-2008 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
// 
//  http://www.apache.org/licenses/LICENSE-2.0
// 
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//

#import <Foundation/Foundation.h>
#import <Carbon/Carbon.h>

#import "GTMDefines.h"

@class GTMCarbonEventHandler;

// Objective C wrapper for a Carbon Event
@interface GTMCarbonEvent : NSObject <NSCopying> {
 @private
  EventRef event_;  //Event we are wrapping. STRONG
}


//  Create an event of class |inClass| and kind |inKind|
//
//  Returns:
//    Autoreleased GTMCarbonEvent
//
+ (id)eventWithClass:(UInt32)inClass kind:(UInt32)kind;

//  Create an event based on |event|. Retains |event|.
//
//  Returns:
//    Autoreleased GTMCarbonEvent
//
+ (id)eventWithEvent:(EventRef)event;

//  Create an event based on the event currently being handled.
//
//  Returns:
//    Autoreleased GTMCarbonEvent
//
+ (id)currentEvent;

//  Create an event of class |inClass| and kind |inKind|
//
//  Returns:
//    GTMCarbonEvent
//
- (id)initWithClass:(UInt32)inClass kind:(UInt32)kind;

//  Create an event based on |event|. Retains |event|.
//
//  Returns:
//    GTMCarbonEvent
//
- (id)initWithEvent:(EventRef)event;

//  Get the event's class.
//
//  Returns:
//    event class
//
- (UInt32)eventClass;

//  Get the event's kind.
//
//  Returns:
//    event kind
//
- (UInt32)eventKind;

//  Set the event's time.
//
//  Arguments:
//    time - the time you want associated with the event
//
- (void)setTime:(EventTime)eventTime;

//  Get the event's time.
//
//  Returns:
//    the time associated with the event
//
- (EventTime)time;

//  Get the event's eventref for passing to other carbon functions.
//
//  Returns:
//    the event ref associated with the event
//
- (EventRef)event;

//  Sets (or adds) a parameter to an event. Try not to use this function
//  directly. Look at the PARAM_TEMPLATE_DECL/DEFN macros below.
//
//  Arguments:
//    name - the parameter name.
//    type - the parameter type.
//    size - the size of the data that |data| points to.
//    data - pointer to the data you want to set the parameter to.
//
- (void)setParameterNamed:(EventParamName)name 
                     type:(EventParamType)type 
                     size:(ByteCount)size 
                     data:(const void *)data;


//  Gets a parameter from an event. Try not to use this function
//  directly. Look at the PARAM_TEMPLATE_DECL/DEFN macros below.
//
//  Arguments:
//    name - the parameter name.
//    type - the parameter type.
//    size - the size of the data that |data| points to.
//    data - pointer to the buffer that you want to fill with your data.
//
//  Returns:
//    YES is parameter is retrieved successfully. NO if parameter doesn't exist.
//
- (BOOL)getParameterNamed:(EventParamName)name 
                     type:(EventParamType)type 
                     size:(ByteCount)size 
                     data:(void *)data;

//  Gets a the size of a parameter from an event. 
//
//  Arguments:
//    name - the parameter name.
//    type - the parameter type.
//
//  Returns:
//    The size of the buffer required to hold the parameter. 0 if parameter
//    doesn't exist.
//
- (ByteCount)sizeOfParameterNamed:(EventParamName)name
                             type:(EventParamType)type;

//  Sends event to an event target with options
//
//  Arguments:
//    target - target to send event to.
//    options - options to send event. See SendEventToEventTargetWithOptions
//              for details.
//
//  Returns:
//    OSStatus value.
//
- (OSStatus)sendToTarget:(GTMCarbonEventHandler *)target 
                 options:(OptionBits)options;

//  Post event to an event queue.
//
//  Arguments:
//    queue - queue to post it to.
//    priority - priority to post it with
//
//  Returns:
//    OSStatus value.
//
- (OSStatus)postToQueue:(EventQueueRef)queue priority:(EventPriority)priority;

//  Post event to current queue with standard priority.
//
- (void)postToCurrentQueue;

//  Post event to main queue with standard priority.
//
- (void)postToMainQueue;

@end

// Macros for defining simple set/get parameter methods for GTMCarbonEvent. See
// the category GTMCarbonEvent (GTMCarbonEventGettersAndSetters) for an example
// of their use. GTM_PARAM_TEMPLATE_DECL2/DEFN2 is for the case where the
// parameter name is different than the parameter type (rare, but it does
// occur...e.g. for a Rect, the name is typeQDRectangle, and the type is Rect,
// so it would be GTM_PARAM_TEMPLATE_DECL2(QDRectangle, Rect) ). In most cases
// you will just use GTM_PARAM_TEMPLATE_DECL/DEFN.
#define GTM_PARAM_TEMPLATE_DECL2(paramName, paramType) \
- (void)set##paramName##ParameterNamed:(EventParamName)name data:(paramType *)data; \
- (BOOL)get##paramName##ParameterNamed:(EventParamName)name data:(paramType *)data;

#define GTM_PARAM_TEMPLATE_DEFN2(paramName, paramType) \
- (void)set##paramName##ParameterNamed:(EventParamName)name data:(paramType *)data { \
[self setParameterNamed:name type:type##paramName size:sizeof(paramType) data:data]; \
} \
- (BOOL)get##paramName##ParameterNamed:(EventParamName)name data:(paramType *)data { \
return [self getParameterNamed:name type:type##paramName size:sizeof(paramType) data:data]; \
}

#define GTM_PARAM_TEMPLATE_DECL(paramType) GTM_PARAM_TEMPLATE_DECL2(paramType, paramType)
#define GTM_PARAM_TEMPLATE_DEFN(paramType) GTM_PARAM_TEMPLATE_DEFN2(paramType, paramType)


// Category defining some basic types that we want to be able to easily set and
// get from GTMCarbonEvents
@interface GTMCarbonEvent (GTMCarbonEventGettersAndSetters)
GTM_PARAM_TEMPLATE_DECL(UInt32)
GTM_PARAM_TEMPLATE_DECL(EventHotKeyID)
@end

//  Utility function for converting between modifier types
//  Arguments:
//    inCocoaModifiers - keyboard modifiers in carbon form 
//                       (NSCommandKeyMask etc)
//  Returns:
//    Carbon modifiers equivalent to |inCocoaModifiers| (cmdKey etc)
GTM_EXTERN UInt32 GTMCocoaToCarbonKeyModifiers(NSUInteger inCocoaModifiers);

// Utility function for converting between modifier types
//  Arguments:
//    inCarbonModifiers - keyboard modifiers in carbon form (cmdKey etc)
//  Returns:
//    cocoa modifiers equivalent to |inCocoaModifiers| (NSCommandKeyMask etc)
GTM_EXTERN NSUInteger GTMCarbonToCocoaKeyModifiers(UInt32 inCarbonModifiers);

// An "abstract" superclass for objects that handle events such as 
// menus, HIObjects, etc. 
//
// Subclasses are expected to override the eventTarget and
// handleEvent:handler: methods to customize them.
@interface GTMCarbonEventHandler : NSObject {
 @private
  // handler we are wrapping  
  // lazily created in the eventHandler method
  EventHandlerRef eventHandler_;  
  __weak id delegate_;  // Our delegate
  // Does our delegate respond to the gtm_eventHandler:receivedEvent:handler:
  // selector? Cached for performance reasons.
  BOOL delegateRespondsToHandleEvent_;  
}

// Registers the event handler to listen for |events|.
//
// Arguments:
//   events - an array of EventTypeSpec. The events to register for.
//   count - the number of EventTypeSpecs in events.
//
- (void)registerForEvents:(const EventTypeSpec *)events count:(size_t)count;

// Causes the event handler to stop listening for |events|.
//
// Arguments:
//   events - an array of EventTypeSpec. The events to register for.
//   count - the number of EventTypeSpecs in events.
//
- (void)unregisterForEvents:(const EventTypeSpec *)events count:(size_t)count;

// To be overridden by subclasses to respond to events. 
//
// All subclasses should call [super handleEvent:handler:] if they
// don't handle the event themselves.
//
// Arguments:
//   event - the event to be handled
//   handler - the call ref in case you want to call CallNextEventHandler
//             in your method
// Returns:
//   OSStatus - usually either noErr or eventNotHandledErr
//
- (OSStatus)handleEvent:(GTMCarbonEvent *)event 
                handler:(EventHandlerCallRef)handler;

// To be overridden by subclasses to return the event target for the class.
// GTMCarbonEventHandler's implementation returns NULL.
//
// Returns:
//   The event target ref.
//
- (EventTargetRef)eventTarget;

// Gets the underlying EventHandlerRef for that this class wraps.
//
// Returns:
//   The EventHandlerRef this class wraps.
//
- (EventHandlerRef)eventHandler;

// Gets the delegate for the handler
//
// Returns:
//   the delegate
- (id)delegate;

// Sets the delegate for the handler
//
// Arguments:
//   delegate - the delegate to set to
- (void)setDelegate:(id)delegate;

@end

// Category for methods that a delegate of GTMCarbonEventHandlerDelegate may 
// want to implement.
@interface NSObject (GTMCarbonEventHandlerDelegate) 

// If a delegate implements this method it gets called before every event
// that the handler gets sent. If it returns anything but eventNotHandledErr, 
// the handlers handlerEvent:handler: method will not be called, and
// the return value returned by the delegate will be returned back to the
// carbon event dispatch system. This allows you to override any method
// that a handler may implement.
//
// Arguments:
//  delegate - the delegate to set to
//
- (OSStatus)gtm_eventHandler:(GTMCarbonEventHandler *)sender 
               receivedEvent:(GTMCarbonEvent *)event 
                     handler:(EventHandlerCallRef)handler;

@end

// A general OSType for use when setting properties on GTMCarbonEvent objects.
// This is the "signature" as part of commandIDs, controlsIDs, and properties.
// 'GooG'
GTM_EXTERN const OSType kGTMCarbonFrameworkSignature;

// An event handler class representing the event monitor event handler
//
// there is only one of these per application. This way you can put
// event handlers directly on the dispatcher if necessary.
@interface GTMCarbonEventMonitorHandler : GTMCarbonEventHandler
// Accessor to get the GTMCarbonEventMonitorHandler singleton.
//
// Returns:
//  pointer to the GTMCarbonEventMonitorHandler singleton.
+ (GTMCarbonEventMonitorHandler *)sharedEventMonitorHandler;
@end

// An event handler class representing the application event handler.
//
// there is only one of these per application. This way you can put
// event handlers directly on the application if necessary.
@interface GTMCarbonEventApplicationEventHandler : GTMCarbonEventHandler
// Accessor to get the GTMCarbonEventApplicationEventHandler singleton.
//
// Returns:
//  pointer to the GTMCarbonEventApplicationEventHandler singleton.
+ (GTMCarbonEventApplicationEventHandler *)sharedApplicationEventHandler;
@end

// An event handler class representing the toolbox dispatcher event handler
//
// there is only one of these per application. This way you can put
// event handlers directly on the dispatcher if necessary.
@interface GTMCarbonEventDispatcherHandler : GTMCarbonEventHandler {
 @private
  NSMutableDictionary *hotkeys_;  // Collection of registered hotkeys 
}

// Accessor to get the GTMCarbonEventDispatcherHandler singleton.
//
// Returns:
//  pointer to the GTMCarbonEventDispatcherHandler singleton.
+ (GTMCarbonEventDispatcherHandler *)sharedEventDispatcherHandler;

// Registers a hotkey. When the hotkey is executed by the user, target will be
// called with selector.
//  Arguments:
//    keyCode - the virtual keycode of the hotkey
//    cocoaModifiers - the modifiers that need to be used with |keyCode|. NB
//                     that these are cocoa modifiers, so NSCommandKeyMask etc.
//    target - instance that will get |action| called when the hotkey fires
//    action - the method to call on |target| when the hotkey fires
//    onPress - is YES, the hotkey fires on the keydown (usual) otherwise
//              it fires on the key up.
//  Returns:
//    a EventHotKeyRef that you can use with other Carbon functions, or for
//    unregistering the hotkey. Note that all hotkeys are unregistered
//    automatically when an app quits. Will be NULL on failure.
- (EventHotKeyRef)registerHotKey:(NSUInteger)keyCode 
                       modifiers:(NSUInteger)cocoaModifiers 
                          target:(id)target 
                          action:(SEL)action 
                     whenPressed:(BOOL)onPress;

// Unregisters a hotkey previously registered with registerHotKey.
//  Arguments:
//    keyRef - the EventHotKeyRef to unregister
- (void)unregisterHotKey:(EventHotKeyRef)keyRef;

@end
