The Be Book The Application Kit The Application Kit Index

BHandler

Derived from: none
Mix-in classes: BArchivable
Declared in:  be/app/Handler.h
Library: libbe.so
Summary:  more...


A BHandler object responds to messages that are handed to it by a BLooper. The BLooper tells the BHandler about a message by invoking the BHandler's MessageReceived() function.

The Handler List

To be eligible to get messages from a BLooper, a BHandler must be in the BLooper's list of eligible handlers (as explained in the BLooper class). The list of eligible handlers is ordered; if the "first" handler doesn't want to respond to a message that it has received, it simply calls the inherited version of MessageReceived() and the message will automatically be handed to the object's "next" handler. (System messages are not handed down the list.) The BLooper that all these BHandlers belong to is always the last the last handler in the list (BLooper inherits from BHandler).

A BHandler's next handler assignment can be changed through SetNextHandler().

Targets

You can designate a target BHandler for most messages. The designation is made when calling BLooper's PostMessage() function or when constructing the BMessenger object that will send the message. Messages that a user drags and drops are targeted to the object (a BView) that controls the part of the window where the message was dropped. The messaging mechanism eventually passes the target BHandler to DispatchMessage(), so that the message can be delivered to its designated destination.

Filtering

Messages can be filtered before they're dispatched—that is, you can define a function that will look at the message before the target BHandler's hook function is called. The filter function is associated with a BMessageFilter object, which records the criteria for calling the function.

Filters that should apply only to messages targeted to a particular BHandler are assigned to the BHandler by SetFilterList() or AddFilter(). Filters that might apply to any message a BLooper dispatches, regardless of its target, are assigned by the parallel BLooper functions, SetCommonFilterList() and AddCommonFilter(). See those functions and the BMessageFilter class for details.

Notifiers and Observers

A BHandler can be a notifier. A notifier is a handler that maintains one or more states and notifies interested parties when those states change. Each state is idenfified by a 32-bit "what" code. Interested parties, called observers, can register to monitor changes in one or more states by calling StartWatching() and specifying the "what" code of the state they want to be notified of changes to.

This notification occurs when the BHandler calls SendNotices(); it's the handler's job to call SendNotices() whenever a state changes, to ensure that observers are kept informed of the changes. The BHandler passes to SendNotices() a message template to be sent to the observers.

When a notification is sent, observers receive a B_OBSERVER_NOTICE_CHANGE message with an int32 field B_OBSERVE_WHICH_CHANGE that contains the "what" code of the state that changed, and a B_OBSERVE_ORIGINAL_WHAT field that contains the "what" value that was in the template BMessage.


Hook Functions

MessageReceived()


Constructor and Destructor


BHandler()

BHandler(const char *name = NULL)
BHandler(BMessage *archive)

Initializes the BHandler by assigning it a name and registering it with the messaging system. BHandlers can also be reconstructed from a BMessage archive.


~BHandler()

virtual ~BHandler()

Deletes any BMessageFilters assigned to the BHandler.


Static Functions


Instantiate()  see BArchivable::Instantiate()


Member Functions


AddFilter()  see SetFilterList()


Archive() , see


FilterList()  see SetFilterList()


GetSupportedSuites()

virtual status_t GetSupportedSuites(BMessage *message)

Implemented by derived classes to report the suites of messages and specifiers they understand. This function is called in response to either a B_GET_PROPERTIES scripting message for the "Suites" property or a B_GET_SUPPORTED_SUITES message.

Each derived class should add the names of the suites it implements to the "suites" array of message. Each item in the array is a MIME string with the "suite" supertype. In addition, the class should add corresponding flattened BPropertyInfo objects in the "messages" array. A typical implementation of GetSupportedSuites() looks like:

status_t MyHandler::GetSupportedSuites(BMessage *message)
{
   message->AddString("suites", "suite/vnd.Me-my_handler"));
   BPropertyInfo prop_info(prop_list);
   message->AddFlat("messages", &prop_info);
   return BHandler::GetSupportedSuites(message);
}

The value returned by GetSupportedSuites() is added to message in the int32 "error" field.

BHandler's version of this function adds the universal suite "suite/vnd.Be-handler" to message then returns B_OK.


LockLooper() , LockLooperWithTimeout() , UnlockLooper()

bool LockLooper(void)
status_t LockLooperWithTimeout(bigtime_t timeout)
void UnlockLooper(void)

These are "smart" versions of BLooper's locking functions (BLooper::Lock() et. al.). The difference between the versions is that these functions retrieve the handler's looper and lock it (or unlock it) in a pseudo-atomic operation, thus avoiding a race condition. Anytime you're tempted to write code such as this:

/* DON'T DO THIS */
if (myHandler->Looper()->Lock()) {
   ...
   myHandler->Looper()->Unlock();
}

Don't do it. Instead, do this:

/* DO THIS INSTEAD */
if (myHandler->LockLooper()) {
   ...
   myHandler->UnlockLooper();
}

Except for an additional return value in LockLooperWithTimeout(), these functions are identical to their BLooper analogues. See to BLooper::Lock() for details.

RETURN CODES

LockLooper() returns true if it was able to lock the looper, or if it's already locked by the calling thread, and false otherwise. If the handler changes loopers during the call, false is returned.

LockLooperWithTimeout() returns:


LockLooperWithTimeout()  see LockLooper()


Looper()

BLooper *Looper(void) const

Returns the BLooper object that the BHandler has been added to. The function returns NULL if the object hasn't been added to a BLooper. A BHandler can be associated with only one BLooper at a time.

Note that a BLooper object automatically adds itself (as a handler) to itself (as a looper), and a BWindow automatically adds its child views. To explicitly add a handler to a looper, you call BLooper::AddHandler().


MessageReceived()

virtual void MessageReceived(BMessage *message)

Implemented by derived classes to respond to messages that are received by the BHandler. The default (BHandler) implementation of this function responds only to scripting requests. It passes all other messages to the next handler by calling that object's version of MessageReceived().

A typical MessageReceived() implementation distinguishes between messages by looking at its command constant (i.e. the what field). For example:

void MyHandler::MessageReceived(BMessage *message)
{
   switch ( message->what ) {
   case COMMAND_ONE:
      HandleCommandOne()
      break;
   case COMMAND_TWO:
      HandleCommandTwo()
      break;
   ...
   default:
      baseClass::MessageReceived(message);
      break;
   ...
   }
}

It's essential that all unhandled messages are passed to the base class implementation of MessageReceived(), as shown here. The handler chain model depends on it.

If the message comes to the end of the line—if it's not recognized and there is no next handler—the BHandler version of this function sends a B_MESSAGE_NOT_UNDERSTOOD reply to notify the message source.

Do not delete the argument message when you're done with. It doesn't belong to you.


Name()  see SetName()
NextHandler()  see SetNextHandler()


ResolveSpecifier()

virtual BHandler *ResolveSpecifier(BMessage *message,
      int32 index,
      BMessage *specifier,
      int32 what,
      const char *property)

Implemented by derived classes to determine the proper handler for a scripting message. The message is targeted to the BHandler, but the specifiers may indicate that it should be assigned to another object. It's the job of ResolveSpecifier() to examine the current specifier (or more, if necessary) and return the object that should either handle the message or look at the next specifier. This function is called before the message is dispatched and before any filtering functions are called.

The first argument, message, points to the scripting message under consideration. The current specifier is passed in specifier; it will be at index index in the specifier array of message. Finally, what contains the what data member of specifier while property contains the name of the targetted property.

ResolveSpecifier() returns a pointer to the next BHandler that should look at the message. To identify the BHandler, it tries these methods, in order:

Method 1: If the specifier identifies a BHandler belonging to another BLooper, it should send the message to the BLooper and return NULL. The message will be handled in the message loop of the other BLooper; it won't be further processed in this one. For example, a BHandler that kept a list of proxies might use code like the following:

if ( (strcmp(property, "Proxy") == 0)
            && (what == B_INDEX_SPECIFIER) ) {
   int32 i;
   if ( specifier->FindInt32("index", &i) == B_OK ) {
      MyProxy *proxy = (MyProxy *)proxyList->ItemAt(i);
      if ( proxy ) {
            message->PopSpecifier();
            if ( proxy->Looper() != Looper() ) {
               proxy->Looper()->PostMessage(message, proxy);
               return NULL;
            }
      }
      . . .
   }
   . . .
}

Since this function resolved the specifier at index, it calls PopSpecifier() to decrement the index before forwarding the message. Otherwise, the next handler would try to resolve the same specifier.

Method 2: If the specifier picks out another BHandler object belonging to the same BLooper, ResolveSpecifier() can return that BHandler. For example:

if ( proxy ) {
   message->PopSpecifier();
   if ( proxy->Looper() != Looper() ) {
      proxy->Looper()->PostMessage(message, proxy);
      return NULL;
   }
   else {
      return proxy;
   }
}

This, in effect, puts the returned object in the BHandler's place as the designated handler for the message. The BLooper will give the returned handler a chance to respond to the message or resolve the next specifier.

Again, PopSpecifier() should be called so that an attempt isn't made to resolve the same specifier twice.

Method 3: If it can resolve all remaining specifiers and recognizes the message as one that the BHandler itself can handle, it should return the BHandler (this). For example:

if ( (strcmp(property, "Value") == 0) && (message->what == B_GET_PROPERTY) )
   return this;

This confirms the BHandler as the message target. ResolveSpecifier() won't be called again, so it's not necessary to call PopSpecifier() before returning.

Method 4: If it doesn't recognize the property or can't resolve the specifier, it should call (and return the value returned by) the inherited version of ResolveSpecifier().

Examples

The BApplication object takes the first path when it resolves a specifier for a "Window" property; it sends the message to the specified BWindow and returns NULL. A BWindow follows the second path when it resolves a specifier for a "View" property; it returns the specified BView. Thus, a message initially targeted to the BApplication object can find its way to a BView.

BHandler's version of ResolveSpecifier() recognizes a B_GET_PROPERTY message with a direct specifier requesting a "Suite" for the supported suites, "Messenger" for the BHandler, or the BHandler's "InternalName" (the same name that its Name() function returns). In all three cases, it assigns the BHandler (this) as the object responsible for the message.

For all other specifiers and messages, it sends a B_MESSAGE_NOT_UNDERSTOOD reply and returns NULL. The reply message has an "error" field with B_SCRIPT_SYNTAX as the error and a "message" field with a longer textual explanation of the error.


SetFilterList() , FilterList() , AddFilter() , RemoveFilter()

virtual void SetFilterList(BList *list)
BList *FilterList(void) const
virtual void AddFilter(BMessageFilter *filter)
virtual bool RemoveFilter(BMessageFilter *filter)

These functions manage a list of BMessageFilter objects associated with the BHandler.

SetFilterList() assigns the BHandler a new list of filters; the list must contain pointers to instances of the BMessageFilter class or to instances of classes that derive from BMessageFilter. The new list replaces any list of filters previously assigned. All objects in the previous list are deleted, as is the BList that contains them. If list is NULL, the current list is removed without a replacement. FilterList() returns the current list of filters.

AddFilter() adds a filter to the end of the BHandler's list of filters. It creates the BList object if it doesn't already exist. By default, BHandlers don't maintain a BList of filters until one is assigned or the first BMessageFilter is added. RemoveFilter() removes a filter from the list without deleting it. It returns true if successful, and false if it can't find the specified filter in the list (or the list doesn't exist). It leaves the BList in place even after removing the last filter.

For SetFilterList(), AddFilter(), and RemoveFilter() to work, the BHandler must be assigned to a BLooper object and the BLooper must be locked.

See also: BLooper::SetCommonFilterList(), BLooper::Lock(), the BMessageFilter class


SetName() , Name()

void SetName(const char *string)
const char *Name(void) const

These functions set and return the name that identifies the BHandler. The name is originally set by the constructor. SetName() assigns the BHandler a new name, and Name() returns the current name. The string returned by Name() belongs to the BHandler object; it shouldn't be altered or freed.

See also: the BHandler constructor, BView::FindView() in the Interface Kit


SetNextHandler() , NextHandler()

void SetNextHandler(BHandler *handler)
BHandler *NextHandler(void) const

SetNextHandler() reorders the objects in the handler chain so that handler follows this BHandler. This BHandler and handler must already be part of the same chain, and the BLooper they belong to must be locked. The order of objects in the handler chain affects the way in-coming messages are handled (as explained in "Inheritance and the Handler Chain". By default handlers are placed in the order that they're added (through BLooper::AddHandler()).

NextHandler() returns this object next handler. If this object is at the end of the chain, it returns NULL.


StartWatching() , StartWatchingAll() , StopWatching() , StopWatchingAll()

status_t StartWatching(BMessenger watcher, uint32 what)
status_t StartWatching(BHandler *watcher, uint32 what)
status_t StartWatchingAll(BMessenger watcher)
status_t StartWatchingAll(BHandller *watcher)
status_t StopWatching(BMessenger watcher, uint32 what)
status_t StopWatching(BHandler *watcher, uint32 what)
status_t StopWatchingAll(BMessenger watcher)
status_t StopWatchingAll(BHandller *watcher)

The BHandler class provides the concept of a notifier. Notifiers maintain one or more states that other entities might want to monitor changes to. These states are identified by a 32-bit what code. Another entity—a BHandler or a BMessenger—can watch for changes notifiers' states. These are called observers.

StartWatching() registers the BMessenger or BHandler specified by watcher to be notified whenever the state specified by what changes. StartWatchingAll() registers the specified BMessenger or BHandler to be notified when any of the notifer's states change.

StartWatching() works by sending a message to the BHandler you want to observe, with a BMessenger back to the observer, so both must be attached to a looper at the time StartWatching() is called.

The forms of StartWatching() and StartWatchingAll() that accept a BHandler can be used to observe a handler that's not yet attached to a looper. However, these only work if the observer and notifier are both in the same looper.

StopWatching() ceases monitoring of the state what. StopWatchingAll(), by some odd coincidence, stops all monitoring by the BHandler or BMessenger specified by watcher.

RETURN CODES


UnlockLooper()  see LockLooper()


Archived Fields

Field Type code Meaning
"_name" B_STRING_TYPE The object's name (see SetName()).

BHandler records its own name.


Scripting Suites and Properties

Suite: suite/vnd.Be-handler

"InternalName"

Message Specifiers Reply Type
B_GET_PROPERTY B_DIRECT_SPECIFIER B_STRING_TYPE

Returns the handler's name.


"Messenger"

Message Specifiers Reply Type
B_GET_PROPERTY B_DIRECT_SPECIFIER B_MESSENGER_TYPE

Returns a BMessenger for the handler.


"Suites"

Message Specifiers Reply Type
B_GET_PROPERTY B_DIRECT_SPECIFIER B_STRING_TYPE array

Returns an array of suites that the target supports, identified by name (e.g. "suite/vnd.Be-handler").


The Be Book The Application Kit The Application Kit Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..