source: trunk/src/org/apollo/gui/EditableSampledTrackGraphView.java@ 1102

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

File size: 14.3 KB
Line 
1package org.apollo.gui;
2
3import java.awt.Graphics;
4import java.awt.Graphics2D;
5import java.awt.Point;
6import java.awt.event.KeyEvent;
7import java.awt.event.KeyListener;
8import java.awt.event.MouseEvent;
9import java.awt.event.MouseListener;
10import java.awt.event.MouseMotionListener;
11import java.io.IOException;
12
13import javax.sound.sampled.AudioFormat;
14import javax.swing.SwingUtilities;
15
16import org.apollo.audio.ApolloSubjectChangedEvent;
17import org.apollo.audio.SampledTrackModel;
18import org.apollo.io.IconRepository;
19import org.apollo.mvc.Subject;
20import org.apollo.mvc.SubjectChangedEvent;
21import org.apollo.util.TrackNameCreator;
22import org.apollo.widgets.SampledTrack;
23import org.expeditee.core.Colour;
24import org.expeditee.core.bounds.AxisAlignedBoxBounds;
25import org.expeditee.gio.gesture.StandardGestureActions;
26import org.expeditee.gio.swing.SwingConversions;
27import org.expeditee.gio.swing.SwingMiscManager;
28import org.expeditee.gui.Browser;
29import org.expeditee.gui.DisplayController;
30import org.expeditee.gui.Frame;
31import org.expeditee.gui.FreeItems;
32import org.expeditee.items.Item;
33import org.expeditee.items.widgets.WidgetCorner;
34
35/**
36 *
37 * EditableSampledTrackGraphView's provide selection, removing, copying and inserting audio.
38 *
39 * @author Brook Novak
40 *
41 */
42public class EditableSampledTrackGraphView
43 extends SampledTrackGraphView
44 implements MouseListener, MouseMotionListener, KeyListener {
45
46 private static final long serialVersionUID = 1L;
47
48 private int selectionMode = SELECTION_MODE_SELECT_ONLY;
49 private int selectionStartX = 0;
50
51 private SelectionBackSection selectionBackSection;
52
53 private boolean selectAllOnDoubleClick = false;
54
55 public static final Colour SELECTION_BACKING_COLOR_SELECT_ONLY = Colour.BLACK;
56 private static final Colour SELECTION_BACKING_COLOR_SELECT_AND_COPY = Colour.GREEN;
57
58 private static final int SELECTION_MODE_SELECT_ONLY = 1;
59 private static final int SELECTION_MODE_SELECT_AND_COPY = 2;
60
61 private static final int COPY_SELECTION_PIXEL_RANGE_THRESHOLD = 8;
62 private static final int COARSE_PAN_PIXEL_LENGTH = 10;
63 public static final int MIN_FRAME_SELECTION_SIZE = 100;
64
65 public static final int LOCK_ICON_CORNER_OFFSET = 20;
66
67 public EditableSampledTrackGraphView() {
68
69 // Create selection back section
70 selectionBackSection = new SelectionBackSection();
71 addBackingSection(selectionBackSection);
72
73 reAddListeners();
74
75 setSelectionMode(SELECTION_MODE_SELECT_ONLY);
76
77 }
78
79 public void reAddListeners() {
80 removeMouseListener(this);
81 removeMouseMotionListener(this);
82 removeKeyListener(this);
83
84 addMouseListener(this);
85 addMouseMotionListener(this);
86 addKeyListener(this);
87 }
88
89 /**
90 * Use this for setting selectionMode. Note is does not invlaidate.
91 *
92 * @param mode
93 * The new mode to set.
94 */
95 private void setSelectionMode(int mode) {
96 selectionMode = mode;
97 if (selectionMode == SELECTION_MODE_SELECT_ONLY) {
98
99 } else if (selectionMode == SELECTION_MODE_SELECT_AND_COPY) {
100
101 } else {
102 assert(false);
103 }
104 }
105
106
107 public void keyPressed(KeyEvent e) {
108 }
109
110 public void selectAllInView() {
111 if (getSampledTrackModel() == null) return;
112
113 getSampledTrackModel().setSelection(
114 getTimeScaleStart(),
115 getTimeScaleLength());
116 }
117
118 public void keyReleased(KeyEvent e) {
119 if (getSampledTrackModel() == null) return;
120 if (e.isConsumed()) return;
121
122 switch (e.getKeyCode()) {
123 case KeyEvent.VK_DELETE:
124
125 if (getSampledTrackModel().getSelectionLength() > 1
126 && !isPlaying()
127 && (getSampledTrackModel().getFrameCount() - getSampledTrackModel().getSelectionLength()) > MIN_FRAME_SELECTION_SIZE) {
128
129 getSampledTrackModel().removeSelectedBytes();
130
131 }
132
133 case KeyEvent.VK_LEFT:
134 case KeyEvent.VK_RIGHT:
135
136 // Select to end of track?
137 if (e.isShiftDown()) {
138 if (e.getKeyCode() == KeyEvent.VK_LEFT) {
139
140 int oldStart = getSampledTrackModel().getSelectionStart();
141 int oldLength = getSampledTrackModel().getSelectionLength();
142 if (oldLength < 1) oldLength = 1;
143
144 getSampledTrackModel().setSelection(0, oldStart + oldLength);
145
146 } else { // Right
147
148 getSampledTrackModel().setSelection(
149 getSampledTrackModel().getSelectionStart(),
150 getSampledTrackModel().getFrameCount() - getSampledTrackModel().getSelectionStart());
151
152 }
153 } else {
154 float ftmp = (float)COARSE_PAN_PIXEL_LENGTH / (float)getWidth();
155 int pan = (int)(getTimeScaleLength() * ftmp);
156 if (pan == 0) pan = 1;
157
158 if (e.getKeyCode() == KeyEvent.VK_RIGHT &&
159 (getSampledTrackModel().getSelectionStart() +
160 getSampledTrackModel().getSelectionLength()) < getSampledTrackModel().getFrameCount()) { // RIGHT
161
162 getSampledTrackModel().setSelection(getSampledTrackModel().getSelectionStart() + pan,
163 getSampledTrackModel().getSelectionLength());
164
165 } else if(e.getKeyCode() == KeyEvent.VK_LEFT &&
166 getSampledTrackModel().getSelectionStart() > 0) { // LEFT
167 getSampledTrackModel().setSelection(getSampledTrackModel().getSelectionStart() - pan,
168 getSampledTrackModel().getSelectionLength());
169 }
170 }
171 break;
172 }
173
174
175 }
176
177
178 public void keyTyped(KeyEvent e) {
179 }
180
181
182 public void mouseClicked(MouseEvent e) {
183 if (e.isConsumed()) return;
184
185 if (selectAllOnDoubleClick && getSampledTrackModel() != null && e.getClickCount() >= 2)
186 {
187 selectAllInView();
188
189 if (e.getButton() == MouseEvent.BUTTON3) { // copy
190 copySelection(e);
191 }
192
193 } else if (!isPlaying() &&
194 ((e.getButton() == MouseEvent.BUTTON2 // Insert
195 || e.getButton() == MouseEvent.BUTTON3) && !FreeItems.getInstance().isEmpty())) {
196
197 SampledTrack floatingTrack = null;
198
199 // Look for a floating track for injection of audio bytes
200 for (Item i : FreeItems.getInstance()) {
201 if (i instanceof WidgetCorner) {
202 if (((WidgetCorner)i).getWidgetSource() instanceof SampledTrack) {
203 floatingTrack = (SampledTrack)((WidgetCorner)i).getWidgetSource();
204 break;
205 }
206 }
207 }
208
209 // Perform an injection
210 if (floatingTrack != null) {
211 try {
212 floatingTrack.injectAudio(this, e.getX(), e.getButton() == MouseEvent.BUTTON2);
213 } catch (IOException ex) {
214 ex.printStackTrace();
215 }
216 }
217
218 DisplayController.requestRefresh(true);
219
220 }
221 }
222
223
224 public void mouseEntered(MouseEvent e) {
225 }
226
227
228 public void mouseExited(MouseEvent e) {
229 }
230
231
232 public void mousePressed(MouseEvent e) {
233 if (getSampledTrackModel() == null) return;
234 if (e.isConsumed()) return;
235
236 selectionStartX = e.getX();
237
238 // set selection mode
239 if (e.getButton() == MouseEvent.BUTTON3)
240 setSelectionMode(SELECTION_MODE_SELECT_AND_COPY);
241 else setSelectionMode(SELECTION_MODE_SELECT_ONLY);
242
243 // Set selection start, and length as a single frame
244 getSampledTrackModel().setSelection(frameAtX(e.getX()), 1);
245 }
246
247
248 public void mouseReleased(MouseEvent e) {
249 if (getSampledTrackModel() == null) return;
250 if (e.isConsumed()) return;
251
252 // If the mouse was released and the current selection mode is for copying frames...
253 if (selectionMode == SELECTION_MODE_SELECT_AND_COPY) {
254
255 // Get selection range in pixels to check if selection range is outside threshold...
256 int selectionStartXPixel = XatFrame(getSampledTrackModel().getSelectionStart());
257 int selectionEndXPixel = (getSampledTrackModel().getSelectionLength() > 1) ?
258 XatFrame(getSampledTrackModel().getSelectionStart() + getSampledTrackModel().getSelectionLength())
259 : -1;
260
261 // Only copy if valid range selected
262 int selLength = (selectionEndXPixel > selectionStartXPixel) ?
263 selectionEndXPixel - selectionStartXPixel : -1;
264
265 // Only copy if outside threshold - in pixels of course
266 if (selLength > COPY_SELECTION_PIXEL_RANGE_THRESHOLD) {
267
268 copySelection(e);
269
270 }
271
272 // Reset selection mode since copy HAVE FINISHED
273 setSelectionMode(SELECTION_MODE_SELECT_ONLY);
274
275 // Update the new selection color
276 invalidateSelection();
277 }
278 }
279
280 /**
281 * Copies the selection into free space as a {@link SampledTrack}
282 *
283 * @param e
284 */
285 private void copySelection(MouseEvent e) {
286
287 if (FreeItems.getInstance().isEmpty()) {
288 // Get copy of selected bytes
289 byte[] copiedAudioRegion = getSampledTrackModel().getSelectedFramesCopy();
290 assert(copiedAudioRegion != null);
291
292 Frame targetFrame = DisplayController.getCurrentFrame();
293 if (targetFrame != null) {
294
295 SampledTrack twidget = SampledTrack.createFromMemory(
296 copiedAudioRegion,
297 getSampledTrackModel().getFormat(),
298 targetFrame,
299 0,
300 0,
301 TrackNameCreator.getDefaultName(),
302 getMix());
303
304 /* getSampledTrackModel().getName() != null ?
305 TrackNameCreator.getNameCopy(getSampledTrackModel().getName() + "_section")
306 : null);*/
307
308 assert(Browser._theBrowser != null);
309
310 // A workaround for getting the widget into free space centred on cursor
311 Point p = e.getLocationOnScreen();
312 SwingUtilities.convertPointFromScreen(p, SwingMiscManager.getIfUsingSwingGraphicsManager().getContentPane());
313 twidget.setPosition(p.x - (twidget.getWidth() / 2), p.y - (twidget.getHeight() / 2));
314
315 for (Item i : twidget.getItems()) {
316 i.setOffset(SwingConversions.fromSwingPoint(new Point(
317 (i.getX() - DisplayController.getMouseX()) + (twidget.getWidth() / 2),
318 (i.getY() - DisplayController.getMouseY()) + (twidget.getHeight() / 2)
319 )));
320 }
321
322 // Put the new widget into free space
323 StandardGestureActions.pickup(twidget.getItems());
324
325 }
326
327 }
328 }
329
330
331 public void mouseDragged(MouseEvent e) {
332 if (getSampledTrackModel() == null) return;
333 if (e.isConsumed()) return;
334 if (selectionStartX < 0) return;
335
336 // Set selection range
337 int frameAtCursor = frameAtX(e.getX());
338 int frameAtStartPoint = frameAtX(selectionStartX);
339
340 int start = Math.min(frameAtCursor, frameAtStartPoint);
341 int length = Math.max(frameAtCursor, frameAtStartPoint) - start;
342 if (length > 0) {
343 getSampledTrackModel().setSelection(start, length);
344 }
345 }
346
347
348 public void mouseMoved(MouseEvent e) {
349
350 // Ensure selection is not out of synch - could have missed some mouse events.
351 if (e.getButton() == MouseEvent.NOBUTTON
352 && selectionMode != SELECTION_MODE_SELECT_ONLY) {
353 setSelectionMode(SELECTION_MODE_SELECT_ONLY);
354 invalidateSelection();
355 }
356
357 }
358
359
360 @Override
361 public void modelChanged(Subject source, SubjectChangedEvent event) {
362 super.modelChanged(source, event);
363
364 if (getSampledTrackModel() == null) return;
365
366 switch(event.getID()) {
367
368 // Whenever the selection changes, update the selection backing & invalidate
369 case ApolloSubjectChangedEvent.SELECTION_CHANGED:
370
371 selectionBackSection.left = XatFrame(getSampledTrackModel().getSelectionStart());
372
373 selectionBackSection.width = (getSampledTrackModel().getSelectionLength() <= 1) ?
374 -1 :
375 XatFrame(getSampledTrackModel().getSelectionStart() + getSampledTrackModel().getSelectionLength())
376 - selectionBackSection.left;
377
378 if (selectionBackSection.visible) {
379
380 invalidateSelection();
381 }
382
383 break;
384
385 }
386
387
388 }
389
390 /**
391 * If the selection region drawn differs to the current selection region,
392 * then the dirty regions will be invalidated
393 *
394 */
395 public void invalidateSelection() {
396
397 invalidateAll(); // TODO: FINE GRAINED VERSION - MORE EFFICIENT
398 }
399
400
401 @Override
402 public void paint(Graphics g)
403 {
404 super.paint(g);
405
406 // Draw selection start bar
407 if (getSampledTrackModel().getSelectionStart() >= getTimeScaleStart()) {
408
409 ((Graphics2D)g).setStroke(GRAPH_BAR_STROKE);
410
411 // Note that if the start line is near the edges of the panel it can be concealed - thus
412 // set a thick line on the edges
413 int x = selectionBackSection.left;
414 if (x == 0) {
415 x = 1;
416 } else if (x == getWidth()){
417 x = getWidth() - 1;
418 }
419
420 g.setColor(SwingConversions.toSwingColor(Colour.RED));
421 g.drawLine(x, 0, x, getHeight());
422
423 }
424
425 paintLock(g);
426
427
428 }
429
430 private void paintLock(Graphics g) {
431 if (isPlaying()) {
432 /*IconRepository.getIcon("lock.png").paintIcon(null,
433 g,
434 getWidth() - LOCK_ICON_CORNER_OFFSET,
435 LOCK_ICON_CORNER_OFFSET - 16);*/
436
437 g.drawImage(SwingMiscManager.getIfUsingSwingImageManager().getInternalImage(IconRepository.getIcon("lock.png")),
438 getWidth() - LOCK_ICON_CORNER_OFFSET,
439 LOCK_ICON_CORNER_OFFSET - 16,
440 null);
441 }
442 }
443
444 private void invalidateLockIcon() {
445 Point exp = getExpediteePoint();
446 if (exp != null) {
447 DisplayController.invalidateArea(new AxisAlignedBoxBounds(
448 exp.x + getWidth() - LOCK_ICON_CORNER_OFFSET,
449 exp.y + LOCK_ICON_CORNER_OFFSET - 16,
450 16, 16));
451 }
452
453 }
454
455 @Override
456 protected void onPlaybackStarted() {
457 super.onPlaybackStarted();
458 invalidateLockIcon();
459 }
460
461 @Override
462 protected void onPlaybackStopped() {
463 super.onPlaybackStopped();
464 invalidateLockIcon();
465 }
466
467 @Override
468 protected void fireTimelineChanged() {
469 super.fireTimelineChanged();
470 // Keep selection consistent when timeline changes
471 selectionBackSection.updateBounds();
472 }
473
474 /**
475 * Injects bytes into the observed {@link SampledTrackModel} - if one is set and is not playing.
476 * Also resets selection - to select all on the new bytes
477 *
478 * @see SampledTrackModel#insertBytes(byte[], javax.sound.sampled.AudioFormat, int)
479 */
480 public void insertAudio(byte[] bytesToAdd, AudioFormat format, int framePosition)
481 throws IOException {
482 if (getSampledTrackModel() != null && !isPlaying()) {
483
484 // Perform insert
485 getSampledTrackModel().insertBytes(bytesToAdd, format, framePosition);
486
487 // Set new selection
488 getSampledTrackModel().setSelection(framePosition, bytesToAdd.length / format.getFrameSize());
489 }
490
491
492 }
493
494 public void setSelectAllOnDoubleClick(boolean selectAllOnDoubleClick) {
495 this.selectAllOnDoubleClick = selectAllOnDoubleClick;
496 }
497
498 private class SelectionBackSection extends BackSection
499 {
500
501 public SelectionBackSection() {
502 super();
503 visible = true;
504 }
505
506 @Override
507 void paint(Graphics g) {
508
509 color = (selectionMode == SELECTION_MODE_SELECT_ONLY) ?
510 SwingConversions.toSwingColor(SELECTION_BACKING_COLOR_SELECT_ONLY) :
511 SwingConversions.toSwingColor(SELECTION_BACKING_COLOR_SELECT_AND_COPY);
512 super.paint(g);
513 }
514
515 @Override
516 void updateBounds() {
517
518 assert(getSampledTrackModel() != null);
519
520 left = XatFrame(getSampledTrackModel().getSelectionStart());
521
522 width = (getSampledTrackModel().getSelectionLength() <= 1) ?
523 -1 :
524 XatFrame(getSampledTrackModel().getSelectionStart() + getSampledTrackModel().getSelectionLength())
525 - left;
526 }
527 }
528
529}
Note: See TracBrowser for help on using the repository browser.