/*

File IKAnnouncer.m

Release 1.2, 7 June 1994
Copyright (C) 1994 by H. Scott Roy

This code is part of IconKit, a general toolbox for drag-and-drop applications.  IconKit is free for noncommercial use, but costs money for a commercial license.  You should have received a copy of the license agreement with this file.  If not, a copy of the license and the complete source of IconKit can be obtained from the author:

		H. Scott Roy
		2573 Stowe Ct.
		Northbrook, IL  60062-8103
		iconkit@cs.stanford.edu

For your editing convenience, this file is best viewed using an editor that automatically wraps long lines, in a fixed point font at 80 columns, with tabs every 4 spaces.

*/


/* ========================================================================== */


/*

The IKAnnouncer class is the basic class that IconKit uses to conform to the IKDependency protocol.  It provides a simple announcement service to notify users and listeners of changes to an object.

There are two ways to incorporate an IKAnnouncer in a new class.  One can either inherit from it directly, or one can keep an IKAnnouncer as an instance variable.  The IconKit classes have adopted this second approach as it seems conceptually clearer.

*/

#import <appkit/appkit.h>
#import "iconkit.h"

@implementation IKAnnouncer


/* ========================================================================== */


/*

Here is the initialization method.  The owner of an IKAnnouncer will be given as the first argument in any announce messages.

*/


- initOwner: theOwner
{
	if ((self = [super  init]) != nil)
	{
		owner = theOwner;
		usersAndListeners = [[List  alloc]  init];
		numUsers = 0;
		sendAnnouncements = YES;
	}
	
	return self;
}


- free
{
	[usersAndListeners  free];
	return self = [super  free];
}


- usersAndListeners
{
	return usersAndListeners;
}


- (int) numUsers
{
	return numUsers;
}


- (BOOL) sendAnnouncements
{
	return sendAnnouncements;
}


- setSendAnnouncements: (BOOL) flag;
{
	sendAnnouncements = flag;
	return self;
}


/* ========================================================================== */


/*

Here are the methods to add and remove users and listeners.  They all return the IKAnnouncer owner.  It's important to permit objects to add themselves multiple times, so that all the add and remove calls correctly balance.

*/


- addUser: who
{
	IKdprintf ("adding user:         %-17s (%p)  for: %s (%p)\n", [who  name],
		who, [owner  name], owner);
	
	[usersAndListeners  addObject: who];
	numUsers++;
	return owner;
}


- addListener: who
{
	IKdprintf ("adding listener:     %-17s (%p)  for: %s (%p)\n", [who  name],
		who, [owner  name], owner);
	
	[usersAndListeners  addObject: who];
	return owner;
}


- removeUser: who
{
	IKdprintf ("removing user:       %-17s (%p)  for: %s (%p)\n", [who  name],
		who, [owner  name], owner);
	
	[usersAndListeners  removeObject: who];
	numUsers--;
	return owner;
}


- removeListener: who
{
	IKdprintf ("removing listener:   %-17s (%p)  for: %s (%p)\n", [who  name],
		who, [owner  name], owner);
	
	[usersAndListeners  removeObject: who];
	return owner;
}


/* ========================================================================== */


/*

Here are the methods to send out notification messages to all users and listeners.  The methods try to be safe in case objects try to add or delete themselves from the user list during an announcement, but keep in mind that it's probably a bad idea to muck too much with an object during an announcement.

There's a fair amount of overhead to these announcement messages.  A more performance minded implementation is certainly possible.

Users and listeners only receive the messages to which they can actually respond.  Notification messages should all take forms like,

		didChange: sender
		didChange: sender  info: arg

*/

- announce: (SEL) message
{
	id
		recipients,
		who;
	int
		i = [usersAndListeners  count];
	
	if (i > 0 && sendAnnouncements)
	{
		recipients = [usersAndListeners  copy];
		while (i--) if ([who = [recipients  objectAt: i]  respondsTo: message])
				[who  perform: message  with: owner];
		
		[recipients  free];
	}
	
	return owner;
}


- announce: (SEL) message  with: arg
{
	id
		recipients,
		who;
	int
		i = [usersAndListeners  count];
	
	if (i > 0 && sendAnnouncements)
	{
		recipients = [usersAndListeners  copy];
		while (i--) if ([who = [recipients  objectAt: i]  respondsTo: message])
				[who  perform: message  with: owner  with: arg];
		
		[recipients  free];
	}
	
	return owner;
}


/* ========================================================================== */


/*

Here are the methods to poll the users of an object for approval before performing an operation.  The result is a logical AND of all the responses.  Both methods return as soon as any user responds NO.  Some users might therefore never see a polling message.

*/

- (BOOL) poll: (SEL) message
{
	id
		recipients,
		who;
	int
		i = [usersAndListeners  count];
	
	if (i > 0)
	{
		recipients = [usersAndListeners  copy];
		while (i--) if (
			[who = [recipients  objectAt: i]  respondsTo: message]	&&
			[who  perform: message  with: owner] == NO)
		
					 		break;
		
		[recipients  free];
		return (i < 0) ? YES : NO;
	}
	
	else return YES;
}


- (BOOL) poll: (SEL) message  with: arg
{
	id
		recipients,
		who;
	int
		i = [usersAndListeners  count];
	
	if (i > 0)
	{
		recipients = [usersAndListeners  copy];
		while (i--) if (
			[who = [recipients  objectAt: i]  respondsTo: message]	&&
			[who  perform: message  with: owner  with: arg] == NO)
		
					 		break;
		
		[recipients  free];
		return (i < 0) ? YES : NO;
	}
	
	else return YES;
}


@end
