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

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

Apollo spin-off added

File size: 13.2 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.SampledTrackEvo4;
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 private 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 float ftmp = (float)COARSE_PAN_PIXEL_LENGTH / (float)getWidth();
136 int pan = (int)(getTimeScaleLength() * ftmp);
137 if (pan == 0) pan = 1;
138
139 if (e.getKeyCode() == KeyEvent.VK_RIGHT &&
140 (getSampledTrackModel().getSelectionStart() +
141 getSampledTrackModel().getSelectionLength()) < getSampledTrackModel().getFrameCount()) { // RIGHT
142
143 getSampledTrackModel().setSelection(getSampledTrackModel().getSelectionStart() + pan,
144 getSampledTrackModel().getSelectionLength());
145
146 } else if(e.getKeyCode() == KeyEvent.VK_LEFT &&
147 getSampledTrackModel().getSelectionStart() > 0) { // LEFT
148 getSampledTrackModel().setSelection(getSampledTrackModel().getSelectionStart() - pan,
149 getSampledTrackModel().getSelectionLength());
150 }
151 break;
152 }
153
154
155 }
156
157
158 public void keyTyped(KeyEvent e) {
159 }
160
161
162 public void mouseClicked(MouseEvent e) {
163 if (e.isConsumed()) return;
164
165 if (selectAllOnDoubleClick && getSampledTrackModel() != null && e.getClickCount() >= 2)
166 {
167 selectAllInView();
168
169 if (e.getButton() == MouseEvent.BUTTON3) { // copy
170 copySelection(e);
171 }
172
173 } else if (!isPlaying() &&
174 ((e.getButton() == MouseEvent.BUTTON2 // Insert
175 || e.getButton() == MouseEvent.BUTTON3) && !FreeItems.getInstance().isEmpty())) {
176
177 SampledTrackEvo4 floatingTrack = null;
178
179 // Look for a floating track for injection of audio bytes
180 for (Item i : FreeItems.getInstance()) {
181 if (i instanceof WidgetCorner) {
182 if (((WidgetCorner)i).getWidgetSource() instanceof SampledTrackEvo4) {
183 floatingTrack = (SampledTrackEvo4)((WidgetCorner)i).getWidgetSource();
184 break;
185 }
186 }
187 }
188
189 // Perform an injection
190 if (floatingTrack != null) {
191 try {
192 floatingTrack.injectAudio(this, e.getX(), e.getButton() == MouseEvent.BUTTON2);
193 } catch (IOException ex) {
194 ex.printStackTrace();
195 }
196 }
197
198 FrameGraphics.refresh(true);
199
200 }
201 }
202
203
204 public void mouseEntered(MouseEvent e) {
205 }
206
207
208 public void mouseExited(MouseEvent e) {
209 }
210
211
212 public void mousePressed(MouseEvent e) {
213 if (getSampledTrackModel() == null) return;
214 if (e.isConsumed()) return;
215
216 selectionStartX = e.getX();
217
218 // set selection mode
219 if (e.getButton() == MouseEvent.BUTTON3)
220 setSelectionMode(SELECTION_MODE_SELECT_AND_COPY);
221 else setSelectionMode(SELECTION_MODE_SELECT_ONLY);
222
223 // Set selection start, and length as a single frame
224 getSampledTrackModel().setSelection(frameAtX(e.getX()), 1);
225 }
226
227
228 public void mouseReleased(MouseEvent e) {
229 if (getSampledTrackModel() == null) return;
230 if (e.isConsumed()) return;
231
232 // If the mouse was released and the current selection mode is for copying frames...
233 if (selectionMode == SELECTION_MODE_SELECT_AND_COPY) {
234
235 // Get selection range in pixels to check if selection range is outside threshold...
236 int selectionStartXPixel = XatFrame(getSampledTrackModel().getSelectionStart());
237 int selectionEndXPixel = (getSampledTrackModel().getSelectionLength() > 1) ?
238 XatFrame(getSampledTrackModel().getSelectionStart() + getSampledTrackModel().getSelectionLength())
239 : -1;
240
241 // Only copy if valid range selected
242 int selLength = (selectionEndXPixel > selectionStartXPixel) ?
243 selectionEndXPixel - selectionStartXPixel : -1;
244
245 // Only copy if outside threshold - in pixels of course
246 if (selLength > COPY_SELECTION_PIXEL_RANGE_THRESHOLD) {
247
248 copySelection(e);
249
250 }
251
252 // Reset selection mode since copy HAVE FINISHED
253 setSelectionMode(SELECTION_MODE_SELECT_ONLY);
254
255 // Update the new selection color
256 invalidateSelection();
257 }
258 }
259
260 /**
261 * Copies the selection into free space as a {@link SampledTrackEvo4}
262 *
263 * @param e
264 */
265 private void copySelection(MouseEvent e) {
266
267 if (FreeItems.getInstance().isEmpty()) {
268 // Get copy of selected bytes
269 byte[] copiedAudioRegion = getSampledTrackModel().getSelectedFramesCopy();
270 assert(copiedAudioRegion != null);
271
272 Frame targetFrame = DisplayIO.getCurrentFrame();
273 if (targetFrame != null) {
274
275 SampledTrackEvo4 twidget = SampledTrackEvo4.createFromMemory(
276 copiedAudioRegion,
277 getSampledTrackModel().getFormat(),
278 targetFrame,
279 0,
280 0,
281 -1,
282 TrackNameCreator.getDefaultName(),
283 getMix());
284
285 /* getSampledTrackModel().getName() != null ?
286 TrackNameCreator.getNameCopy(getSampledTrackModel().getName() + "_section")
287 : null);*/
288
289 assert(Browser._theBrowser != null);
290
291 // A workround for getting the widget into fre space centered on cursor
292 Point p = e.getLocationOnScreen();
293 SwingUtilities.convertPointFromScreen(p, Browser._theBrowser.getContentPane());
294 twidget.setPosition(p.x - (twidget.getWidth() / 2), p.y - (twidget.getHeight() / 2));
295
296 for (Item i : twidget.getItems()) {
297 i.setOffset(new Point(
298 (i.getX() - DisplayIO.getMouseX()) + (twidget.getWidth() / 2),
299 (i.getY() - FrameMouseActions.getY()) + (twidget.getHeight() / 2)
300 ));
301 }
302
303 // Put the new widget into free space
304 FrameMouseActions.pickup(twidget.getItems());
305
306 }
307
308 }
309 }
310
311
312 public void mouseDragged(MouseEvent e) {
313 if (getSampledTrackModel() == null) return;
314 if (e.isConsumed()) return;
315 if (selectionStartX < 0) return;
316
317 // Set selection range
318 int frameAtCursor = frameAtX(e.getX());
319 int frameAtStartPoint = frameAtX(selectionStartX);
320
321 int start = Math.min(frameAtCursor, frameAtStartPoint);
322 int length = Math.max(frameAtCursor, frameAtStartPoint) - start;
323 if (length > 0) {
324 getSampledTrackModel().setSelection(start, length);
325 }
326 }
327
328
329 public void mouseMoved(MouseEvent e) {
330
331 // Ensure selection is not out of synch - could have missed some mouse events.
332 if (e.getButton() == MouseEvent.NOBUTTON
333 && selectionMode != SELECTION_MODE_SELECT_ONLY) {
334 setSelectionMode(SELECTION_MODE_SELECT_ONLY);
335 invalidateSelection();
336 }
337
338 }
339
340
341 @Override
342 public void modelChanged(Subject source, SubjectChangedEvent event) {
343 super.modelChanged(source, event);
344
345 if (getSampledTrackModel() == null) return;
346
347 switch(event.getID()) {
348
349 // Whenever the selection changes, update the selection backing & invalidate
350 case ApolloSubjectChangedEvent.SELECTION_CHANGED:
351
352 selectionBackSection.left = XatFrame(getSampledTrackModel().getSelectionStart());
353
354 selectionBackSection.width = (getSampledTrackModel().getSelectionLength() <= 1) ?
355 -1 :
356 XatFrame(getSampledTrackModel().getSelectionStart() + getSampledTrackModel().getSelectionLength())
357 - selectionBackSection.left;
358
359 if (selectionBackSection.visible) {
360
361 invalidateSelection();
362 }
363
364 break;
365
366 }
367
368
369 }
370
371 /**
372 * If the selection region drawn differs to the current selection region,
373 * then the dirty regions will be invalidated
374 *
375 */
376 public void invalidateSelection() {
377
378 invalidateAll(); // TODO: FINE GRAINED VERSION - MORE EFFICIENT
379 }
380
381
382 @Override
383 public void paint(Graphics g) {
384 super.paint(g);
385
386 // Draw selection start bar
387 if (getSampledTrackModel().getSelectionStart() >= getTimeScaleStart()) {
388
389 ((Graphics2D)g).setStroke(GRAPH_BAR_STROKE);
390
391 // Note that if the start line is near the edges of the panel it can be concealed - thus
392 // set a thick line on the edges
393 int x = selectionBackSection.left;
394 if (x == 0) {
395 x = 1;
396 } else if (x == getWidth()){
397 x = getWidth() - 1;
398 }
399
400 g.setColor(Color.RED);
401 g.drawLine(
402 x,
403 0,
404 x,
405 getHeight());
406
407 }
408
409 paintLock(g);
410
411
412 }
413
414 private void paintLock(Graphics g) {
415 if (isPlaying()) {
416 IconRepository.getIcon("lock.png").paintIcon(null,
417 g,
418 getWidth() - LOCK_ICON_CORNER_OFFSET,
419 LOCK_ICON_CORNER_OFFSET - 16);
420 }
421 }
422
423 private void invalidateLockIcon() {
424 Point exp = getExpediteePoint();
425 if (exp != null) {
426 FrameGraphics.invalidateArea(new Rectangle(
427 exp.x + getWidth() - LOCK_ICON_CORNER_OFFSET,
428 exp.y + LOCK_ICON_CORNER_OFFSET - 16,
429 16, 16));
430 }
431
432 }
433
434 @Override
435 protected void onPlaybackStarted() {
436 super.onPlaybackStarted();
437 invalidateLockIcon();
438 }
439
440 @Override
441 protected void onPlaybackStopped() {
442 super.onPlaybackStopped();
443 invalidateLockIcon();
444 }
445
446 @Override
447 protected void fireTimelineChanged() {
448 super.fireTimelineChanged();
449 // Keep selection consistent when timeline changes
450 selectionBackSection.updateBounds();
451 }
452
453 /**
454 * Injects bytes into the observed {@link SampledTrackModel} - if one is set and is not playing.
455 * Also resets selection - to select all on the new bytes
456 *
457 * @see SampledTrackModel#insertBytes(byte[], javax.sound.sampled.AudioFormat, int)
458 */
459 public void insertAudio(byte[] bytesToAdd, AudioFormat format, int framePosition)
460 throws IOException {
461 if (getSampledTrackModel() != null && !isPlaying()) {
462
463 // Perform insert
464 getSampledTrackModel().insertBytes(bytesToAdd, format, framePosition);
465
466 // Set new selection
467 getSampledTrackModel().setSelection(framePosition, bytesToAdd.length / format.getFrameSize());
468 }
469
470
471 }
472
473 public void setSelectAllOnDoubleClick(boolean selectAllOnDoubleClick) {
474 this.selectAllOnDoubleClick = selectAllOnDoubleClick;
475 }
476
477 private class SelectionBackSection extends BackSection
478 {
479
480 public SelectionBackSection() {
481 super();
482 visible = true;
483 }
484
485 @Override
486 void paint(Graphics g) {
487
488 color = (selectionMode == SELECTION_MODE_SELECT_ONLY) ?
489 SELECTION_BACKING_COLOR_SELECT_ONLY : SELECTION_BACKING_COLOR_SELECT_AND_COPY;
490 super.paint(g);
491 }
492
493 @Override
494 void updateBounds() {
495
496 assert(getSampledTrackModel() != null);
497
498 left = XatFrame(getSampledTrackModel().getSelectionStart());
499
500 width = (getSampledTrackModel().getSelectionLength() <= 1) ?
501 -1 :
502 XatFrame(getSampledTrackModel().getSelectionStart() + getSampledTrackModel().getSelectionLength())
503 - left;
504 }
505 }
506
507}
Note: See TracBrowser for help on using the repository browser.