001package jmri.jmrit.vsdecoder;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.HashMap;
006import java.util.Iterator;
007import javax.swing.AbstractButton;
008import javax.swing.JComponent;
009import org.jdom2.Element;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 * Process Sound Events.
015 *
016 * <hr>
017 * This file is part of JMRI.
018 * <p>
019 * JMRI is free software; you can redistribute it and/or modify it under 
020 * the terms of version 2 of the GNU General Public License as published 
021 * by the Free Software Foundation. See the "COPYING" file for a copy
022 * of this license.
023 * <p>
024 * JMRI is distributed in the hope that it will be useful, but WITHOUT 
025 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
026 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
027 * for more details.
028 *
029 * @author Mark Underwood Copyright (C) 2011
030 */
031public class SoundEvent implements PropertyChangeListener {
032
033    public enum ButtonType {
034
035        MOMENTARY, TOGGLE, ENGINE, NONE
036    }
037
038    String name;
039    String button_label;
040    String event_name;
041    ButtonType buttontype;
042
043    AbstractButton button;
044    EnginePane engine_pane;
045
046    Trigger t; // used in setXml as a temporary holder for creating the
047    // event listener class.
048    ButtonTrigger bt; // used in setupButtonAction() as a temporary holder
049    // for creating the button listeners.
050    VSDecoder parent;
051
052    protected HashMap<String, ButtonTrigger> button_trigger_list;
053
054    protected HashMap<String, Trigger> trigger_list;
055    VSDSound my_sound;
056
057    public SoundEvent() {
058        this(null, null);
059    }
060
061    public SoundEvent(String n) {
062        this(n, n);
063    }
064
065    public SoundEvent(String n, String bl) {
066        name = n;
067        button_label = bl;
068        trigger_list = new HashMap<>();
069        button_trigger_list = new HashMap<>();
070        button = null;
071    }
072
073    public void setName(String n) {
074        name = n;
075    }
076
077    public String getName() {
078        return name;
079    }
080
081    public void setEventName(String n) {
082        event_name = n;
083    }
084
085    public String getEventName() {
086        return event_name;
087    }
088
089    public ButtonType getButtonType() {
090        return buttontype;
091    }
092
093    public boolean hasButton() {
094        if ((buttontype == ButtonType.NONE) || (buttontype == ButtonType.ENGINE) || (button == null)) {
095            return false;
096        } else {
097            return true;
098        }
099    }
100
101    public boolean hasEnginePane() {
102        if ((buttontype == ButtonType.ENGINE) && (engine_pane != null)) {
103            return true;
104        } else {
105            return false;
106        }
107    }
108
109    public void setButton(AbstractButton b) {
110        button = b;
111    }
112
113    public JComponent getButton() {
114        if ((buttontype == ButtonType.NONE) || (buttontype == ButtonType.ENGINE)) {
115            return (null);
116        } else {
117            return button;
118        }
119    }
120
121    public EnginePane getEnginePane() {
122        if (buttontype == ButtonType.ENGINE) {
123            return engine_pane;
124        } else {
125            return null;
126        }
127    }
128
129    public void setEnginePane(EnginePane e) {
130        engine_pane = e;
131    }
132
133    public void setButtonLabel(String bl) {
134        button.setText(bl);
135    }
136
137    public String getButtonLabel() {
138        return button.getText();
139    }
140
141    public void addTrigger(String s, Trigger t) {
142        trigger_list.put(s, t);
143    }
144
145    public Trigger getTrigger(String s) {
146        return trigger_list.get(s);
147    }
148
149    public void setSound(VSDSound v) {
150        my_sound = v;
151    }
152
153    public VSDSound getSound() {
154        return my_sound;
155    }
156
157    public void setParent(VSDecoder v) {
158        parent = v;
159    }
160
161    public VSDecoder getParent() {
162        return parent;
163    }
164
165    @Override
166    public void propertyChange(PropertyChangeEvent event) {
167        for (Trigger t : trigger_list.values()) {
168            t.propertyChange(event);
169        }
170    }
171
172    // What's wrong here:
173    // the anonymous MouseListeners are storing a reference to BT, which keeps getting replaced
174    // each time the function is called.
175    // what we need to do is (maybe) make the ButtonTrigger itself a MouseListener (and ActionListener)
176    // 
177    protected ButtonTrigger setupButtonAction(Element te) {
178        /*
179         MouseListener ml;
180         bt = new ButtonTrigger(te.getAttributeValue("name"));
181         button_trigger_list.put(bt.getName(), bt);
182         log.debug("new ButtonTrigger " + bt.getName() + " type " + btype.toString());
183         switch(btype) {
184         case TOGGLE:
185         this.getButton().addActionListener(bt);
186         break;
187         case MOMENTARY:
188         default:
189         this.getButton().addMouseListener(bt);
190         // Just send the trigger a click.
191         }
192         return bt; // cast OK since we just instantiated it up above.
193         */
194        return null; // cast OK since we just instantiated it up above.
195    }
196
197    public Element getXml() {
198        Element me = new Element("SoundEvent");
199        me.setAttribute("name", name);
200        me.setAttribute("label", me.getText());
201        for (Trigger t : trigger_list.values()) {
202            me.addContent(t.getXml());
203        }
204
205        return me;
206    }
207
208    public void setXml(Element el) {
209        this.setXml(el, null);
210    }
211
212    protected void addXmlTrigger(Element te, VSDFile vf) {
213        String tts;
214        Trigger.TriggerType tt;
215        if ((tts = te.getAttributeValue("type")) != null) {
216            tt = Trigger.TriggerType.valueOf(tts.toUpperCase());
217        } else {
218            tt = Trigger.TriggerType.NONE;
219        }
220
221        switch (tt) {
222            case BUTTON:
223                if (this.buttontype != SoundEvent.ButtonType.NONE) {
224                    t = setupButtonAction(te);
225                }
226                break;
227            case BOOLEAN:
228                t = new BoolTrigger(te.getAttributeValue("name"));
229                break;
230            case FLOAT:
231                t = new FloatTrigger(te.getAttributeValue("name"), 0.0f, Trigger.CompareType.EQ);
232                break;
233            case NOTCH:
234                t = new NotchTrigger(te.getAttributeValue("name"));
235                break;
236            case INT:
237                t = new IntTrigger(te.getAttributeValue("name"));
238                break;
239            case STRING:
240                //t = new StringTrigger(el.getAttributeValue("name"));
241                log.warn("Don't have StringTriggers yet...");
242                t = null;
243                return;
244            case THROTTLE:
245                t = new ThrottleTrigger(te.getAttributeValue("name"));
246                break;
247            case NONE:
248            default:
249                break;
250        }
251
252        log.debug("Building trigger {}", t.getName());
253        t.setXml(te);
254        trigger_list.put(te.getAttributeValue("name"), t);
255        //log.debug("target name " + t.getTargetName() + " sound " + parent.getSound(t.getTargetName()));
256        t.setTarget(parent.getSound(t.getTargetName()));
257        //log.debug("target " + t.getTarget());
258
259        if (t.getTarget() == null) {
260            // If the target is missing, set up a do-nothing operation.
261            // Protects against errors in the XML file.
262            // Should probably post a warning, though.
263            t.setTargetAction(Trigger.TargetAction.NOTHING);
264        }
265        switch (t.getTargetAction()) {
266            case PLAY:
267            case FADEIN:
268                //log.debug("PLAY");
269                t.setCallback(new TriggerListener() {
270                    @Override
271                    public void takeAction() {
272                        t.getTarget().play();
273                    }
274
275                    @Override
276                    public void takeAction(int i) {
277                    }
278
279                    @Override
280                    public void takeAction(float f) {
281                    } // do nothing
282                });
283                break;
284            case LOOP:
285                //log.debug("LOOP");
286                t.setCallback(new TriggerListener() {
287                    @Override
288                    public void takeAction() {
289                        t.getTarget().loop();
290                    }
291
292                    @Override
293                    public void takeAction(int i) {
294                    }
295
296                    @Override
297                    public void takeAction(float f) {
298                    } // do nothing
299                });
300                break;
301            case STOP:
302            case FADEOUT:
303                //log.debug("STOP");
304                t.setCallback(new TriggerListener() {
305                    @Override
306                    public void takeAction() {
307                        t.getTarget().stop();
308                    }
309
310                    @Override
311                    public void takeAction(int i) {
312                    }
313
314                    @Override
315                    public void takeAction(float f) {
316                    } // do nothing
317                });
318                break;
319            case NOTCH:
320                //log.debug("NOTCH");
321                log.debug("making callback t {} target {}", t, t.getTarget());
322                t.setCallback(new TriggerListener() {
323                    @Override
324                    public void takeAction(int i) {
325                        //log.debug("Notch Trigger Listener. t = " + t + " Target = " + t.getTarget() + " notch = " + i);
326                        t.getTarget().changeNotch(i);
327                    }
328
329                    @Override
330                    public void takeAction() {
331                    }
332
333                    @Override
334                    public void takeAction(float f) {
335                    } // do nothing
336                });
337                break;
338            case CHANGE:
339                //log.debug("CHANGE");
340                log.debug("making callback t {} target {}", t, t.getTarget());
341                t.setCallback(new TriggerListener() {
342                    @Override
343                    public void takeAction() {
344                    } // do nothing
345
346                    @Override
347                    public void takeAction(int i) {
348                    } // do nothing
349
350                    @Override
351                    public void takeAction(float f) {
352                        //log.debug("Throttle Trigger Listener. t = " + t + " Target = " + t.getTarget() + " value = " + f);
353                        t.getTarget().changeThrottle(f);
354                    }
355                });
356                break;
357            case NOTHING:
358            case STOP_AT_ZERO:
359                // Used for when the target sound is missing.
360                //log.debug("NOTHING");
361                t.setCallback(new TriggerListener() {
362                    @Override
363                    public void takeAction() {
364                    } // do nothing
365
366                    @Override
367                    public void takeAction(int i) {
368                    } // do nothing
369
370                    @Override
371                    public void takeAction(float f) {
372                    } // do nothing
373                });
374                break;
375            default:
376                // do nothing.
377                break;
378        } // end switch
379    } // end function
380
381    public void setXml(Element el, VSDFile vf) {
382        Element te;
383        String btv;
384
385        // Get the SoundEvent's name.
386        name = el.getAttributeValue("name");
387        if ((btv = el.getAttributeValue("buttontype")) != null) {
388            buttontype = SoundEvent.ButtonType.valueOf(btv.toUpperCase());
389        } else {
390            buttontype = SoundEvent.ButtonType.NONE;
391        }
392
393        // Get the SoundEvent's Triggers and set them up.
394        Iterator<Element> itr = (el.getChildren("trigger")).iterator();
395        while (itr.hasNext()) {
396            te = itr.next();
397            this.addXmlTrigger(te, vf);
398        } // end while
399
400    }  // end setXml()
401
402    private static final Logger log = LoggerFactory.getLogger(SoundEvent.class);
403
404}