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