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: {}, sound: {}", t.getTargetName(), 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}