source: trunk/src_apollo/org/apollo/gui/EditableSampledTrackGraphView.java@ 355

Last change on this file since 355 was 355, checked in by bjn8, 16 years ago

Many fixes and usability improvements.

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