About broadcaster/listener concept


#1

Hi all out there!

I hope it is not a silly question but i thing I don’t get the broad/listen concept.
I have looked into the demo to see how it works but.

I am trying to use the boadcaster/listener methods in the following way…

I have got a main components which holds several different components (comms_panel, info_panel, buttons_panel).
For example if I press i button at buttons_panel, its name appears at info_panel and comms_panel.
I have thought of using the broadcaster/listener classes to keep a component informed about events in other components.

The juce demo use ONE main listener to listen to SEVERAL broadcasters(events from child components -> balls in the demo)
In my case I want SEVERAL listeners to listen to ONE broadcaster…so for example I press a button in a component, its name appear at the listeners. I can do it although I am not really sure if this is the concepts.
also changeListenerCallback(ChangeBroadcaster* source) receives the source, so if broadcasters belong to the listener is easy to write: if(source==child_A) or whatever, but when components are defined at different files and they are added in a main component, i don’t know how to find out who the trigger was.

Any advise?I would like to be sure of how to use it before writing a lot useless code :unamused:

Thanks in advance


#2

also,the listener is who sens a message to the broadcaster, shouldn’t it be in the opposite waY??


#3

Hi @carlosm,

I’m very much still learning, but I’m sure more experienced folk will chime in and correct any mistakes. Here’s what I (think I) know about the listener broadcaster system in JUCE…

JUCE follows the observer pattern (i.e. a broadcaster object maintains a list of listeners and when its state changes, notifies each listener by calling one of the listener’s methods).

A broadcaster will:

  • Have a data member ListenerList<Broadcaster::Listener> listeners (or similar) - thats the list of listeners it maintains as per the observer pattern
  • Contain a Broadcaster::Listener class which has virtual callback methods for each possible change of state (to be inherited from & implemented by each Listener object)
  • Have a void addListener (Broadcaster::Listener* listener) method to add listeners to the ListenerList
  • Have a void removeListener (Broadcaster::Listener* listener) method to remove listeners from the ListenerList
  • Call the appropriate callback method on all the listeners in its ListenerList whenever it undergoes a state change. In its simplest form that will look something like this:
if (notification != dontSendNotification)
{
    Component::BailOutChecker checker (this);
    listeners.callChecked (checker, &Broadcaster::Listener::somethingChanged, this);
}

(Note the this: the broadcaster object sends a pointer to itself to the listener, so that when the callback is received, the listener can check the pointer’s address to determine which broadcaster object the callback has come from)

A Listener:

  • Must inherit from Broadcaster::Listener
  • Must implement all pure virtual callback methods in Broadcaster::Listener
  • May implement any non-pure virtual callback methods in Broadcaster::Listener
  • Must call Broadcaster::addListener (this) in order to register itself with the Broadcaster’s ListenerList before it can receive listener callbacks
  • Will now have its callback methods invoked by the Broadcaster when the Broadcaster’s state changes and can use the Broadcaster* it receives as an argument to determine which broadcaster object the callback has come from
  • Must call Broadcaster::removeListener (this) in its destructor so as to avoid leaving a dangling pointer to itself in the Broadcaster’s ListenerList

#4

In order for your listener object to call Broadcaster.addListener (this) to register itself with the broadcaster, it needs to have a pointer or reference to the broadcaster object; so you just keep this reference/ pointer as a private member of the listener object and later check if the pointer received from the broadcaster when it calls the listener object’s callback methods matches the pointer / reference stored earlier by the listener.


#5

You can do that, but there is also a way without keeping pointers, in case they are changing often:
In the case when the listener is not the owner of a broadcaster/slider/anything, I usually use the componentID, that way no raw pointer is involved.
Sure you can argue that the comparism to the raw pointer is more performant than the string of componentID. But I try to avoid raw pointers, where the lifetime is not well defined. That way I am not tempted to use that raw pointer for other things than compare for a sender (and not getting into hells kitchen for that). So I decide for each situation differently which to use…

like this:

static String paramGain ("GainSlider");

ScopedPointer<Slider> slider = new Slider();
slider->setComponentID (paramGain);
slider->addListener (myListener);

void Listener::sliderValueChanged (Slider* s)
{
    if (s->getComponentID() == paramGain)
    {
        // do something
    }
}

…just as alternative


#6

I think that’s OK for component/events callbacks. But in the case of a listener, it receives the broadcaster object but no the component pointer in itself, so if the listener doesn’t own the broadcaster i can’t know how to figure out who the trigger was.
The example is simple. I have got a main component which contains several panels…so i want to trigger events between panels sharing some info regarding the event. Therefore I didn’t understand the message callback from the listener to the broadcaster, because i find useful to send some info from the broadcaster to inform about the event. And if the listener owns the broadcaster I could easily write any method to send from the listener to the broadcaster.
Or maybe there is a better way of doing it and I am confuse of it.
I guess I can program something with pointers and so, but i thought there would be a juce class as broad/listen to perform something like that.
Regards and thanks for your answers!!


#7

Because the ChangeBroadcaster is an interface, you can use the vtable and dynamic_cast:

if (Component* c = dynamic_cast<Component*> (broadcaster)) {
    if (c->getComponentID() == "gainSlider") {
        // and so on
    }
    else if (c->getComponentID() == "another") {
        // another thing
    }
}
else if (AudioDeviceManager* manager = dynamic_cast<AudioDeviceManager*> (broadcaster)) {
    // change came from device manager
}

an ID for ChangeBroadcaster would be a nice addition, but I think you can live without.


#8

It is a good idea, i missed it is an interface.
However I am trying but I’ve got nothing from the ID. like “”. empty field. Of course I have asigned an ID to the trigger.
Maybe because the broadcaster and listener are different classes/files that belong to a main comonent.
Main.c/.h contains A(located at A.h/A.c file) and B(located at B.h/B.c file): i am performing the event straigh from A to B by no means of main.


#9

Do you inherit from ChangeBroadcaster or do you aggregate? It will only work, if you inherit from ChangeBroadcaster:

class SpecialComponent : public Component, public ChangeBroadcaster
{
    // [...]
    void trigger() {
        sendChangeMessage();
    }
}

#10

I inherit from either ChangeBroadcaster or Listener depending on the side.
if I wouldn’t , could I override the callback?.


#11

That’s true for the Listener, but not for the broadcaster. There you can use aggregation:

class Foo {
    void initialise () {
        broadcaster.addListener (listener);
    }
    void trigger () {
        broadcaster.sendChangeMessage ();
    }
    ChangeBroadcaster broadcaster;
}

In that case the received broadcaster pointer wouldn’t be Foo but the generic ChangeBroadcaster.
Sorry, that was probably obvious to you, I just wanted to point to possible erors…