/[svn]/jsampler/trunk/src/org/jsampler/view/fantasia/Channel.java
ViewVC logotype

Contents of /jsampler/trunk/src/org/jsampler/view/fantasia/Channel.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1496 - (show annotations) (download)
Mon Nov 19 22:22:22 2007 UTC (16 years, 5 months ago) by iliev
File size: 47624 byte(s)
* Added option for turning off the custom window decoration
  (choose Edit/Preferences, then click the `View' tab)
* Added new menu item: Help/Online Tutorial
* Fantasia: first step of implementing a theme manager
* Fantasia: fixed the view of the channel's stream/voice count statistic
* some minor GUI fixes and enhancements

1 /*
2 * JSampler - a java front-end for LinuxSampler
3 *
4 * Copyright (C) 2005-2007 Grigor Iliev <grigor@grigoriliev.com>
5 *
6 * This file is part of JSampler.
7 *
8 * JSampler is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * JSampler is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with JSampler; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22
23 package org.jsampler.view.fantasia;
24
25 import java.awt.Container;
26 import java.awt.Cursor;
27 import java.awt.Dimension;
28 import java.awt.Graphics;
29 import java.awt.Graphics2D;
30 import java.awt.Insets;
31 import java.awt.Rectangle;
32
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.HierarchyEvent;
36 import java.awt.event.HierarchyListener;
37 import java.awt.event.MouseAdapter;
38 import java.awt.event.MouseEvent;
39
40 import java.beans.PropertyChangeEvent;
41 import java.beans.PropertyChangeListener;
42
43 import java.util.logging.Level;
44
45 import javax.swing.Action;
46 import javax.swing.BorderFactory;
47 import javax.swing.Box;
48 import javax.swing.BoxLayout;
49 import javax.swing.DefaultListCellRenderer;
50 import javax.swing.JButton;
51 import javax.swing.JComboBox;
52 import javax.swing.JLabel;
53 import javax.swing.JMenuItem;
54 import javax.swing.JPanel;
55 import javax.swing.JPopupMenu;
56 import javax.swing.JScrollPane;
57 import javax.swing.JToggleButton;
58 import javax.swing.JToolBar;
59 import javax.swing.SwingConstants;
60 import javax.swing.Timer;
61
62 import javax.swing.event.ChangeEvent;
63 import javax.swing.event.ChangeListener;
64
65 import net.sf.juife.Dial;
66 import net.sf.juife.InformationDialog;
67 import net.sf.juife.JuifeUtils;
68 import net.sf.juife.TitleBar;
69
70 import org.jdesktop.swingx.JXCollapsiblePane;
71
72 import org.jsampler.AudioDeviceModel;
73 import org.jsampler.CC;
74 import org.jsampler.MidiDeviceModel;
75 import org.jsampler.MidiInstrumentMap;
76 import org.jsampler.SamplerChannelModel;
77 import org.jsampler.SamplerModel;
78
79 import org.jsampler.event.ListEvent;
80 import org.jsampler.event.ListListener;
81 import org.jsampler.event.MidiDeviceEvent;
82 import org.jsampler.event.MidiDeviceListEvent;
83 import org.jsampler.event.MidiDeviceListListener;
84 import org.jsampler.event.MidiDeviceListener;
85 import org.jsampler.event.SamplerAdapter;
86 import org.jsampler.event.SamplerChannelAdapter;
87 import org.jsampler.event.SamplerChannelEvent;
88 import org.jsampler.event.SamplerChannelListEvent;
89 import org.jsampler.event.SamplerChannelListListener;
90 import org.jsampler.event.SamplerChannelListener;
91 import org.jsampler.event.SamplerEvent;
92 import org.jsampler.event.SamplerListener;
93
94 import org.jsampler.view.std.JSChannelOutputRoutingDlg;
95 import org.jsampler.view.std.JSFxSendsPane;
96 import org.jsampler.view.std.JSInstrumentChooser;
97
98 import org.jvnet.lafwidget.animation.FadeConfigurationManager;
99 import org.jvnet.lafwidget.animation.FadeKind;
100
101 import org.jvnet.substance.SubstanceImageCreator;
102 import org.jvnet.substance.SubstanceLookAndFeel;
103
104 import org.linuxsampler.lscp.AudioOutputDevice;
105 import org.linuxsampler.lscp.MidiInputDevice;
106 import org.linuxsampler.lscp.MidiPort;
107 import org.linuxsampler.lscp.SamplerChannel;
108 import org.linuxsampler.lscp.SamplerEngine;
109
110 import static org.jsampler.view.fantasia.FantasiaI18n.i18n;
111 import static org.jsampler.view.fantasia.FantasiaPrefs.*;
112
113
114 /**
115 *
116 * @author Grigor Iliev
117 */
118 public class Channel extends org.jsampler.view.JSChannel {
119 private final JXCollapsiblePane mainPane;
120 private final ChannelScreen screen = new ChannelScreen(this);
121 private final ChannelOptions optionsPane = new ChannelOptions(this);
122
123 private final PowerButton btnPower = new PowerButton();
124 private final MuteButton btnMute = new MuteButton();
125 private final SoloButton btnSolo = new SoloButton();
126 private final OptionsButton btnOptions = new OptionsButton();
127
128 private final EnhancedDial dialVolume = new EnhancedDial();
129
130 private boolean selected = false;
131
132 /**
133 * Creates a new instance of <code>Channel</code> using the specified
134 * non-<code>null</code> channel model.
135 * @param model The model to be used by this channel.
136 * @throws IllegalArgumentException If the model is <code>null</code>.
137 */
138 public
139 Channel(SamplerChannelModel model) {
140 this(model, null);
141 }
142
143 /**
144 * Creates a new instance of <code>Channel</code> using the specified
145 * non-<code>null</code> channel model.
146 * @param model The model to be used by this channel.
147 * @param listener A listener which is notified when the newly created
148 * channel is fully expanded on the screen.
149 * @throws IllegalArgumentException If the model is <code>null</code>.
150 */
151 public
152 Channel(SamplerChannelModel model, final ActionListener listener) {
153 super(model);
154
155 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
156 ChannelPane p = new ChannelPane();
157 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
158
159 //p.add(Box.createRigidArea(new Dimension(3, 0)));
160
161 btnPower.setAlignmentY(JPanel.TOP_ALIGNMENT);
162
163 JPanel tb = new JPanel();
164 tb.setBorder(BorderFactory.createEmptyBorder(3, 3, 0, 4));
165 tb.setLayout(new BoxLayout(tb, BoxLayout.X_AXIS));
166 tb.setOpaque(false);
167 tb.setAlignmentY(JPanel.TOP_ALIGNMENT);
168 tb.add(btnPower);
169 tb.setPreferredSize(new Dimension(tb.getPreferredSize().width, 58));
170 tb.setMinimumSize(tb.getPreferredSize());
171 tb.setMaximumSize(tb.getPreferredSize());
172 p.add(tb);
173
174 //p.add(Box.createRigidArea(new Dimension(4, 0)));
175
176 p.add(createVSeparator());
177
178 //p.add(Box.createRigidArea(new Dimension(3, 0)));
179
180 JPanel p2 = new JPanel();
181 p2.setOpaque(false);
182 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
183 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
184 p2.setBorder(BorderFactory.createEmptyBorder(5, 3, 0, 2));
185 p2.add(screen);
186 p.add(p2);
187
188 p.add(createVSeparator());
189
190 p2 = new JPanel();
191 p2.setOpaque(false);
192 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
193 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
194 p2.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
195 p2.add(new JLabel(Res.gfxMuteTitle));
196 p2.add(btnMute);
197 p2.add(new JLabel(Res.gfxSoloTitle));
198 p2.add(btnSolo);
199
200 p.add(p2);
201
202 p.add(createVSeparator());
203
204 p2 = new JPanel();
205 p2.setOpaque(false);
206 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
207 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
208 p2.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
209 JLabel l = new JLabel(Res.gfxVolumeTitle);
210 l.setAlignmentX(JPanel.CENTER_ALIGNMENT);
211 l.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0));
212 p2.add(l);
213 dialVolume.setDialPixmap(Res.gfxVolumeDial, 30, 330);
214 dialVolume.setAlignmentX(JPanel.CENTER_ALIGNMENT);
215 p2.add(dialVolume);
216 p.add(p2);
217
218 p.add(createVSeparator());
219
220 p2 = new JPanel();
221 p2.setOpaque(false);
222 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
223 p2.setAlignmentY(JPanel.TOP_ALIGNMENT);
224 p2.setBorder(BorderFactory.createEmptyBorder(27, 0, 0, 0));
225 l = new JLabel(Res.gfxOptionsTitle);
226 l.setAlignmentX(JPanel.CENTER_ALIGNMENT);
227 l.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0));
228 p2.add(l);
229
230 p2.add(Box.createRigidArea(new Dimension(0, 3)));
231
232 btnOptions.setAlignmentX(JPanel.CENTER_ALIGNMENT);
233 p2.add(btnOptions);
234 p.add(p2);
235
236
237 p.setPreferredSize(new Dimension(420, 60));
238 p.setMinimumSize(p.getPreferredSize());
239 p.setMaximumSize(p.getPreferredSize());
240 //p.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0));
241
242 p.setAlignmentX(JPanel.CENTER_ALIGNMENT);
243 optionsPane.setAlignmentX(JPanel.CENTER_ALIGNMENT);
244
245 mainPane = new JXCollapsiblePane();
246 mainPane.getContentPane().setLayout (
247 new BoxLayout(mainPane.getContentPane(), BoxLayout.Y_AXIS)
248 );
249
250 mainPane.add(p);
251 mainPane.add(optionsPane);
252
253 setOpaque(false);
254
255 int i = preferences().getIntProperty(MAXIMUM_CHANNEL_VOLUME);
256 dialVolume.setMaximum(i);
257 String mcv = MAXIMUM_CHANNEL_VOLUME;
258 preferences().addPropertyChangeListener(mcv, new PropertyChangeListener() {
259 public void
260 propertyChange(PropertyChangeEvent e) {
261 int j = preferences().getIntProperty(MAXIMUM_CHANNEL_VOLUME);
262 dialVolume.setMaximum(j);
263 }
264 });
265
266 getModel().addSamplerChannelListener(getHandler());
267
268 updateChannelInfo();
269
270 add(mainPane);
271
272 if(listener != null) {
273 final String s = JXCollapsiblePane.ANIMATION_STATE_KEY;
274 mainPane.addPropertyChangeListener(s, new PropertyChangeListener() {
275 public void
276 propertyChange(PropertyChangeEvent e) {
277 if(e.getNewValue() == "expanded") {
278 // TODO: this should be done regardles the listener != null?
279 mainPane.removePropertyChangeListener(s, this);
280 ///////
281 listener.actionPerformed(null);
282 ensureChannelIsVisible();
283 } else if(e.getNewValue() == "expanding/collapsing") {
284 ensureChannelIsVisible();
285 }
286 }
287 });
288 }
289
290 mainPane.setAnimated(false);
291 mainPane.setCollapsed(true);
292 mainPane.setAnimated(preferences().getBoolProperty(ANIMATED));
293 mainPane.setCollapsed(false);
294
295 preferences().addPropertyChangeListener(ANIMATED, new PropertyChangeListener() {
296 public void
297 propertyChange(PropertyChangeEvent e) {
298 mainPane.setAnimated(preferences().getBoolProperty(ANIMATED));
299 }
300 });
301
302 if(listener != null) {
303 javax.swing.SwingUtilities.invokeLater(new Runnable() {
304 public void
305 run() { listener.actionPerformed(null); }
306 });
307 }
308
309 CC.getSamplerModel().addSamplerChannelListListener(getHandler());
310 }
311
312 private void
313 ensureChannelIsVisible() {
314 Container p = getParent();
315 JScrollPane sp = null;
316 while(p != null) {
317 if(p instanceof JScrollPane) {
318 sp = (JScrollPane)p;
319 break;
320 }
321 p = p.getParent();
322 }
323 if(sp == null) return;
324 int h = sp.getViewport().getView().getHeight();
325 sp.getViewport().scrollRectToVisible(new Rectangle(0, h - 2, 1, 1));
326 }
327
328 private JPanel
329 createVSeparator() {
330 PixmapPane p = new PixmapPane(Res.gfxVLine);
331 p.setAlignmentY(JPanel.TOP_ALIGNMENT);
332 p.setPreferredSize(new Dimension(2, 60));
333 p.setMinimumSize(p.getPreferredSize());
334 p.setMaximumSize(p.getPreferredSize());
335 return p;
336 }
337
338 /**
339 * Determines whether the channel is selected.
340 * @return <code>true</code> if the channel is selected, <code>false</code> otherwise.
341 */
342 public boolean isSelected() { return selected; }
343
344 /**
345 * Sets the selection state of this channel.
346 * This method is invoked when the selection state of the channel has changed.
347 * @param select Specifies the new selection state of this channel;
348 * <code>true</code> to select the channel, <code>false</code> otherwise.
349 */
350 public void
351 setSelected(boolean select) {
352
353 selected = select;
354 }
355
356 /** Shows the channel properties. */
357 public void
358 expandChannel() { expandChannel(optionsPane.isAnimated()); }
359
360 /** Shows the channel properties. */
361 public void
362 expandChannel(boolean animated) {
363 if(btnOptions.isSelected()) return;
364
365 boolean b = optionsPane.isAnimated();
366 optionsPane.setAnimated(animated);
367 btnOptions.doClick();
368 optionsPane.setAnimated(b);
369 }
370
371
372 /** Invoked when the user changes the volume */
373 private void
374 setVolume() {
375 screen.updateVolumeInfo(dialVolume.getValue());
376
377 if(dialVolume.getValueIsAdjusting()) return;
378
379 int vol = (int)(getChannelInfo().getVolume() * 100);
380
381 if(vol == dialVolume.getValue()) return;
382
383
384 /*
385 * If the model's volume is not equal to the dial knob
386 * value we assume that the change is due to user input.
387 * So we must update the volume at the backend too.
388 */
389 float volume = dialVolume.getValue();
390 volume /= 100;
391 getModel().setBackendVolume(volume);
392 }
393
394 /**
395 * Updates the channel settings. This method is invoked when changes to the
396 * channel were made.
397 */
398 private void
399 updateChannelInfo() {
400 SamplerChannel sc = getChannelInfo();
401
402 screen.updateScreenInfo(sc);
403 updateMuteIcon(sc);
404
405 if(sc.isSoloChannel()) btnSolo.setIcon(Res.gfxSoloOn);
406 else btnSolo.setIcon(Res.gfxSoloOff);
407
408 dialVolume.setValue((int)(sc.getVolume() * 100));
409
410 boolean b = sc.getEngine() != null;
411 dialVolume.setEnabled(b);
412 btnSolo.setEnabled(b);
413 btnMute.setEnabled(b);
414 }
415
416 /**
417 * Updates the mute button with the proper icon regarding to information obtained
418 * from <code>channel</code>.
419 * @param channel A <code>SamplerChannel</code> instance containing the new settings
420 * for this channel.
421 */
422 private void
423 updateMuteIcon(SamplerChannel channel) {
424 if(channel.isMutedBySolo()) btnMute.setIcon(Res.gfxMutedBySolo);
425 else if(channel.isMuted()) btnMute.setIcon(Res.gfxMuteOn);
426 else btnMute.setIcon(Res.gfxMuteOff);
427 }
428
429 private class EnhancedDial extends Dial {
430 EnhancedDial() {
431 super(0, 100);
432
433 setMouseHandlerMode(MouseHandlerMode.LEFT_TO_RIGHT_AND_DOWN_TO_UP);
434
435 addMouseListener(new MouseAdapter() {
436 public void
437 mouseClicked(MouseEvent e) {
438 if(e.getButton() == e.BUTTON3) {
439 setValue(getMaximum() / 2);
440 return;
441 }
442
443 if(e.getButton() != e.BUTTON1) return;
444
445 if(e.getClickCount() < 2) return;
446 setValue(getValueByPoint(e.getPoint()));
447 }
448 });
449
450 addChangeListener(new ChangeListener() {
451 public void
452 stateChanged(ChangeEvent e) { setVolume(); }
453 });
454 }
455 }
456
457 protected void
458 onDestroy() {
459 CC.getSamplerModel().removeSamplerChannelListListener(getHandler());
460
461 screen.onDestroy();
462 optionsPane.onDestroy();
463 }
464
465 private final EventHandler eventHandler = new EventHandler();
466
467 private EventHandler
468 getHandler() { return eventHandler; }
469
470 private class EventHandler implements SamplerChannelListener, SamplerChannelListListener {
471 /**
472 * Invoked when changes are made to a sampler channel.
473 * @param e A <code>SamplerChannelEvent</code> instance
474 * containing event information.
475 */
476 public void
477 channelChanged(SamplerChannelEvent e) { updateChannelInfo(); }
478
479 /**
480 * Invoked when the number of active disk streams has changed.
481 * @param e A <code>SamplerChannelEvent</code> instance
482 * containing event information.
483 */
484 public void
485 streamCountChanged(SamplerChannelEvent e) {
486 screen.updateStreamCount(getModel().getStreamCount());
487 }
488
489 /**
490 * Invoked when the number of active voices has changed.
491 * @param e A <code>SamplerChannelEvent</code> instance
492 * containing event information.
493 */
494 public void
495 voiceCountChanged(SamplerChannelEvent e) {
496 screen.updateVoiceCount(getModel().getVoiceCount());
497 }
498
499 /**
500 * Invoked when a new sampler channel is created.
501 * @param e A <code>SamplerChannelListEvent</code>
502 * instance providing the event information.
503 */
504 public void
505 channelAdded(SamplerChannelListEvent e) { }
506
507 /**
508 * Invoked when a sampler channel is removed.
509 * @param e A <code>SamplerChannelListEvent</code>
510 * instance providing the event information.
511 */
512 public void
513 channelRemoved(SamplerChannelListEvent e) {
514 // Some cleanup when the channel is removed.
515 if(e.getChannelModel().getChannelId() == getChannelId()) {
516 onDestroy();
517 }
518 }
519 }
520
521
522 private class PowerButton extends PixmapToggleButton
523 implements ActionListener, PropertyChangeListener {
524
525 PowerButton() {
526 super(Res.gfxPowerOff, Res.gfxPowerOn);
527
528 setSelected(true);
529 addActionListener(this);
530 }
531
532 public void
533 actionPerformed(ActionEvent e) {
534 if(!mainPane.isAnimated()) {
535 CC.getSamplerModel().removeBackendChannel(getChannelId());
536 return;
537 }
538
539 String s = JXCollapsiblePane.ANIMATION_STATE_KEY;
540 mainPane.addPropertyChangeListener(s, this);
541 mainPane.setCollapsed(true);
542 }
543
544 public void
545 propertyChange(PropertyChangeEvent e) {
546 if(e.getNewValue() == "collapsed") {
547 CC.getSamplerModel().removeBackendChannel(getChannelId());
548 }
549 }
550
551 public boolean
552 contains(int x, int y) { return (x - 11)*(x - 11) + (y - 11)*(y - 11) < 71; }
553 }
554
555 private class MuteButton extends PixmapButton implements ActionListener {
556 MuteButton() {
557 super(Res.gfxMuteOff);
558 //setDisabledIcon(Res.gfxMuteSoloDisabled);
559 setDisabledIcon (
560 SubstanceImageCreator.makeTransparent(this, Res.gfxMuteOff, 0.4)
561 );
562 addActionListener(this);
563 }
564
565 public void
566 actionPerformed(ActionEvent e) {
567 SamplerChannel sc = getChannelInfo();
568 boolean b = true;
569
570 /*
571 * Changing the mute button icon now instead of
572 * leaving the work to the notification mechanism of the LinuxSampler.
573 */
574 if(sc.isMuted() && !sc.isMutedBySolo()) {
575 b = false;
576 boolean hasSolo = CC.getSamplerModel().hasSoloChannel();
577
578 if(sc.isSoloChannel() || !hasSolo) setIcon(Res.gfxMuteOff);
579 else setIcon(Res.gfxMutedBySolo);
580 } else setIcon(Res.gfxMuteOn);
581
582 Channel.this.getModel().setBackendMute(b);
583 }
584
585 public boolean
586 contains(int x, int y) { return (x > 5 && x < 23) && (y > 5 && y < 16); }
587 }
588
589 private class SoloButton extends PixmapButton implements ActionListener {
590 SoloButton() {
591 super(Res.gfxSoloOff);
592 //setDisabledIcon(Res.gfxMuteSoloDisabled);
593 setDisabledIcon (
594 SubstanceImageCreator.makeTransparent(this, Res.gfxSoloOff, 0.4)
595 );
596 addActionListener(this);
597 }
598
599 public void
600 actionPerformed(ActionEvent e) {
601 SamplerChannel sc = getChannelInfo();
602 boolean b = !sc.isSoloChannel();
603
604 /*
605 * Changing the solo button icon (and related) now instead of
606 * leaving the work to the notification mechanism of the LinuxSampler.
607 */
608 if(b) {
609 setIcon(Res.gfxSoloOn);
610 if(sc.isMutedBySolo()) btnMute.setIcon(Res.gfxMuteOff);
611 } else {
612 setIcon(Res.gfxSoloOff);
613 if(!sc.isMuted() && CC.getSamplerModel().getSoloChannelCount() > 1)
614 btnMute.setIcon(Res.gfxMutedBySolo);
615 }
616
617 Channel.this.getModel().setBackendSolo(b);
618 }
619
620 public boolean
621 contains(int x, int y) { return (x > 5 && x < 23) && (y > 5 && y < 16); }
622 }
623
624 private class OptionsButton extends PixmapToggleButton implements ActionListener {
625 OptionsButton() {
626 super(Res.gfxOptionsOff, Res.gfxOptionsOn);
627 setRolloverIcon(Res.gfxOptionsOffRO);
628 this.setRolloverSelectedIcon(Res.gfxOptionsOnRO);
629 addActionListener(this);
630 }
631
632 public void
633 actionPerformed(ActionEvent e) {
634 showOptionsPane(isSelected());
635
636 String s;
637 if(isSelected()) s = i18n.getButtonLabel("OptionsButton.ttHideOptions");
638 else s = i18n.getButtonLabel("OptionsButton.ttShowOptions");
639
640 setToolTipText(s);
641 }
642
643 private void
644 showOptionsPane(boolean show) {
645 optionsPane.setCollapsed(!show);
646 }
647
648 public boolean
649 contains(int x, int y) { return super.contains(x, y) & y < 13; }
650 }
651 }
652
653 class ChannelPane extends PixmapPane {
654 ChannelPane() {
655 super(Res.gfxChannel);
656 setPixmapInsets(new Insets(3, 3, 3, 3));
657 }
658 }
659
660 class ChannelScreen extends PixmapPane {
661 private final Channel channel;
662
663 private final InstrumentPane instrumentPane;
664 private JButton btnInstr = new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnInstr"));
665
666 private final JButton btnEditInstr =
667 new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnEditInstr"));
668 private final ScreenButtonBg sbbEditInstr = new ScreenButtonBg(btnEditInstr);
669
670 private final JButton btnFxSends =
671 new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnFxSends"));
672
673 private final JButton btnEngine
674 = new ScreenButton(i18n.getButtonLabel("ChannelScreen.btnEngine"));
675
676 private final JPopupMenu menuEngines = new JPopupMenu();
677
678 private final JLabel lVolume = new ScreenLabel("");
679 private final JLabel lStreams = new ScreenLabel(" --");
680 private final JLabel lVoices = new ScreenLabel("-- ");
681
682 private InformationDialog fxSendsDlg = null;
683
684 private Dimension dimVolume;
685
686 private Timer timer;
687
688 ChannelScreen(final Channel channel) {
689 super(Res.gfxChannelScreen);
690 setPixmapInsets(new Insets(6, 6, 6, 6));
691 setBorder(BorderFactory.createEmptyBorder(5, 4, 5, 4));
692
693 this.channel = channel;
694
695 setOpaque(false);
696
697 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
698
699 btnInstr.setAlignmentX(CENTER_ALIGNMENT);
700 btnInstr.setRolloverEnabled(false);
701 btnInstr.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
702
703 instrumentPane = new InstrumentPane();
704 add(instrumentPane);
705
706 JPanel p = new JPanel();
707 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
708 p.setAlignmentX(CENTER_ALIGNMENT);
709 p.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
710
711 btnFxSends.setToolTipText(i18n.getButtonLabel("ChannelScreen.btnFxSends.tt"));
712 btnFxSends.addActionListener(new ActionListener() {
713 public void
714 actionPerformed(ActionEvent e) {
715 if(fxSendsDlg != null && fxSendsDlg.isVisible()) {
716 fxSendsDlg.toFront();
717 return;
718 }
719 FxSendsPane p = new FxSendsPane(channel.getModel());
720 int id = channel.getModel().getChannelId();
721 fxSendsDlg = new InformationDialog(CC.getMainFrame(), p);
722 fxSendsDlg.setTitle(i18n.getLabel("FxSendsDlg.title", id));
723 fxSendsDlg.setModal(false);
724 fxSendsDlg.showCloseButton(false);
725 fxSendsDlg.setVisible(true);
726 }
727 });
728
729 p.add(btnFxSends);
730
731 p.add(Box.createRigidArea(new Dimension(6, 0)));
732
733 btnEngine.setIcon(Res.iconEngine12);
734 p.add(btnEngine);
735 //p.add(new Label("|"));
736
737 //p.add(Box.createRigidArea(new Dimension(6, 0)));
738
739 //p.add(btnReset);
740
741 p.add(Box.createGlue());
742
743 lStreams.setFont(Res.fontScreenMono);
744 lStreams.setHorizontalAlignment(JLabel.RIGHT);
745 p.add(lStreams);
746
747 JLabel l = new ScreenLabel("/");
748 l.setFont(Res.fontScreenMono);
749 p.add(l);
750
751 lVoices.setFont(Res.fontScreenMono);
752 p.add(lVoices);
753
754 lVolume.setIcon(Res.iconVolume14);
755 lVolume.setAlignmentX(RIGHT_ALIGNMENT);
756 updateVolumeInfo(100);
757 dimVolume = lVolume.getPreferredSize();
758 p.add(lVolume);
759 p.setPreferredSize(new Dimension(250, p.getPreferredSize().height));
760 p.setMinimumSize(p.getPreferredSize());
761 p.setMaximumSize(p.getPreferredSize());
762
763 //btnInstr.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
764 p.setOpaque(false);
765 add(p);
766
767
768 setPreferredSize(new Dimension(270, 48));
769 setMinimumSize(getPreferredSize());
770 setMaximumSize(getPreferredSize());
771
772 createEngineMenu();
773 installListeners();
774 }
775
776 protected void
777 onDestroy() { timer.stop(); }
778
779 private void
780 createEngineMenu() {
781 for(final SamplerEngine engine : CC.getSamplerModel().getEngines()) {
782 JMenuItem mi = new JMenuItem(engine.getDescription());
783
784 mi.addActionListener(new ActionListener() {
785 public void
786 actionPerformed(ActionEvent e) {
787 channel.getModel().setBackendEngineType(engine.getName());
788 }
789 });
790
791 menuEngines.add(mi);
792 }
793 }
794
795 private void
796 installListeners() {
797 btnInstr.addActionListener(new ActionListener() {
798 public void
799 actionPerformed(ActionEvent e) { loadInstrument(); }
800 });
801
802 btnEditInstr.addActionListener(new ActionListener() {
803 public void
804 actionPerformed(ActionEvent e) {
805 CC.getSamplerModel().editBackendInstrument(channel.getChannelId());
806 }
807 });
808
809 btnEngine.addActionListener(new ActionListener() {
810 public void
811 actionPerformed(ActionEvent e) {
812 int y = btnEngine.getHeight() + 1;
813 menuEngines.show(btnEngine, 0, y);
814 }
815 });
816
817 addMouseListener(getHandler());
818 addHierarchyListener(getHandler());
819
820 ActionListener l = new ActionListener() {
821 public void
822 actionPerformed(ActionEvent e) {
823 if(getMousePosition(true) != null) {
824 getHandler().mouseEntered(null);
825 } else {
826 getHandler().mouseExited(null);
827 }
828 }
829 };
830 timer = new Timer(1000, l);
831 timer.start();
832 }
833
834 private void
835 loadInstrument() {
836 JSInstrumentChooser dlg = FantasiaUtils.createInstrumentChooser(CC.getMainFrame());
837 dlg.setVisible(true);
838
839 if(!dlg.isCancelled()) {
840 SamplerChannelModel m = channel.getModel();
841 m.loadBackendInstrument(dlg.getInstrumentFile(), dlg.getInstrumentIndex());
842 }
843 }
844
845 protected void
846 updateScreenInfo(SamplerChannel sc) {
847 int status = sc.getInstrumentStatus();
848 if(status >= 0 && status < 100) {
849 btnInstr.setText(i18n.getLabel("ChannelScreen.loadingInstrument", status));
850 } else if(status == -1) {
851 btnInstr.setText(i18n.getButtonLabel("ChannelScreen.btnInstr"));
852 } else if(status < -1) {
853 btnInstr.setText(i18n.getLabel("ChannelScreen.errorLoadingInstrument"));
854 } else {
855 if(sc.getInstrumentName() != null) btnInstr.setText(sc.getInstrumentName());
856 else btnInstr.setText(i18n.getButtonLabel("ChannelScreen.btnInstr"));
857 }
858
859 instrumentPane.update();
860
861 if(sc.getEngine() != null) {
862 String s = sc.getEngine().getDescription();
863 if(!s.equals(btnEngine.getText())) btnEngine.setText(s);
864 }
865
866 }
867
868 protected void
869 updateVolumeInfo(int volume) {
870 lVolume.setText(i18n.getLabel("ChannelScreen.volume", volume));
871 lVolume.setMinimumSize(dimVolume);
872 lVolume.setPreferredSize(dimVolume);
873
874 }
875
876 /**
877 * Updates the number of active disk streams.
878 * @param count The new number of active disk streams.
879 */
880 protected void
881 updateStreamCount(int count) {
882 Dimension d = lStreams.getPreferredSize();
883 lStreams.setText(count == 0 ? "--" : String.valueOf(count));
884 d = JuifeUtils.getUnionSize(d, lStreams.getPreferredSize());
885 lStreams.setMinimumSize(d);
886 lStreams.setPreferredSize(d);
887 lStreams.setMaximumSize(d);
888 }
889
890 /**
891 * Updates the number of active voices.
892 * @param count The new number of active voices.
893 */
894 protected void
895 updateVoiceCount(int count) {
896 Dimension d = lVoices.getPreferredSize();
897 lVoices.setText(count == 0 ? "--" : String.valueOf(count));
898 d = JuifeUtils.getUnionSize(d, lVoices.getPreferredSize());
899 lVoices.setMinimumSize(d);
900 lVoices.setPreferredSize(d);
901 lVoices.setMaximumSize(d);
902 }
903
904 class InstrumentPane extends JPanel {
905 private final JPanel leftPane = new JPanel();
906 private final JPanel rightPane = new JPanel();
907
908 InstrumentPane() {
909 setOpaque(false);
910 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
911 add(leftPane);
912 add(btnInstr);
913 add(rightPane);
914 add(sbbEditInstr);
915 btnEditInstr.setToolTipText(i18n.getLabel("ChannelScreen.btnEditInstr.tt"));
916 sbbEditInstr.setVisible(false);
917 setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 6));
918
919 update();
920 }
921
922 public void
923 update() {
924 int a = btnInstr.getMinimumSize().width;
925 int b = 0;
926 if(sbbEditInstr.isVisible()) b = sbbEditInstr.getPreferredSize().width;
927
928 int max = 254 - b;
929 if(a > max) a = max;
930
931 int h = btnInstr.getPreferredSize().height;
932 btnInstr.setPreferredSize(new Dimension(a, h));
933 h = btnInstr.getMaximumSize().height;
934 btnInstr.setMaximumSize(new Dimension(a, h));
935
936
937 int i = (254 - btnInstr.getPreferredSize().width) / 2;
938
939 int j = i;
940 if(sbbEditInstr.isVisible()) j -= sbbEditInstr.getPreferredSize().width;
941 if(i < 0 || j < 0) i = j = 0;
942
943 Dimension d = new Dimension(i, 1);
944 leftPane.setMinimumSize(d);
945 leftPane.setPreferredSize(d);
946 leftPane.setMaximumSize(d);
947
948 d = new Dimension(j, 1);
949 rightPane.setMinimumSize(d);
950 rightPane.setPreferredSize(d);
951 rightPane.setMaximumSize(d);
952
953 validate();
954 }
955 }
956
957 class FxSendsPane extends JSFxSendsPane {
958 FxSendsPane(SamplerChannelModel model) {
959 super(model);
960
961 actionAddFxSend.putValue(Action.SMALL_ICON, Res.iconNew16);
962 actionRemoveFxSend.putValue(Action.SMALL_ICON, Res.iconDelete16);
963 }
964
965 protected JToolBar
966 createToolBar() {
967 JToolBar tb = new JToolBar();
968 Dimension d = new Dimension(Short.MAX_VALUE, tb.getPreferredSize().height);
969 tb.setMaximumSize(d);
970 tb.setFloatable(false);
971 tb.setAlignmentX(JPanel.RIGHT_ALIGNMENT);
972
973 tb.add(new ToolbarButton(actionAddFxSend));
974 tb.add(new ToolbarButton(actionRemoveFxSend));
975
976 return tb;
977 }
978 }
979
980 static class ScreenButton extends JButton {
981 ScreenButton(String s) {
982 super(s);
983 setContentAreaFilled(false);
984 setFocusPainted(false);
985 setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
986 setMargin(new Insets(0, 0, 0, 0));
987
988 putClientProperty (
989 SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY, Boolean.TRUE
990 );
991
992 putClientProperty (
993 SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY, Boolean.TRUE
994 );
995
996 putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
997
998 FadeConfigurationManager.getInstance().disallowFades(FadeKind.ROLLOVER, this);
999
1000 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
1001 setFont(Res.fontScreen);
1002 setForeground(new java.awt.Color(0xFFA300));
1003 }
1004
1005 protected void
1006 paintComponent(Graphics g) {
1007 Graphics2D g2d = (Graphics2D)g;
1008
1009 g2d.setRenderingHint (
1010 java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
1011 java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
1012 );
1013
1014 super.paintComponent(g2d);
1015 }
1016 }
1017
1018 static class ScreenButtonBg extends PixmapPane {
1019 ScreenButtonBg(JButton btn) {
1020 super(Res.gfxScreenBtnBg);
1021 setPixmapInsets(new Insets(4, 4, 4, 4));
1022 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
1023 setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 7));
1024 add(btn);
1025 setPreferredSize(new Dimension(getPreferredSize().width, 13));
1026 }
1027
1028 public Dimension
1029 getPreferredSize() {
1030 return new Dimension(super.getPreferredSize().width, 13);
1031 }
1032 }
1033
1034
1035
1036 static class ScreenLabel extends JLabel {
1037 ScreenLabel() { this(""); }
1038
1039 ScreenLabel(String s) {
1040 super(s);
1041 setFont(Res.fontScreen);
1042 setForeground(new java.awt.Color(0xFFA300));
1043 }
1044
1045 protected void
1046 paintComponent(Graphics g) {
1047 Graphics2D g2d = (Graphics2D)g;
1048
1049 g2d.setRenderingHint (
1050 java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,
1051 java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON
1052 );
1053
1054 super.paintComponent(g2d);
1055 }
1056 }
1057
1058 private final EventHandler eventHandler = new EventHandler();
1059
1060 private EventHandler
1061 getHandler() { return eventHandler; }
1062
1063 private class EventHandler extends MouseAdapter implements HierarchyListener {
1064 public void
1065 mouseEntered(MouseEvent e) {
1066 if(channel.getChannelInfo().getInstrumentStatus() != 100) return;
1067
1068 if(!sbbEditInstr.isVisible()) {
1069 sbbEditInstr.setVisible(true);
1070 instrumentPane.update();
1071 }
1072 }
1073
1074 public void
1075 mouseExited(MouseEvent e) {
1076 if(getMousePosition(true) != null) return;
1077 if(sbbEditInstr.isVisible()) {
1078 sbbEditInstr.setVisible(false);
1079 instrumentPane.update();
1080 }
1081 }
1082
1083 /** Called when the hierarchy has been changed. */
1084 public void
1085 hierarchyChanged(HierarchyEvent e) {
1086 if((e.getChangeFlags() & e.SHOWING_CHANGED) == e.SHOWING_CHANGED) {
1087 if(getMousePosition() == null) mouseExited(null);
1088 else mouseEntered(null);
1089 }
1090 }
1091 }
1092 }
1093
1094 class ChannelOptions extends JXCollapsiblePane {
1095 private final Channel channel;
1096 private MidiDeviceModel midiDevice = null;
1097
1098 private final JComboBox cbMidiDevice = new FantasiaComboBox();
1099 private final JComboBox cbMidiPort = new FantasiaComboBox();
1100 private final JComboBox cbMidiChannel = new FantasiaComboBox();
1101 private final JComboBox cbInstrumentMap = new FantasiaComboBox();
1102 private final JComboBox cbAudioDevice = new FantasiaComboBox();
1103
1104 private final PixmapButton btnChannelRouting;
1105
1106 private boolean update = false;
1107
1108 private final SamplerListener samplerListener;
1109 private final MapListListener mapListListener = new MapListListener();
1110
1111 private class NoMap {
1112 public String
1113 toString() { return "[None]"; }
1114 }
1115
1116 private NoMap noMap = new NoMap();
1117
1118 private class DefaultMap {
1119 public String
1120 toString() { return "[Default]"; }
1121 }
1122
1123 private DefaultMap defaultMap = new DefaultMap();
1124
1125 ChannelOptions(final Channel channel) {
1126 setAnimated(false);
1127 setCollapsed(true);
1128 setAnimated(preferences().getBoolProperty(ANIMATED));
1129
1130 preferences().addPropertyChangeListener(ANIMATED, new PropertyChangeListener() {
1131 public void
1132 propertyChange(PropertyChangeEvent e) {
1133 setAnimated(preferences().getBoolProperty(ANIMATED));
1134 }
1135 });
1136
1137 PixmapPane bgp = new PixmapPane(Res.gfxChannelOptions);
1138 bgp.setPixmapInsets(new Insets(1, 1, 1, 1));
1139
1140 this.channel = channel;
1141
1142 bgp.setBorder(BorderFactory.createEmptyBorder(5, 4, 5, 4));
1143 bgp.setLayout(new BoxLayout(bgp, BoxLayout.X_AXIS));
1144
1145 bgp.setPreferredSize(new Dimension(420, 44));
1146 bgp.setMinimumSize(getPreferredSize());
1147 bgp.setMaximumSize(getPreferredSize());
1148
1149 JPanel p = new JPanel();
1150 p.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 4));
1151 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
1152 JLabel l = new JLabel(Res.gfxMidiInputTitle);
1153 l.setAlignmentX(LEFT_ALIGNMENT);
1154 p.add(l);
1155
1156 JPanel p2 = new JPanel();
1157 p2.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
1158 p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
1159
1160 Object o = cbMidiDevice.getRenderer();
1161 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1162
1163 cbMidiDevice.setPreferredSize(new Dimension(40, 18));
1164 cbMidiDevice.setMinimumSize(cbMidiDevice.getPreferredSize());
1165 cbMidiDevice.setMaximumSize(cbMidiDevice.getPreferredSize());
1166 p2.add(cbMidiDevice);
1167
1168 p2.add(Box.createRigidArea(new Dimension(3, 0)));
1169
1170 o = cbMidiPort.getRenderer();
1171 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1172
1173 cbMidiPort.setPreferredSize(new Dimension(62, 18));
1174 cbMidiPort.setMinimumSize(cbMidiPort.getPreferredSize());
1175 cbMidiPort.setMaximumSize(cbMidiPort.getPreferredSize());
1176 p2.add(cbMidiPort);
1177
1178 p2.add(Box.createRigidArea(new Dimension(3, 0)));
1179
1180 o = cbMidiChannel.getRenderer();
1181 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1182
1183 cbMidiChannel.addItem("All");
1184 for(int i = 1; i <= 16; i++) cbMidiChannel.addItem("Channel " + String.valueOf(i));
1185 cbMidiChannel.setPreferredSize(new Dimension(80, 18));
1186 cbMidiChannel.setMinimumSize(cbMidiChannel.getPreferredSize());
1187 cbMidiChannel.setMaximumSize(cbMidiChannel.getPreferredSize());
1188
1189 p2.add(cbMidiChannel);
1190 p2.setAlignmentX(LEFT_ALIGNMENT);
1191 p2.setOpaque(false);
1192 p.add(p2);
1193 p.setBackground(new java.awt.Color(0x818181));
1194
1195 bgp.add(p);
1196
1197 bgp.add(Box.createRigidArea(new Dimension(4, 0)));
1198
1199 p = new JPanel();
1200 p.setOpaque(true);
1201 p.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 4));
1202 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
1203 l = new JLabel(Res.gfxInstrumentMapTitle);
1204 l.setAlignmentX(LEFT_ALIGNMENT);
1205 l.setAlignmentX(LEFT_ALIGNMENT);
1206 p.add(l);
1207
1208 p.add(Box.createRigidArea(new Dimension(0, 3)));
1209
1210 //o = cbInstrumentMap.getRenderer();
1211 //if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.CENTER);
1212
1213 cbInstrumentMap.setPreferredSize(new Dimension(126, 18));
1214 cbInstrumentMap.setMinimumSize(cbInstrumentMap.getPreferredSize());
1215 cbInstrumentMap.setMaximumSize(cbInstrumentMap.getPreferredSize());
1216 cbInstrumentMap.setAlignmentX(LEFT_ALIGNMENT);
1217 p.add(cbInstrumentMap);
1218 p.setBackground(new java.awt.Color(0x818181));
1219 bgp.add(p);
1220
1221 bgp.add(Box.createRigidArea(new Dimension(4, 0)));
1222
1223 p = new JPanel();
1224 p.setOpaque(true);
1225 p.setBorder(BorderFactory.createEmptyBorder(3, 4, 3, 4));
1226 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
1227 l = new JLabel(Res.gfxAudioOutputTitle);
1228 l.setAlignmentX(LEFT_ALIGNMENT);
1229 p.add(l);
1230
1231 //p.add(Box.createRigidArea(new Dimension(0, 3)));
1232
1233 p2 = new JPanel();
1234 p2.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
1235 p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
1236 p2.setOpaque(false);
1237 p2.setAlignmentX(LEFT_ALIGNMENT);
1238
1239 o = cbAudioDevice.getRenderer();
1240 if(o instanceof JLabel) ((JLabel )o).setHorizontalAlignment(SwingConstants.RIGHT);
1241
1242 cbAudioDevice.setPreferredSize(new Dimension(40, 18));
1243 cbAudioDevice.setMinimumSize(cbAudioDevice.getPreferredSize());
1244 cbAudioDevice.setMaximumSize(cbAudioDevice.getPreferredSize());
1245
1246 p2.add(cbAudioDevice);
1247 p2.add(Box.createRigidArea(new Dimension(3, 0)));
1248 btnChannelRouting = new PixmapButton(Res.gfxBtnCr, Res.gfxBtnCrRO);
1249 btnChannelRouting.setPressedIcon(Res.gfxBtnCrRO);
1250 btnChannelRouting.setEnabled(false);
1251 btnChannelRouting.setToolTipText(i18n.getLabel("ChannelOptions.routing"));
1252
1253 btnChannelRouting.addActionListener(new ActionListener() {
1254 public void
1255 actionPerformed(ActionEvent e) {
1256 SamplerChannel c = channel.getChannelInfo();
1257 new JSChannelOutputRoutingDlg(CC.getMainFrame(), c).setVisible(true);
1258
1259 }
1260 });
1261
1262 p2.add(btnChannelRouting);
1263
1264 p.add(p2);
1265 p.setBackground(new java.awt.Color(0x818181));
1266 p2 = new JPanel();
1267 p2.setLayout(new java.awt.BorderLayout());
1268 p.add(p2);
1269 bgp.add(p);
1270
1271 setContentPane(bgp);
1272
1273 cbMidiDevice.addActionListener(new ActionListener() {
1274 public void
1275 actionPerformed(ActionEvent e) { setMidiDevice(); }
1276 });
1277
1278 cbMidiPort.addActionListener(new ActionListener() {
1279 public void
1280 actionPerformed(ActionEvent e) { setMidiPort(); }
1281 });
1282
1283 cbMidiChannel.addActionListener(new ActionListener() {
1284 public void
1285 actionPerformed(ActionEvent e) { setMidiChannel(); }
1286 });
1287
1288 samplerListener = new SamplerAdapter() {
1289 /** Invoked when the default MIDI instrument map is changed. */
1290 public void
1291 defaultMapChanged(SamplerEvent e) {
1292 updateCbInstrumentMapToolTipText();
1293
1294 }
1295 };
1296
1297 CC.getSamplerModel().addSamplerListener(samplerListener);
1298
1299 cbInstrumentMap.addItem(noMap);
1300 cbInstrumentMap.addItem(defaultMap);
1301 for(MidiInstrumentMap map : CC.getSamplerModel().getMidiInstrumentMaps()) {
1302 cbInstrumentMap.addItem(map);
1303 }
1304
1305 int map = channel.getModel().getChannelInfo().getMidiInstrumentMapId();
1306 cbInstrumentMap.setSelectedItem(CC.getSamplerModel().getMidiInstrumentMapById(map));
1307 if(cbInstrumentMap.getSelectedItem() == null) {
1308 if(map == -1) cbInstrumentMap.setSelectedItem(noMap);
1309 else if(map == -2) {
1310 cbInstrumentMap.setSelectedItem(defaultMap);
1311 }
1312 }
1313
1314 updateCbInstrumentMapToolTipText();
1315
1316 if(channel.getModel().getChannelInfo().getEngine() == null) {
1317 cbInstrumentMap.setEnabled(false);
1318 }
1319
1320 cbInstrumentMap.addActionListener(new ActionListener() {
1321 public void
1322 actionPerformed(ActionEvent e) { updateInstrumentMap(); }
1323 });
1324
1325 CC.getSamplerModel().addMidiInstrumentMapListListener(mapListListener);
1326
1327 cbAudioDevice.addActionListener(new ActionListener() {
1328 public void
1329 actionPerformed(ActionEvent e) { setBackendAudioDevice(); }
1330 });
1331
1332 channel.getModel().addSamplerChannelListener(new SamplerChannelAdapter() {
1333 public void
1334 channelChanged(SamplerChannelEvent e) { updateChannelProperties(); }
1335 });
1336
1337 CC.getSamplerModel().addMidiDeviceListListener(getHandler());
1338 CC.getSamplerModel().addAudioDeviceListListener(getHandler());
1339
1340 updateMidiDevices();
1341 updateAudioDevices();
1342 updateChannelProperties();
1343 }
1344
1345 /**
1346 * Updates the channel settings. This method is invoked when changes to the
1347 * channel were made.
1348 */
1349 private void
1350 updateChannelProperties() {
1351 SamplerModel sm = CC.getSamplerModel();
1352 SamplerChannel sc = channel.getModel().getChannelInfo();
1353
1354 MidiDeviceModel mm = sm.getMidiDeviceById(sc.getMidiInputDevice());
1355 AudioDeviceModel am = sm.getAudioDeviceById(sc.getAudioOutputDevice());
1356
1357 if(isUpdate()) CC.getLogger().warning("Unexpected update state!");
1358
1359 setUpdate(true);
1360
1361 try {
1362 cbMidiDevice.setSelectedItem(mm == null ? null : mm.getDeviceInfo());
1363
1364 cbAudioDevice.setSelectedItem(am == null ? null : am.getDeviceInfo());
1365 btnChannelRouting.setEnabled(am != null);
1366 } catch(Exception x) {
1367 CC.getLogger().log(Level.WARNING, "Unkown error", x);
1368 }
1369
1370 if(sc.getEngine() != null) {
1371 cbInstrumentMap.setEnabled(true);
1372 int id = sc.getMidiInstrumentMapId();
1373 Object o;
1374 if(id == -2) o = defaultMap;
1375 else if(id == -1) o = noMap;
1376 else o = CC.getSamplerModel().getMidiInstrumentMapById(id);
1377
1378 if(cbInstrumentMap.getSelectedItem() != o) {
1379 cbInstrumentMap.setSelectedItem(o);
1380 }
1381 } else {
1382 cbInstrumentMap.setSelectedItem(noMap);
1383 cbInstrumentMap.setEnabled(false);
1384 }
1385
1386 setUpdate(false);
1387 }
1388
1389 /**
1390 * Updates the MIDI device list.
1391 */
1392 private void
1393 updateMidiDevices() {
1394 SamplerModel sm = CC.getSamplerModel();
1395 SamplerChannel sc = channel.getModel().getChannelInfo();
1396
1397 setUpdate(true);
1398
1399 try {
1400 cbMidiDevice.removeAllItems();
1401
1402 for(MidiDeviceModel m : sm.getMidiDevices())
1403 cbMidiDevice.addItem(m.getDeviceInfo());
1404
1405 MidiDeviceModel mm = sm.getMidiDeviceById(sc.getMidiInputDevice());
1406 cbMidiDevice.setSelectedItem(mm == null ? null : mm.getDeviceInfo());
1407 } catch(Exception x) {
1408 CC.getLogger().log(Level.WARNING, "Unkown error", x);
1409 }
1410
1411 setUpdate(false);
1412 }
1413
1414
1415 private void
1416 updateInstrumentMap() {
1417 updateCbInstrumentMapToolTipText();
1418
1419 int id = channel.getModel().getChannelInfo().getMidiInstrumentMapId();
1420 Object o = cbInstrumentMap.getSelectedItem();
1421 if(o == null && id == -1) return;
1422
1423 int cbId;
1424 if(o == null || o == noMap) cbId = -1;
1425 else if(o == defaultMap) cbId = -2;
1426 else cbId = ((MidiInstrumentMap)o).getMapId();
1427
1428 if(cbId == id) return;
1429
1430 channel.getModel().setBackendMidiInstrumentMap(cbId);
1431 }
1432
1433 private void
1434 updateCbInstrumentMapToolTipText() {
1435 if(cbInstrumentMap.getSelectedItem() != defaultMap) {
1436 cbInstrumentMap.setToolTipText(null);
1437 return;
1438 }
1439
1440 MidiInstrumentMap m = CC.getSamplerModel().getDefaultMidiInstrumentMap();
1441 if(m != null) {
1442 String s = i18n.getLabel("Channel.ttDefault", m.getName());
1443 cbInstrumentMap.setToolTipText(s);
1444 } else {
1445 cbInstrumentMap.setToolTipText(null);
1446 }
1447 }
1448
1449 /**
1450 * Updates the audio device list.
1451 */
1452 private void
1453 updateAudioDevices() {
1454 SamplerModel sm = CC.getSamplerModel();
1455 SamplerChannel sc = channel.getModel().getChannelInfo();
1456
1457 setUpdate(true);
1458
1459 try {
1460 cbAudioDevice.removeAllItems();
1461
1462 for(AudioDeviceModel m : sm.getAudioDevices())
1463 cbAudioDevice.addItem(m.getDeviceInfo());
1464
1465 AudioDeviceModel am = sm.getAudioDeviceById(sc.getAudioOutputDevice());
1466 cbAudioDevice.setSelectedItem(am == null ? null : am.getDeviceInfo());
1467 } catch(Exception x) {
1468 CC.getLogger().log(Level.WARNING, "Unkown error", x);
1469 }
1470
1471 setUpdate(false);
1472 }
1473
1474 private void
1475 setMidiDevice() {
1476 MidiInputDevice mid = (MidiInputDevice)cbMidiDevice.getSelectedItem();
1477
1478 if(!isUpdate()) {
1479 if(mid != null) {
1480 channel.getModel().setBackendMidiInputDevice(mid.getDeviceId());
1481 }
1482
1483 return;
1484 }
1485
1486 if(midiDevice != null) midiDevice.removeMidiDeviceListener(getHandler());
1487
1488 cbMidiPort.removeAllItems();
1489
1490 if(mid == null) {
1491 midiDevice = null;
1492 cbMidiPort.setEnabled(false);
1493
1494 cbMidiChannel.setSelectedItem(null);
1495 cbMidiChannel.setEnabled(false);
1496 } else {
1497 midiDevice = CC.getSamplerModel().getMidiDeviceById(mid.getDeviceId());
1498 if(midiDevice != null) midiDevice.addMidiDeviceListener(getHandler());
1499
1500 cbMidiPort.setEnabled(true);
1501
1502 MidiPort[] ports = mid.getMidiPorts();
1503 for(MidiPort port : ports) cbMidiPort.addItem(port);
1504
1505 int p = channel.getModel().getChannelInfo().getMidiInputPort();
1506 cbMidiPort.setSelectedItem(p >= 0 && p < ports.length ? ports[p] : null);
1507
1508 cbMidiChannel.setEnabled(true);
1509 int c = channel.getModel().getChannelInfo().getMidiInputChannel();
1510 cbMidiChannel.setSelectedItem(c == -1 ? "All" : "Channel " + (c + 1));
1511 }
1512 }
1513
1514 private void
1515 setMidiPort() {
1516 if(isUpdate()) return;
1517
1518 channel.getModel().setBackendMidiInputPort(cbMidiPort.getSelectedIndex());
1519 }
1520
1521 private void
1522 setMidiChannel() {
1523 if(isUpdate()) return;
1524
1525 Object o = cbMidiChannel.getSelectedItem();
1526 if(o == null) return;
1527 String s = o.toString();
1528
1529 int c = s.equals("All") ? -1 : Integer.parseInt(s.substring(8)) - 1;
1530
1531 channel.getModel().setBackendMidiInputChannel(c);
1532 }
1533
1534 private void
1535 setBackendAudioDevice() {
1536 if(isUpdate()) return;
1537 AudioOutputDevice dev = (AudioOutputDevice)cbAudioDevice.getSelectedItem();
1538 if(dev != null) channel.getModel().setBackendAudioOutputDevice(dev.getDeviceId());
1539 }
1540
1541 /**
1542 * Determines whether the currently processed changes are due to update.
1543 * @return <code>true</code> if the currently processed changes are due to update and
1544 * <code>false</code> if the currently processed changes are due to user input.
1545 */
1546 private boolean
1547 isUpdate() { return update; }
1548
1549 /**
1550 * Sets whether the currently processed changes are due to update.
1551 * @param b Specify <code>true</code> to indicate that the currently
1552 * processed changes are due to update; <code>false</code>
1553 * indicates that the currently processed changes are due to user input.
1554 */
1555 private void
1556 setUpdate(boolean b) { update = b; }
1557
1558 protected void
1559 onDestroy() {
1560 SamplerModel sm = CC.getSamplerModel();
1561
1562 sm.removeMidiDeviceListListener(getHandler());
1563 sm.removeAudioDeviceListListener(getHandler());
1564 sm.removeMidiInstrumentMapListListener(mapListListener);
1565 sm.removeSamplerListener(samplerListener);
1566
1567 if(midiDevice != null) {
1568 midiDevice.removeMidiDeviceListener(getHandler());
1569 }
1570 }
1571
1572 private final Handler handler = new Handler();
1573
1574 private Handler
1575 getHandler() { return handler; }
1576
1577 private class Handler implements MidiDeviceListListener, ListListener<AudioDeviceModel>,
1578 MidiDeviceListener {
1579 /**
1580 * Invoked when a new MIDI device is created.
1581 * @param e A <code>MidiDeviceListEvent</code>
1582 * instance providing the event information.
1583 */
1584 public void
1585 deviceAdded(MidiDeviceListEvent e) {
1586 cbMidiDevice.addItem(e.getMidiDeviceModel().getDeviceInfo());
1587 }
1588
1589 /**
1590 * Invoked when a MIDI device is removed.
1591 * @param e A <code>MidiDeviceListEvent</code>
1592 * instance providing the event information.
1593 */
1594 public void
1595 deviceRemoved(MidiDeviceListEvent e) {
1596 cbMidiDevice.removeItem(e.getMidiDeviceModel().getDeviceInfo());
1597 }
1598
1599 /**
1600 * Invoked when a new audio device is created.
1601 * @param e An <code>AudioDeviceListEvent</code>
1602 * instance providing the event information.
1603 */
1604 public void
1605 entryAdded(ListEvent<AudioDeviceModel> e) {
1606 cbAudioDevice.addItem(e.getEntry().getDeviceInfo());
1607 }
1608
1609 /**
1610 * Invoked when an audio device is removed.
1611 * @param e An <code>AudioDeviceListEvent</code>
1612 * instance providing the event information.
1613 */
1614 public void
1615 entryRemoved(ListEvent<AudioDeviceModel> e) {
1616 cbAudioDevice.removeItem(e.getEntry().getDeviceInfo());
1617 }
1618
1619 public void
1620 settingsChanged(MidiDeviceEvent e) {
1621 if(isUpdate()) {
1622 CC.getLogger().warning("Invalid update state");
1623 return;
1624 }
1625
1626 setUpdate(true);
1627 int idx = cbMidiPort.getSelectedIndex();
1628 MidiInputDevice d = e.getMidiDeviceModel().getDeviceInfo();
1629
1630 cbMidiPort.removeAllItems();
1631 for(MidiPort port : d.getMidiPorts()) cbMidiPort.addItem(port);
1632
1633 if(idx >= cbMidiPort.getModel().getSize()) idx = 0;
1634
1635 setUpdate(false);
1636
1637 if(cbMidiPort.getModel().getSize() > 0) cbMidiPort.setSelectedIndex(idx);
1638 }
1639 }
1640
1641 private class MapListListener implements ListListener<MidiInstrumentMap> {
1642 /** Invoked when a new MIDI instrument map is added to a list. */
1643 public void
1644 entryAdded(ListEvent<MidiInstrumentMap> e) {
1645 cbInstrumentMap.insertItemAt(e.getEntry(), cbInstrumentMap.getItemCount());
1646 boolean b = channel.getModel().getChannelInfo().getEngine() != null;
1647 if(b && !cbInstrumentMap.isEnabled()) cbInstrumentMap.setEnabled(true);
1648 }
1649
1650 /** Invoked when a new MIDI instrument map is removed from a list. */
1651 public void
1652 entryRemoved(ListEvent<MidiInstrumentMap> e) {
1653 cbInstrumentMap.removeItem(e.getEntry());
1654 if(cbInstrumentMap.getItemCount() == 0) { // TODO: ?
1655 cbInstrumentMap.setSelectedItem(noMap);
1656 cbInstrumentMap.setEnabled(false);
1657 }
1658 }
1659 }
1660 }

  ViewVC Help
Powered by ViewVC