]> git.donarmstrong.com Git - lilypond.git/blob - lily/include/listener.hh
Issue 4878: Make type_p_name_ always char pointer
[lilypond.git] / lily / include / listener.hh
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2005 Erik Sandberg <mandolaerik@gmail.com>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef LISTENER_HH
21 #define LISTENER_HH
22
23 /*
24   Listeners
25
26   Listeners are used for stream event dispatching.  The usual way to
27   work with them is to use the GET_LISTENER macro which combines the
28   basic Listener algorithm with a Callback_wrapper structure providing
29   a Scheme handle into a member function.
30
31   To register a member function of Foo as an event handler in a
32   dispatcher, you must:
33
34   - declare the function:
35   class Foo
36   {
37     void method (SCM);
38     ...
39   };
40
41   - implement the method::
42   void Foo::method (SCM e)
43   {
44     write ("Foo hears an event!");
45   }
46
47   - Extract a listener using GET_LISTENER (Foo, method)
48   - Register the method to the dispatcher using Dispatcher::register
49
50   Example:
51
52   Foo *foo = (...);
53   Dispatcher *d = (...);
54   Listener l = foo->GET_LISTENER (Foo, method);
55   d->add_listener (l, ly_symbol2scm ("EventClass"));
56
57   Whenever d hears a stream-event ev of class "EventClass",
58   the implemented procedure is called.
59
60   GET_LISTENER actually makes use of a member function
61   get_listener (SCM) available in every Smob<...>-derived class.
62   get_listener receives a function getting an object instance and an
63   event and will turn it into a Listener that will (after turning into
64   Scheme), behave as a function receiving an event as its sole
65   argument, with the object instance being the object from which
66   get_listener was called as a member.
67
68   So (p->get_listener (f)).smobbed_copy () is roughly equivalent to
69   (lambda (ev) (f p->self_scm() ev))
70
71   Limitations:
72
73   The Callback_wrapper mechanism used in GET_LISTENER works only for
74   classes derived from Smob<...>.
75 */
76
77 #include "callback.hh"
78 #include "smobs.hh"
79
80 // A listener is essentially any procedure accepting a single argument
81 // (namely an event).  The class Listener (or rather a smobbed_copy of
82 // it) behaves like such a procedure and is composed of a generic
83 // callback function accepting two arguments, namely a "target"
84 // (usually an engraver instance) and the event.  Its Scheme
85 // equivalent would be
86 //
87 // (define (make-listener callback target)
88 //   (lambda (event) (callback target event)))
89 //
90 // The class construction is lightweight: as a Simple_smob, this is
91 // only converted into Scheme when a smobbed_copy is created.
92
93 class Listener : public Simple_smob<Listener>
94 {
95 private:
96   SCM callback_;
97   SCM target_;
98 public:
99   static const char * const type_p_name_;
100
101   Listener (SCM callback, SCM target)
102     : callback_ (callback), target_ (target) { }
103
104   LY_DECLARE_SMOB_PROC (&Listener::listen, 1, 0, 0)
105   SCM listen (SCM ev)
106   {
107     scm_call_2 (callback_, target_, ev);
108     return SCM_UNSPECIFIED;
109   }
110
111   SCM mark_smob () const
112   {
113     scm_gc_mark (callback_);
114     return target_;
115   }
116
117   bool operator == (Listener const &other) const
118   {
119     return scm_is_eq (callback_, other.callback_)
120       && scm_is_eq (target_, other.target_);
121   }
122
123   static SCM equal_p (SCM a, SCM b)
124   {
125     return *unchecked_unsmob (a) == *unchecked_unsmob (b)
126       ? SCM_BOOL_T : SCM_BOOL_F;
127   }
128
129   template <class T, void (T::*callback)(SCM)>
130   static SCM trampoline (SCM target, SCM ev)
131   {
132     T *t = unsmob<T> (target);
133     LY_ASSERT_SMOB (T, target, 1);
134
135     (t->*callback) (ev);
136     return SCM_UNDEFINED;
137   }
138 };
139
140 #define GET_LISTENER(cl, proc) get_listener (Callback_wrapper::make_smob<Listener::trampoline<cl, &cl::proc> > ())
141
142 #endif /* LISTENER_HH */