1 |
/* |
2 |
* JSampler - a java front-end for LinuxSampler |
3 |
* |
4 |
* Copyright (C) 2005-2008 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.Dimension; |
27 |
import java.awt.Rectangle; |
28 |
|
29 |
import java.awt.event.ActionEvent; |
30 |
import java.awt.event.ActionListener; |
31 |
import java.awt.event.MouseAdapter; |
32 |
import java.awt.event.MouseEvent; |
33 |
|
34 |
import java.beans.PropertyChangeEvent; |
35 |
import java.beans.PropertyChangeListener; |
36 |
|
37 |
import java.text.NumberFormat; |
38 |
|
39 |
import javax.swing.AbstractAction; |
40 |
import javax.swing.Action; |
41 |
import javax.swing.BoxLayout; |
42 |
import javax.swing.ImageIcon; |
43 |
import javax.swing.JButton; |
44 |
import javax.swing.JComponent; |
45 |
import javax.swing.JLabel; |
46 |
import javax.swing.JMenuItem; |
47 |
import javax.swing.JPanel; |
48 |
import javax.swing.JPopupMenu; |
49 |
import javax.swing.JScrollPane; |
50 |
import javax.swing.JToolBar; |
51 |
|
52 |
import org.jdesktop.swingx.JXCollapsiblePane; |
53 |
|
54 |
import org.jsampler.CC; |
55 |
import org.jsampler.HF; |
56 |
import org.jsampler.SamplerChannelModel; |
57 |
|
58 |
import org.jsampler.event.SamplerChannelEvent; |
59 |
import org.jsampler.event.SamplerChannelListEvent; |
60 |
import org.jsampler.event.SamplerChannelListListener; |
61 |
import org.jsampler.event.SamplerChannelListener; |
62 |
|
63 |
import org.jsampler.view.JSChannel; |
64 |
import org.jsampler.view.JSChannelsPane; |
65 |
|
66 |
import org.jsampler.view.fantasia.basic.PixmapToggleButton; |
67 |
|
68 |
import org.jsampler.view.std.JSChannelOutputRoutingDlg; |
69 |
import org.jsampler.view.std.JSFxSendsDlg; |
70 |
import org.jsampler.view.std.JSFxSendsPane; |
71 |
import org.jsampler.view.std.JSInstrumentChooser; |
72 |
import org.jsampler.view.std.JSVolumeEditorPopup; |
73 |
|
74 |
import org.linuxsampler.lscp.SamplerChannel; |
75 |
|
76 |
import static org.jsampler.view.fantasia.A4n.a4n; |
77 |
import static org.jsampler.view.fantasia.FantasiaI18n.i18n; |
78 |
import static org.jsampler.view.fantasia.FantasiaPrefs.*; |
79 |
import static org.jsampler.view.fantasia.FantasiaUtils.*; |
80 |
import static org.jsampler.view.std.JSVolumeEditorPopup.VolumeType; |
81 |
|
82 |
|
83 |
/** |
84 |
* |
85 |
* @author Grigor Iliev |
86 |
*/ |
87 |
public class Channel extends JSChannel { |
88 |
private final JXCollapsiblePane mainPane; |
89 |
private final ChannelOptionsPane optionsPane = new ChannelOptionsPane(); |
90 |
|
91 |
private final ViewTracker viewTracker; |
92 |
|
93 |
private JSFxSendsDlg fxSendsDlg = null; |
94 |
|
95 |
private final ContextMenu contextMenu; |
96 |
|
97 |
private boolean selected = false; |
98 |
|
99 |
private AnimatedPorpetyListener animatedPorpetyListener = new AnimatedPorpetyListener(); |
100 |
|
101 |
class AnimatedPorpetyListener implements PropertyChangeListener { |
102 |
public void |
103 |
propertyChange(PropertyChangeEvent e) { |
104 |
mainPane.setAnimated(preferences().getBoolProperty(ANIMATED)); |
105 |
} |
106 |
} |
107 |
|
108 |
//////////////////////////////// |
109 |
// ******* Mouse tracker ******* |
110 |
//////////////////////////////// |
111 |
private static int mouseOverChannelId = -1; |
112 |
private static boolean mousePressed = false; |
113 |
|
114 |
private static ActionListener guiListener = null; |
115 |
|
116 |
private static Channel oldMouseOverChannel = null; |
117 |
private static Channel newMouseOverChannel = null; |
118 |
|
119 |
|
120 |
private static void |
121 |
mouseMoved() { |
122 |
//TODO: do this for all channels panes |
123 |
JSChannelsPane cp = CC.getMainFrame().getChannelsPane(0); |
124 |
for(int i = 0; i < cp.getChannelCount(); i++) { |
125 |
mouseMoved((Channel)cp.getChannel(i)); |
126 |
} |
127 |
|
128 |
if(oldMouseOverChannel == newMouseOverChannel) return; |
129 |
|
130 |
if(oldMouseOverChannel != null) oldMouseOverChannel.mouseExited(); |
131 |
|
132 |
if(newMouseOverChannel != null) { |
133 |
mouseOverChannelId = newMouseOverChannel.getChannelId(); |
134 |
newMouseOverChannel.mouseEntered(); |
135 |
} |
136 |
|
137 |
oldMouseOverChannel = newMouseOverChannel = null; |
138 |
} |
139 |
|
140 |
private static void |
141 |
mouseMoved(Channel c) { |
142 |
int id = c.getChannelId(); |
143 |
if(c.mainPane.getMousePosition(true) != null) { |
144 |
newMouseOverChannel = c; |
145 |
} else if(id == mouseOverChannelId) { |
146 |
oldMouseOverChannel = c; |
147 |
} |
148 |
} |
149 |
|
150 |
//////////////////////////////// |
151 |
|
152 |
|
153 |
/** |
154 |
* Creates a new instance of <code>Channel</code> using the specified |
155 |
* non-<code>null</code> channel model. |
156 |
* @param model The model to be used by this channel. |
157 |
* @throws IllegalArgumentException If the model is <code>null</code>. |
158 |
*/ |
159 |
public |
160 |
Channel(SamplerChannelModel model) { |
161 |
this(model, null); |
162 |
} |
163 |
|
164 |
/** |
165 |
* Creates a new instance of <code>Channel</code> using the specified |
166 |
* non-<code>null</code> channel model. |
167 |
* @param model The model to be used by this channel. |
168 |
* @param listener A listener which is notified when the newly created |
169 |
* channel is fully expanded on the screen. |
170 |
* @throws IllegalArgumentException If the model is <code>null</code>. |
171 |
*/ |
172 |
public |
173 |
Channel(SamplerChannelModel model, final ActionListener listener) { |
174 |
this ( |
175 |
model, listener, |
176 |
preferences().getIntProperty(DEFAULT_CHANNEL_VIEW) == 0 ? |
177 |
ChannelView.Type.SMALL : ChannelView.Type.NORMAL |
178 |
); |
179 |
} |
180 |
|
181 |
/** |
182 |
* Creates a new instance of <code>Channel</code> using the specified |
183 |
* non-<code>null</code> channel model. |
184 |
* @param model The model to be used by this channel. |
185 |
* @param listener A listener which is notified when the newly created |
186 |
* channel is fully expanded on the screen. |
187 |
* @param type Specifies the view type to be used. |
188 |
* @throws IllegalArgumentException If the model is <code>null</code>. |
189 |
*/ |
190 |
public |
191 |
Channel(SamplerChannelModel model, final ActionListener listener, ChannelView.Type type) { |
192 |
super(model); |
193 |
|
194 |
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); |
195 |
optionsPane.setAlignmentX(JPanel.CENTER_ALIGNMENT); |
196 |
|
197 |
mainPane = new JXCollapsiblePane(); |
198 |
viewTracker = new ViewTracker(); |
199 |
contextMenu = new ContextMenu(); |
200 |
|
201 |
mainPane.getContentPane().setLayout ( |
202 |
new BoxLayout(mainPane.getContentPane(), BoxLayout.Y_AXIS) |
203 |
); |
204 |
|
205 |
switch(type) { |
206 |
case SMALL: viewTracker.setView(new SmallChannelView(Channel.this)); break; |
207 |
case NORMAL: viewTracker.setView(new NormalChannelView(Channel.this)); break; |
208 |
default: viewTracker.setView(new NormalChannelView(Channel.this)); |
209 |
} |
210 |
|
211 |
setOpaque(false); |
212 |
|
213 |
getModel().addSamplerChannelListener(getHandler()); |
214 |
|
215 |
updateChannelInfo(); |
216 |
|
217 |
add(mainPane); |
218 |
|
219 |
if(listener != null) { |
220 |
final String s = JXCollapsiblePane.ANIMATION_STATE_KEY; |
221 |
mainPane.addPropertyChangeListener(s, new PropertyChangeListener() { |
222 |
public void |
223 |
propertyChange(PropertyChangeEvent e) { |
224 |
if(e.getNewValue() == "expanded") { |
225 |
// TODO: this should be done regardles the listener != null? |
226 |
mainPane.removePropertyChangeListener(s, this); |
227 |
/////// |
228 |
listener.actionPerformed(null); |
229 |
ensureChannelIsVisible(); |
230 |
} else if(e.getNewValue() == "expanding/collapsing") { |
231 |
ensureChannelIsVisible(); |
232 |
} |
233 |
} |
234 |
}); |
235 |
} |
236 |
|
237 |
mainPane.setAnimated(false); |
238 |
mainPane.setCollapsed(true); |
239 |
mainPane.setAnimated(preferences().getBoolProperty(ANIMATED)); |
240 |
mainPane.setCollapsed(false); |
241 |
|
242 |
preferences().addPropertyChangeListener(ANIMATED, animatedPorpetyListener); |
243 |
|
244 |
if(listener != null) { |
245 |
javax.swing.SwingUtilities.invokeLater(new Runnable() { |
246 |
public void |
247 |
run() { listener.actionPerformed(null); } |
248 |
}); |
249 |
} |
250 |
|
251 |
CC.getSamplerModel().addSamplerChannelListListener(getHandler()); |
252 |
|
253 |
installGuiListener(); |
254 |
} |
255 |
|
256 |
private static void |
257 |
installGuiListener() { |
258 |
if(guiListener != null) return; |
259 |
|
260 |
guiListener = new ActionListener() { |
261 |
public void |
262 |
actionPerformed(ActionEvent e) { |
263 |
mouseMoved(); |
264 |
} |
265 |
}; |
266 |
|
267 |
((MainFrame)CC.getMainFrame()).getGuiTimer().addActionListener(guiListener); |
268 |
} |
269 |
|
270 |
private void |
271 |
mouseEntered() { |
272 |
viewTracker.mouseEntered(); |
273 |
} |
274 |
|
275 |
private void |
276 |
mouseExited() { |
277 |
viewTracker.mouseExited(); |
278 |
} |
279 |
|
280 |
private void |
281 |
ensureChannelIsVisible() { |
282 |
Container p = getParent(); |
283 |
JScrollPane sp = null; |
284 |
while(p != null) { |
285 |
if(p instanceof JScrollPane) { |
286 |
sp = (JScrollPane)p; |
287 |
break; |
288 |
} |
289 |
p = p.getParent(); |
290 |
} |
291 |
if(sp == null) return; |
292 |
int h = sp.getViewport().getView().getHeight(); |
293 |
sp.getViewport().scrollRectToVisible(new Rectangle(0, h - 2, 1, 1)); |
294 |
} |
295 |
|
296 |
/** |
297 |
* Determines whether the channel is selected. |
298 |
* @return <code>true</code> if the channel is selected, <code>false</code> otherwise. |
299 |
*/ |
300 |
@Override |
301 |
public boolean |
302 |
isSelected() { return selected; } |
303 |
|
304 |
/** |
305 |
* Sets the selection state of this channel. |
306 |
* This method is invoked when the selection state of the channel has changed. |
307 |
* @param select Specifies the new selection state of this channel; |
308 |
* <code>true</code> to select the channel, <code>false</code> otherwise. |
309 |
*/ |
310 |
@Override |
311 |
public void |
312 |
setSelected(boolean select) { |
313 |
if(selected == select) return; |
314 |
selected = select; |
315 |
repaint(); |
316 |
} |
317 |
|
318 |
/** Shows the channel properties. */ |
319 |
public void |
320 |
expandChannel() { expandChannel(optionsPane.isAnimated()); } |
321 |
|
322 |
/** Shows the channel properties. */ |
323 |
public void |
324 |
expandChannel(boolean animated) { |
325 |
boolean b = optionsPane.isAnimated(); |
326 |
optionsPane.setAnimated(animated); |
327 |
viewTracker.getCurrentView().expandChannel(); |
328 |
optionsPane.setAnimated(b); |
329 |
} |
330 |
|
331 |
/** |
332 |
* Updates the channel settings. This method is invoked when changes to the |
333 |
* channel were made. |
334 |
*/ |
335 |
private void |
336 |
updateChannelInfo() { |
337 |
viewTracker.getCurrentView().updateChannelInfo(); |
338 |
} |
339 |
|
340 |
public void |
341 |
loadInstrument() { |
342 |
JSInstrumentChooser dlg = FantasiaUtils.createInstrumentChooser(CC.getMainFrame()); |
343 |
dlg.setVisible(true); |
344 |
|
345 |
if(!dlg.isCancelled()) { |
346 |
SamplerChannelModel m = getModel(); |
347 |
m.loadBackendInstrument(dlg.getInstrumentFile(), dlg.getInstrumentIndex()); |
348 |
} |
349 |
} |
350 |
|
351 |
public void |
352 |
fallbackToOriginalView() { |
353 |
viewTracker.fallbackToOriginalView(); |
354 |
} |
355 |
|
356 |
public boolean |
357 |
isUsingOriginalView() { |
358 |
return viewTracker.isUsingOriginalView(); |
359 |
} |
360 |
|
361 |
protected void |
362 |
onDestroy() { |
363 |
CC.getSamplerModel().removeSamplerChannelListListener(getHandler()); |
364 |
preferences().removePropertyChangeListener(ANIMATED, animatedPorpetyListener); |
365 |
if(fxSendsDlg != null) fxSendsDlg.dispose(); |
366 |
viewTracker.onDestroy(); |
367 |
} |
368 |
|
369 |
public void |
370 |
remove() { |
371 |
if(!mainPane.isAnimated()) { |
372 |
CC.getSamplerModel().removeBackendChannel(getChannelId()); |
373 |
return; |
374 |
} |
375 |
|
376 |
String s = JXCollapsiblePane.ANIMATION_STATE_KEY; |
377 |
mainPane.addPropertyChangeListener(s, getHandler()); |
378 |
mainPane.setCollapsed(true); |
379 |
} |
380 |
|
381 |
public void |
382 |
showOptionsPane(boolean show) { optionsPane.showOptionsPane(show); } |
383 |
|
384 |
public void |
385 |
showFxSendsDialog() { |
386 |
if(fxSendsDlg != null && fxSendsDlg.isVisible()) { |
387 |
fxSendsDlg.toFront(); |
388 |
return; |
389 |
} |
390 |
|
391 |
fxSendsDlg = new JSFxSendsDlg(new FxSendsPane(getModel())); |
392 |
fxSendsDlg.setVisible(true); |
393 |
} |
394 |
|
395 |
private final EventHandler eventHandler = new EventHandler(); |
396 |
|
397 |
private EventHandler |
398 |
getHandler() { return eventHandler; } |
399 |
|
400 |
private class EventHandler implements SamplerChannelListener, |
401 |
SamplerChannelListListener, PropertyChangeListener { |
402 |
/** |
403 |
* Invoked when changes are made to a sampler channel. |
404 |
* @param e A <code>SamplerChannelEvent</code> instance |
405 |
* containing event information. |
406 |
*/ |
407 |
@Override |
408 |
public void |
409 |
channelChanged(SamplerChannelEvent e) { updateChannelInfo(); } |
410 |
|
411 |
/** |
412 |
* Invoked when the number of active disk streams has changed. |
413 |
* @param e A <code>SamplerChannelEvent</code> instance |
414 |
* containing event information. |
415 |
*/ |
416 |
@Override |
417 |
public void |
418 |
streamCountChanged(SamplerChannelEvent e) { |
419 |
viewTracker.getCurrentView().updateStreamCount(getModel().getStreamCount()); |
420 |
} |
421 |
|
422 |
/** |
423 |
* Invoked when the number of active voices has changed. |
424 |
* @param e A <code>SamplerChannelEvent</code> instance |
425 |
* containing event information. |
426 |
*/ |
427 |
@Override |
428 |
public void |
429 |
voiceCountChanged(SamplerChannelEvent e) { |
430 |
viewTracker.getCurrentView().updateVoiceCount(getModel().getVoiceCount()); |
431 |
} |
432 |
|
433 |
/** |
434 |
* Invoked when a new sampler channel is created. |
435 |
* @param e A <code>SamplerChannelListEvent</code> |
436 |
* instance providing the event information. |
437 |
*/ |
438 |
@Override |
439 |
public void |
440 |
channelAdded(SamplerChannelListEvent e) { } |
441 |
|
442 |
/** |
443 |
* Invoked when a sampler channel is removed. |
444 |
* @param e A <code>SamplerChannelListEvent</code> |
445 |
* instance providing the event information. |
446 |
*/ |
447 |
@Override |
448 |
public void |
449 |
channelRemoved(SamplerChannelListEvent e) { |
450 |
// Some cleanup when the channel is removed. |
451 |
if(e.getChannelModel().getChannelId() == getChannelId()) { |
452 |
onDestroy(); |
453 |
} |
454 |
} |
455 |
|
456 |
@Override |
457 |
public void |
458 |
propertyChange(PropertyChangeEvent e) { |
459 |
if(e.getNewValue() == "collapsed") { |
460 |
CC.getSamplerModel().removeBackendChannel(getChannelId()); |
461 |
} |
462 |
} |
463 |
} |
464 |
|
465 |
public ViewTracker |
466 |
getViewTracker() { return viewTracker; } |
467 |
|
468 |
class ViewTracker extends MouseAdapter implements PropertyChangeListener { |
469 |
private ChannelView originalView; |
470 |
private ChannelView mouseOverView; |
471 |
private ChannelView currentView; |
472 |
|
473 |
private ChannelView.Type mouseOverViewType = null; |
474 |
|
475 |
ViewTracker() { |
476 |
|
477 |
|
478 |
updateMouseOverViewType(); |
479 |
|
480 |
String s = DIFFERENT_CHANNEL_VIEW_ON_MOUSE_OVER; |
481 |
preferences().addPropertyChangeListener(s, this); |
482 |
|
483 |
s = CHANNEL_VIEW_ON_MOUSE_OVER; |
484 |
preferences().addPropertyChangeListener(s, this); |
485 |
} |
486 |
|
487 |
public boolean |
488 |
isUsingOriginalView() { |
489 |
return currentView == originalView; |
490 |
} |
491 |
|
492 |
private void |
493 |
installListeners() { |
494 |
|
495 |
} |
496 |
|
497 |
private void |
498 |
uninstallListeners() { |
499 |
|
500 |
} |
501 |
|
502 |
private void |
503 |
updateMouseOverViewType() { |
504 |
if(mouseOverView != null) { |
505 |
mouseOverView.removeEnhancedMouseListener(this); |
506 |
} |
507 |
|
508 |
mouseOverView = null; |
509 |
|
510 |
boolean b; |
511 |
b = preferences().getBoolProperty(DIFFERENT_CHANNEL_VIEW_ON_MOUSE_OVER); |
512 |
if(!b) { |
513 |
mouseOverViewType = null; |
514 |
uninstallListeners(); |
515 |
return; |
516 |
} |
517 |
|
518 |
int i = preferences().getIntProperty(CHANNEL_VIEW_ON_MOUSE_OVER); |
519 |
|
520 |
switch(i) { |
521 |
case 0: mouseOverViewType = ChannelView.Type.SMALL; break; |
522 |
case 1: mouseOverViewType = ChannelView.Type.NORMAL; break; |
523 |
default:mouseOverViewType = null; |
524 |
} |
525 |
|
526 |
if(mouseOverViewType != null) { |
527 |
installListeners(); |
528 |
} |
529 |
} |
530 |
|
531 |
public ChannelView |
532 |
getMouseOverView() { |
533 |
if(mouseOverViewType == null) return null; |
534 |
|
535 |
if(mouseOverView == null) { |
536 |
Channel channel = Channel.this; |
537 |
|
538 |
switch(mouseOverViewType) { |
539 |
case SMALL: mouseOverView = new SmallChannelView(channel); break; |
540 |
case NORMAL: mouseOverView = new NormalChannelView(channel); break; |
541 |
default: mouseOverView = new NormalChannelView(channel); |
542 |
} |
543 |
|
544 |
mouseOverView.addEnhancedMouseListener(this); |
545 |
} |
546 |
|
547 |
return mouseOverView; |
548 |
} |
549 |
|
550 |
public ChannelView |
551 |
getCurrentView() { return currentView; } |
552 |
|
553 |
public ChannelView |
554 |
getOriginalView() { return originalView; } |
555 |
|
556 |
public void |
557 |
setView(ChannelView view) { |
558 |
setView(view, true); |
559 |
} |
560 |
|
561 |
public void |
562 |
setView(ChannelView view, boolean manual) { |
563 |
boolean selected = false; |
564 |
if(currentView != null) selected = currentView.isOptionsButtonSelected(); |
565 |
|
566 |
if(manual) { |
567 |
if(originalView != null) { |
568 |
originalView.removeEnhancedMouseListener(this); |
569 |
} |
570 |
|
571 |
if(originalView != currentView) destroyOriginalView(); |
572 |
if(currentView != null && currentView.getType() == view.getType()) { |
573 |
originalView = currentView; |
574 |
originalView.addEnhancedMouseListener(this); |
575 |
destroyView(view); |
576 |
return; |
577 |
} |
578 |
|
579 |
removeCurrentView(); |
580 |
|
581 |
originalView = view; |
582 |
originalView.addEnhancedMouseListener(this); |
583 |
currentView = view; |
584 |
} else { |
585 |
if(view.getType() == getCurrentView().getType()) { |
586 |
destroyView(view); |
587 |
return; |
588 |
} |
589 |
|
590 |
removeCurrentView(); |
591 |
currentView = view; |
592 |
} |
593 |
|
594 |
currentView.setOptionsButtonSelected(selected); |
595 |
|
596 |
updateView(); |
597 |
} |
598 |
|
599 |
private void |
600 |
updateView() { |
601 |
ChannelOptionsView view = getCurrentView().getChannelOptionsView(); |
602 |
if(view != null) optionsPane.setContentPane(view.getComponent()); |
603 |
|
604 |
updateChannelInfo(); |
605 |
|
606 |
mainPane.add(getCurrentView().getComponent()); |
607 |
mainPane.add(optionsPane); |
608 |
mainPane.validate(); |
609 |
mainPane.revalidate(); |
610 |
mainPane.repaint(); |
611 |
} |
612 |
|
613 |
public void |
614 |
fallbackToOriginalView() { |
615 |
if(currentView == originalView) return; |
616 |
|
617 |
boolean selected = false; |
618 |
if(currentView != null) selected = currentView.isOptionsButtonSelected(); |
619 |
|
620 |
removeCurrentView(); |
621 |
currentView = originalView; |
622 |
currentView.setOptionsButtonSelected(selected); |
623 |
|
624 |
updateView(); |
625 |
} |
626 |
|
627 |
private void |
628 |
removeCurrentView() { removeView(currentView); } |
629 |
|
630 |
private void |
631 |
destroyCurrentView() { destroyView(currentView); } |
632 |
|
633 |
private void |
634 |
removeOriginalView() { removeView(originalView); } |
635 |
|
636 |
private void |
637 |
destroyOriginalView() { destroyView(originalView); } |
638 |
|
639 |
private void |
640 |
removeView(ChannelView view) { |
641 |
if(view == null) return; |
642 |
|
643 |
mainPane.remove(view.getComponent()); |
644 |
mainPane.remove(optionsPane); |
645 |
|
646 |
destroyView(view); |
647 |
} |
648 |
|
649 |
private void |
650 |
destroyView(ChannelView view) { |
651 |
if(view == null) return; |
652 |
|
653 |
view.uninstallView(); |
654 |
|
655 |
view = null; |
656 |
} |
657 |
|
658 |
public boolean |
659 |
isMouseOverEnabled() { return mouseOverViewType != null; } |
660 |
|
661 |
private void |
662 |
mouseEntered() { |
663 |
if(!isMouseOverEnabled()) return; |
664 |
if(getCurrentView().getType() == getMouseOverView().getType()) return; |
665 |
|
666 |
JSChannel[] channels = CC.getMainFrame().getChannelsPane(0).getChannels(); |
667 |
for(JSChannel c : channels) { |
668 |
if(c == Channel.this) continue; |
669 |
|
670 |
Channel chn = (Channel)c; |
671 |
if(!(chn).isUsingOriginalView()) chn.fallbackToOriginalView(); |
672 |
} |
673 |
|
674 |
setView(getMouseOverView(), false); |
675 |
} |
676 |
|
677 |
private void |
678 |
mouseExited() { |
679 |
if(!isMouseOverEnabled()) return; |
680 |
if(getCurrentView().getType() == originalView.getType()) return; |
681 |
|
682 |
fallbackToOriginalView(); |
683 |
} |
684 |
|
685 |
@Override |
686 |
public void |
687 |
mouseEntered(MouseEvent e) { |
688 |
guiListener.actionPerformed(null); |
689 |
} |
690 |
|
691 |
@Override |
692 |
public void |
693 |
mouseExited(MouseEvent e) { |
694 |
guiListener.actionPerformed(null); |
695 |
} |
696 |
|
697 |
@Override |
698 |
public void |
699 |
mousePressed(MouseEvent e) { |
700 |
mousePressed = true; |
701 |
} |
702 |
|
703 |
@Override |
704 |
public void |
705 |
mouseReleased(MouseEvent e) { |
706 |
mousePressed = false; |
707 |
} |
708 |
|
709 |
public void |
710 |
onDestroy() { |
711 |
destroyCurrentView(); |
712 |
destroyOriginalView(); |
713 |
|
714 |
uninstallListeners(); |
715 |
|
716 |
if(currentView != null) { |
717 |
currentView.removeEnhancedMouseListener(this); |
718 |
} |
719 |
|
720 |
if(mouseOverView != null) { |
721 |
mouseOverView.removeEnhancedMouseListener(this); |
722 |
} |
723 |
|
724 |
String s = DIFFERENT_CHANNEL_VIEW_ON_MOUSE_OVER; |
725 |
preferences().removePropertyChangeListener(s, this); |
726 |
|
727 |
s = CHANNEL_VIEW_ON_MOUSE_OVER; |
728 |
preferences().removePropertyChangeListener(s, this); |
729 |
} |
730 |
|
731 |
@Override |
732 |
public void |
733 |
propertyChange(PropertyChangeEvent e) { |
734 |
updateMouseOverViewType(); |
735 |
} |
736 |
} |
737 |
|
738 |
class EditInstrumentAction extends AbstractAction implements SamplerChannelListener { |
739 |
EditInstrumentAction() { |
740 |
super(i18n.getMenuLabel("channels.editInstrument")); |
741 |
channelChanged(null); |
742 |
getModel().addSamplerChannelListener(this); |
743 |
} |
744 |
|
745 |
@Override |
746 |
public void |
747 |
actionPerformed(ActionEvent e) { |
748 |
CC.getSamplerModel().editBackendInstrument(getChannelId()); |
749 |
} |
750 |
|
751 |
@Override |
752 |
public void |
753 |
channelChanged(SamplerChannelEvent e) { |
754 |
boolean b = getChannelInfo().getInstrumentStatus() == 100; |
755 |
setEnabled(b); |
756 |
} |
757 |
|
758 |
@Override |
759 |
public void |
760 |
streamCountChanged(SamplerChannelEvent e) { } |
761 |
|
762 |
@Override |
763 |
public void |
764 |
voiceCountChanged(SamplerChannelEvent e) { } |
765 |
} |
766 |
|
767 |
class FxSendsAction extends AbstractAction { |
768 |
FxSendsAction() { |
769 |
super(i18n.getMenuLabel("channels.fxSends")); |
770 |
} |
771 |
|
772 |
@Override |
773 |
public void |
774 |
actionPerformed(ActionEvent e) { |
775 |
showFxSendsDialog(); |
776 |
} |
777 |
} |
778 |
|
779 |
class ChannelRoutingAction extends AbstractAction implements SamplerChannelListener { |
780 |
ChannelRoutingAction() { |
781 |
super(i18n.getMenuLabel("channels.channelRouting")); |
782 |
channelChanged(null); |
783 |
getModel().addSamplerChannelListener(this); |
784 |
} |
785 |
|
786 |
@Override |
787 |
public void |
788 |
actionPerformed(ActionEvent e) { |
789 |
SamplerChannel c = getChannelInfo(); |
790 |
new JSChannelOutputRoutingDlg(CC.getMainFrame(), c).setVisible(true); |
791 |
} |
792 |
|
793 |
@Override |
794 |
public void |
795 |
channelChanged(SamplerChannelEvent e) { |
796 |
boolean b = getChannelInfo().getAudioOutputDevice() != -1; |
797 |
setEnabled(b); |
798 |
} |
799 |
|
800 |
@Override |
801 |
public void |
802 |
streamCountChanged(SamplerChannelEvent e) { } |
803 |
|
804 |
@Override |
805 |
public void |
806 |
voiceCountChanged(SamplerChannelEvent e) { } |
807 |
} |
808 |
|
809 |
public ContextMenu |
810 |
getContextMenu() { return contextMenu; } |
811 |
|
812 |
class ContextMenu extends MouseAdapter { |
813 |
private JPopupMenu menu = null; |
814 |
|
815 |
ContextMenu() { |
816 |
|
817 |
} |
818 |
|
819 |
private void |
820 |
createMenu() { |
821 |
menu = new JPopupMenu(); |
822 |
menu.add(new JMenuItem(new EditInstrumentAction())); |
823 |
menu.addSeparator(); |
824 |
|
825 |
MenuManager.ChannelViewGroup group; |
826 |
group = new MenuManager.ChannelViewGroup(true, true); |
827 |
MenuManager.getMenuManager().registerChannelViewGroup(group); |
828 |
|
829 |
for(JMenuItem mi : group.getMenuItems()) menu.add(mi); |
830 |
|
831 |
menu.addSeparator(); |
832 |
|
833 |
menu.add(new JMenuItem(a4n.moveChannelsOnTop)); |
834 |
menu.add(new JMenuItem(a4n.moveChannelsUp)); |
835 |
menu.add(new JMenuItem(a4n.moveChannelsDown)); |
836 |
menu.add(new JMenuItem(a4n.moveChannelsAtBottom)); |
837 |
|
838 |
menu.add(new MainFrame.ToPanelMenu()); |
839 |
|
840 |
menu.addSeparator(); |
841 |
|
842 |
menu.add(new JMenuItem(new FxSendsAction())); |
843 |
menu.add(new JMenuItem(new ChannelRoutingAction())); |
844 |
} |
845 |
|
846 |
private JPopupMenu |
847 |
getMenu() { |
848 |
if(menu == null) createMenu(); |
849 |
return menu; |
850 |
} |
851 |
|
852 |
@Override |
853 |
public void |
854 |
mousePressed(MouseEvent e) { |
855 |
if(e.isPopupTrigger()) show(e); |
856 |
} |
857 |
|
858 |
@Override |
859 |
public void |
860 |
mouseReleased(MouseEvent e) { |
861 |
if(e.isPopupTrigger()) show(e); |
862 |
} |
863 |
|
864 |
void |
865 |
show(MouseEvent e) { |
866 |
getMenu().show(e.getComponent(), e.getX(), e.getY()); |
867 |
} |
868 |
} |
869 |
|
870 |
class FxSendsPane extends JSFxSendsPane { |
871 |
FxSendsPane(SamplerChannelModel model) { |
872 |
super(model); |
873 |
|
874 |
actionAddFxSend.putValue(Action.SMALL_ICON, Res.iconNew16); |
875 |
actionRemoveFxSend.putValue(Action.SMALL_ICON, Res.iconDelete16); |
876 |
} |
877 |
|
878 |
@Override |
879 |
protected JToolBar |
880 |
createToolBar() { |
881 |
JToolBar tb = new JToolBar(); |
882 |
Dimension d = new Dimension(Short.MAX_VALUE, tb.getPreferredSize().height); |
883 |
tb.setMaximumSize(d); |
884 |
tb.setFloatable(false); |
885 |
tb.setAlignmentX(JPanel.RIGHT_ALIGNMENT); |
886 |
|
887 |
tb.add(new ToolbarButton(actionAddFxSend)); |
888 |
tb.add(new ToolbarButton(actionRemoveFxSend)); |
889 |
|
890 |
return tb; |
891 |
} |
892 |
} |
893 |
|
894 |
public static class StreamVoiceCountPane extends JPanel { |
895 |
private final Channel channel; |
896 |
|
897 |
private final JLabel lStreams = createScreenLabel(" --"); |
898 |
private final JLabel lSlash = createScreenLabel("/"); |
899 |
private final JLabel lVoices = createScreenLabel("-- "); |
900 |
|
901 |
public |
902 |
StreamVoiceCountPane(Channel channel) { |
903 |
this.channel = channel; |
904 |
|
905 |
setOpaque(false); |
906 |
setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); |
907 |
lStreams.setFont(Res.fontScreenMono); |
908 |
lStreams.setHorizontalAlignment(JLabel.RIGHT); |
909 |
lStreams.setToolTipText(i18n.getLabel("Channel.streamVoiceCount")); |
910 |
|
911 |
Dimension d = lStreams.getPreferredSize(); |
912 |
lStreams.setMinimumSize(d); |
913 |
lStreams.setPreferredSize(d); |
914 |
lStreams.setMaximumSize(d); |
915 |
add(lStreams); |
916 |
|
917 |
lSlash.setFont(Res.fontScreenMono); |
918 |
lSlash.setToolTipText(i18n.getLabel("Channel.streamVoiceCount")); |
919 |
add(lSlash); |
920 |
|
921 |
lVoices.setFont(Res.fontScreenMono); |
922 |
lVoices.setToolTipText(i18n.getLabel("Channel.streamVoiceCount")); |
923 |
|
924 |
d = lStreams.getPreferredSize(); |
925 |
lVoices.setMinimumSize(d); |
926 |
lVoices.setPreferredSize(d); |
927 |
lVoices.setMaximumSize(d); |
928 |
add(lVoices); |
929 |
|
930 |
lStreams.addMouseListener(channel.getContextMenu()); |
931 |
lSlash.addMouseListener(channel.getContextMenu()); |
932 |
lVoices.addMouseListener(channel.getContextMenu()); |
933 |
} |
934 |
|
935 |
public void |
936 |
updateStreamCount(int count) { |
937 |
lStreams.setText(count == 0 ? " --" : String.valueOf(count)); |
938 |
} |
939 |
|
940 |
public void |
941 |
updateVoiceCount(int count) { |
942 |
lVoices.setText(count == 0 ? "-- " : String.valueOf(count)); |
943 |
} |
944 |
} |
945 |
|
946 |
public static class VolumePane extends JPanel { |
947 |
private final Channel channel; |
948 |
private final JButton btnVolume = createScreenButton(""); |
949 |
private JSVolumeEditorPopup popupVolume; |
950 |
|
951 |
private static NumberFormat numberFormat = NumberFormat.getInstance(); |
952 |
static { numberFormat.setMaximumFractionDigits(1); } |
953 |
|
954 |
public |
955 |
VolumePane(final Channel channel) { |
956 |
this.channel = channel; |
957 |
setOpaque(false); |
958 |
setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); |
959 |
|
960 |
btnVolume.setIcon(Res.iconVolume14); |
961 |
btnVolume.setIconTextGap(2); |
962 |
btnVolume.setAlignmentX(RIGHT_ALIGNMENT); |
963 |
btnVolume.setHorizontalAlignment(btnVolume.LEFT); |
964 |
updateVolumeInfo(100); |
965 |
Dimension d = btnVolume.getPreferredSize(); |
966 |
d.width = 57; |
967 |
btnVolume.setPreferredSize(d); |
968 |
btnVolume.setMinimumSize(d); |
969 |
|
970 |
add(btnVolume); |
971 |
|
972 |
btnVolume.addActionListener(new ActionListener() { |
973 |
public void |
974 |
actionPerformed(ActionEvent e) { |
975 |
if(popupVolume.isVisible()) { |
976 |
popupVolume.commit(); |
977 |
popupVolume.hide(); |
978 |
} else { |
979 |
float vol = channel.getModel().getChannelInfo().getVolume(); |
980 |
popupVolume.setCurrentVolume(vol); |
981 |
popupVolume.show(); |
982 |
} |
983 |
} |
984 |
}); |
985 |
|
986 |
popupVolume = new JSVolumeEditorPopup(btnVolume, VolumeType.CHANNEL); |
987 |
|
988 |
popupVolume.addActionListener(new ActionListener() { |
989 |
public void |
990 |
actionPerformed(ActionEvent e) { |
991 |
channel.getModel().setBackendVolume(popupVolume.getVolumeFactor()); |
992 |
} |
993 |
}); |
994 |
|
995 |
btnVolume.addMouseListener(channel.getContextMenu()); |
996 |
} |
997 |
|
998 |
public void |
999 |
updateVolumeInfo(int volume) { |
1000 |
if(CC.getViewConfig().isMeasurementUnitDecibel()) { |
1001 |
String s = numberFormat.format(HF.percentsToDecibels(volume)); |
1002 |
btnVolume.setText(s + "dB"); |
1003 |
} else { |
1004 |
btnVolume.setText(String.valueOf(volume) + "%"); |
1005 |
} |
1006 |
} |
1007 |
} |
1008 |
|
1009 |
public static class PowerButton extends PixmapToggleButton implements ActionListener { |
1010 |
private final Channel channel; |
1011 |
|
1012 |
PowerButton(Channel channel) { |
1013 |
this(channel, Res.gfxPowerOff, Res.gfxPowerOn); |
1014 |
} |
1015 |
|
1016 |
PowerButton(Channel channel, ImageIcon defaultIcon, ImageIcon selectedIcon) { |
1017 |
super(defaultIcon, selectedIcon); |
1018 |
|
1019 |
this.channel = channel; |
1020 |
|
1021 |
setSelected(true); |
1022 |
addActionListener(this); |
1023 |
setToolTipText(i18n.getButtonLabel("Channel.ttRemoveChannel")); |
1024 |
} |
1025 |
|
1026 |
@Override |
1027 |
public void |
1028 |
actionPerformed(ActionEvent e) { |
1029 |
boolean b = preferences().getBoolProperty(CONFIRM_CHANNEL_REMOVAL); |
1030 |
if(b) { |
1031 |
String s = i18n.getMessage("Channel.remove?", channel.getChannelId()); |
1032 |
if(!HF.showYesNoDialog(channel, s)) { |
1033 |
setSelected(true); |
1034 |
return; |
1035 |
} |
1036 |
} |
1037 |
channel.remove(); |
1038 |
} |
1039 |
|
1040 |
@Override |
1041 |
public boolean |
1042 |
contains(int x, int y) { return (x - 11)*(x - 11) + (y - 11)*(y - 11) < 71; } |
1043 |
} |
1044 |
|
1045 |
public static class OptionsButton extends PixmapToggleButton |
1046 |
implements ActionListener, PropertyChangeListener { |
1047 |
|
1048 |
private final Channel channel; |
1049 |
|
1050 |
OptionsButton(Channel channel) { |
1051 |
super(Res.gfxOptionsOff, Res.gfxOptionsOn); |
1052 |
|
1053 |
this.channel = channel; |
1054 |
|
1055 |
setRolloverIcon(Res.gfxOptionsOffRO); |
1056 |
this.setRolloverSelectedIcon(Res.gfxOptionsOnRO); |
1057 |
addActionListener(this); |
1058 |
setToolTipText(i18n.getButtonLabel("Channel.ttShowOptions")); |
1059 |
} |
1060 |
|
1061 |
@Override |
1062 |
public void |
1063 |
actionPerformed(ActionEvent e) { |
1064 |
ChannelView view = channel.viewTracker.getCurrentView(); |
1065 |
|
1066 |
if(isSelected() && view.getChannelOptionsView() == null) { |
1067 |
view.installChannelOptionsView(); |
1068 |
JComponent c = view.getChannelOptionsView().getComponent(); |
1069 |
channel.optionsPane.setContentPane(c); |
1070 |
} |
1071 |
|
1072 |
channel.showOptionsPane(isSelected()); |
1073 |
|
1074 |
String s; |
1075 |
if(isSelected()) s = i18n.getButtonLabel("Channel.ttHideOptions"); |
1076 |
else s = i18n.getButtonLabel("Channel.ttShowOptions"); |
1077 |
|
1078 |
setToolTipText(s); |
1079 |
|
1080 |
s = JXCollapsiblePane.ANIMATION_STATE_KEY; |
1081 |
channel.optionsPane.addPropertyChangeListener(s, this); |
1082 |
} |
1083 |
|
1084 |
@Override |
1085 |
public void |
1086 |
propertyChange(PropertyChangeEvent e) { |
1087 |
if(e.getNewValue() == "collapsed") { |
1088 |
ChannelView view = channel.viewTracker.getCurrentView(); |
1089 |
view.uninstallChannelOptionsView(); |
1090 |
channel.optionsPane.setContentPane(new JPanel()); |
1091 |
} |
1092 |
} |
1093 |
|
1094 |
public void |
1095 |
onDestroy() { |
1096 |
String s = JXCollapsiblePane.ANIMATION_STATE_KEY; |
1097 |
channel.optionsPane.removePropertyChangeListener(s, this); |
1098 |
} |
1099 |
|
1100 |
@Override |
1101 |
public boolean |
1102 |
contains(int x, int y) { return super.contains(x, y) & y < 13; } |
1103 |
} |
1104 |
} |
1105 |
|
1106 |
class ChannelOptionsPane extends JXCollapsiblePane { |
1107 |
ChannelOptionsPane() { |
1108 |
setAnimated(false); |
1109 |
setCollapsed(true); |
1110 |
setAnimated(preferences().getBoolProperty(ANIMATED)); |
1111 |
|
1112 |
preferences().addPropertyChangeListener(ANIMATED, new PropertyChangeListener() { |
1113 |
public void |
1114 |
propertyChange(PropertyChangeEvent e) { |
1115 |
setAnimated(preferences().getBoolProperty(ANIMATED)); |
1116 |
} |
1117 |
}); |
1118 |
} |
1119 |
|
1120 |
public void |
1121 |
showOptionsPane(boolean show) { setCollapsed(!show); } |
1122 |
} |