001: /*
002: * Jacareto Copyright (c) 2002-2005
003: * Applied Computer Science Research Group, Darmstadt University of
004: * Technology, Institute of Mathematics & Computer Science,
005: * Ludwigsburg University of Education, and Computer Based
006: * Learning Research Group, Aachen University. All rights reserved.
007: *
008: * Jacareto is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation; either
011: * version 2 of the License, or (at your option) any later version.
012: *
013: * Jacareto is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public
019: * License along with Jacareto; if not, write to the Free
020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: *
022: */
023:
024: package jacareto.trackimpl;
025:
026: import jacareto.cleverphl.gui.TrackEditorFrame;
027: import jacareto.system.Environment;
028: import jacareto.track.TrackModel;
029: import jacareto.track.TrackSelectionEvent;
030: import jacareto.track.TrackSelectionListener;
031: import jacareto.track.block.Block;
032: import jacareto.track.block.BlockType;
033: import jacareto.trackimpl.blockimpl.MediaBlock;
034:
035: import org.apache.commons.lang.Validate;
036:
037: import java.awt.AlphaComposite;
038: import java.awt.Color;
039: import java.awt.GradientPaint;
040: import java.awt.Graphics;
041: import java.awt.Graphics2D;
042: import java.awt.Point;
043: import java.awt.Rectangle;
044: import java.awt.event.MouseAdapter;
045: import java.awt.event.MouseEvent;
046: import java.awt.event.MouseMotionAdapter;
047:
048: import java.util.Iterator;
049:
050: import javax.swing.BorderFactory;
051: import javax.swing.JPanel;
052:
053: /**
054: * <p>
055: * Panel where all blocks and tracks are shown.
056: * </p>
057: *
058: * @author Oliver Specht
059: * @version $revision$
060: */
061: public class TrackViewPanel extends JPanel implements
062: TrackSelectionListener {
063: /** The popup menu which is activated when mouse is over a block */
064: TrackViewPopupMenu popupMenu;
065:
066: /**
067: * The PopupListener (MouseListener). Has to be created here to add and remove it in case of
068: * moving over a block.
069: */
070: PopupListener popupListener = new PopupListener();
071:
072: /** The TrackModel */
073: TrackModel trackModel;
074:
075: /** The environment. */
076: private Environment env;
077:
078: /** The Helper to calculate coordinates etc. */
079: ViewPanelHelper viewPanelHelper;
080:
081: /** The TrackEditorFrame this panel is shown on */
082: private TrackEditorFrame parentFrame;
083: private final int offsetBackgroundBar = 3;
084:
085: /** The colors of the bars */
086: private final Color videoBarColor = new Color(252, 165, 48);
087: private final Color audioBarColor = new Color(67, 141, 233);
088: private final Color trackBarActiveColor = new Color(93, 195, 86);
089: private final Color trackBarInactiveColor = new Color(136, 136, 136);
090: private Point rightClickPosition;
091:
092: /**
093: * <p>
094: * Creates a new TrackViewPanel with the given {@link TrackModel} and {@link
095: * jacareto.track.TrackSelectionModel}.
096: * </p>
097: *
098: * @param env the environment
099: * @param parent the calling {@link TrackEditorFrame}
100: */
101: public TrackViewPanel(Environment env, TrackEditorFrame parent) {
102: Validate.notNull(parent);
103:
104: this .env = env;
105: this .parentFrame = parent;
106: init();
107: }
108:
109: /**
110: * <p>
111: * Initializes the Panel with Listeners, Helper etc.
112: * </p>
113: */
114: private void init() {
115: viewPanelHelper = new ViewPanelHelper();
116:
117: this .popupMenu = new TrackViewPopupMenu(env, this );
118: this .addMouseListener(new BlockMouseListener());
119: this .addMouseMotionListener(new BlockMouseMotionListener());
120: this .setBackground(Color.WHITE);
121: this .setBorder(BorderFactory.createLineBorder(Color.BLACK));
122: }
123:
124: /**
125: * <p>
126: * Returns the TrackModel of this TrackViewPanel
127: * </p>
128: *
129: * @return TrackModel {@link TrackModel}
130: */
131: public TrackModel getTrackModel() {
132: return trackModel;
133: }
134:
135: /**
136: * <p>
137: * Overwritten paintComponent method to paint all blocks.
138: * </p>
139: *
140: * @param g {@link Graphics} object to paint on
141: */
142: protected void paintComponent(Graphics g) {
143: super .paintComponent(g);
144:
145: Graphics2D graphics = (Graphics2D) g;
146:
147: paintPanelBackground(graphics);
148:
149: viewPanelHelper.setViewPanelSize(this .getWidth(), this
150: .getHeight());
151:
152: paintVideoBackground(graphics);
153: paintAudioBackground(graphics);
154:
155: Iterator iter = viewPanelHelper.getVisibleBlocks().iterator();
156:
157: while (iter.hasNext()) {
158: Block block = (Block) iter.next();
159: Rectangle rect = viewPanelHelper.getRectangle(block);
160:
161: if (viewPanelHelper.isBlockMarked(block)) {
162: graphics.setColor(trackBarActiveColor);
163: paintTrackBar(graphics, rect);
164: } else {
165: graphics.setColor(trackBarInactiveColor);
166: paintTrackBar(graphics, rect);
167: }
168: }
169: }
170:
171: /**
172: * <p>
173: * Paints the TrackBar where all tracks are shown on.
174: * </p>
175: *
176: * @param graphics {@link Graphics2D} object to paint on
177: * @param rect {@link Rectangle} to be painted in
178: */
179: private void paintTrackBar(Graphics2D graphics, Rectangle rect) {
180: graphics.setComposite(AlphaComposite.getInstance(
181: AlphaComposite.SRC_OVER, .6f));
182: graphics.fill(rect);
183: graphics.setComposite(AlphaComposite.getInstance(
184: AlphaComposite.SRC_OVER, 1.0f));
185: graphics.setColor(Color.black);
186: graphics.draw(rect);
187: }
188:
189: /**
190: * <p>
191: * Paints the background of the video track.
192: * </p>
193: *
194: * @param graphics {@link Graphics2D} object to paint on
195: */
196: private void paintVideoBackground(Graphics2D graphics) {
197: String barInfo = env.getLanguage().getString(
198: "Track.TrackViewPanel.Video");
199:
200: Rectangle videoBar = new Rectangle(0, viewPanelHelper
201: .getYPositionVideo()
202: - offsetBackgroundBar, this .getWidth(), viewPanelHelper
203: .getHeightVideo()
204: + (offsetBackgroundBar * 2));
205:
206: graphics.setPaint(Color.white);
207: graphics.fill(videoBar);
208:
209: graphics.setComposite(AlphaComposite.getInstance(
210: AlphaComposite.SRC_OVER, .3f));
211: graphics.setPaint(videoBarColor);
212:
213: graphics.fill(videoBar);
214: graphics.setComposite(AlphaComposite.getInstance(
215: AlphaComposite.SRC_OVER, 1.0f));
216: graphics.draw(videoBar);
217:
218: int textWidth = graphics.getFontMetrics().stringWidth(barInfo);
219: graphics
220: .drawString(
221: barInfo,
222: (int) ((videoBar.getWidth() / 2) - (textWidth / 2)),
223: (int) (videoBar.getY()
224: + (videoBar.getHeight() / 2) + (graphics
225: .getFont().getSize() / 2)));
226: }
227:
228: /**
229: * <p>
230: * Enables/disables the currently selected Track.
231: * </p>
232: *
233: * @param enabled true if track should be enabled
234: */
235: public void setTrackEnabled(boolean enabled) {
236: BlockType type = this .viewPanelHelper
237: .getClickedTrack(this .rightClickPosition);
238:
239: if (type != null) {
240: if (type.equals(BlockType.AUDIO)) {
241: this .parentFrame.setAudioEnabled(enabled);
242: } else if (type.equals(BlockType.VIDEO)) {
243: this .parentFrame.setVideoEnabled(enabled);
244: }
245: }
246: }
247:
248: /**
249: * <p>
250: * Stops the track the user has right clicked in.
251: * </p>
252: */
253: public void stopTrack() {
254: BlockType type = this .viewPanelHelper
255: .getClickedTrack(this .rightClickPosition);
256:
257: if (type != null) {
258: if (type.equals(BlockType.AUDIO)) {
259: this .parentFrame.stopAudio();
260: } else if (type.equals(BlockType.VIDEO)) {
261: this .parentFrame.stopVideo();
262: }
263: }
264: }
265:
266: /**
267: * <p>
268: * Paints the background of the audio track.
269: * </p>
270: *
271: * @param graphics {@link Graphics2D} object to paint on
272: */
273: private void paintAudioBackground(Graphics2D graphics) {
274: String barInfo = env.getLanguage().getString(
275: "Track.TrackViewPanel.Audio");
276: ;
277:
278: Rectangle audioBar = new Rectangle(0, viewPanelHelper
279: .getYPositionAudio()
280: - offsetBackgroundBar, this .getWidth(), viewPanelHelper
281: .getHeightAudio()
282: + (offsetBackgroundBar * 2));
283:
284: graphics.setPaint(Color.white);
285: graphics.fill(audioBar);
286:
287: graphics.setComposite(AlphaComposite.getInstance(
288: AlphaComposite.SRC_OVER, .3f));
289: graphics.setPaint(audioBarColor);
290:
291: graphics.fill(audioBar);
292: graphics.setComposite(AlphaComposite.getInstance(
293: AlphaComposite.SRC_OVER, 1.0f));
294: graphics.draw(audioBar);
295:
296: int textWidth = graphics.getFontMetrics().stringWidth(barInfo);
297: graphics
298: .drawString(
299: barInfo,
300: (int) ((audioBar.getWidth() / 2) - (textWidth / 2)),
301: (int) (audioBar.getY()
302: + (audioBar.getHeight() / 2) + (graphics
303: .getFont().getSize() / 2)));
304: }
305:
306: /**
307: * <p>
308: * Paints the background of the track panel.
309: * </p>
310: *
311: * @param graphics {@link Graphics2D} object to paint on
312: */
313: private void paintPanelBackground(Graphics2D graphics) {
314: GradientPaint gradient = new GradientPaint(0f, 0f, new Color(
315: 236, 236, 236), (float) this .getBounds().getWidth(),
316: 0f, Color.white);
317: graphics.setPaint(gradient);
318: graphics.fill(new Rectangle(0, 0, (int) getBounds().getWidth(),
319: (int) getBounds().getHeight()));
320: }
321:
322: /**
323: * <p>
324: * Called when a block has been selected. Gets the selected block and sets the slider to the
325: * startTime of the block
326: * </p>
327: *
328: * @param event {@link TrackSelectionEvent}
329: */
330: public void blockSelected(TrackSelectionEvent event) {
331: Validate.notNull(event);
332:
333: Block selectedBlock = event.getSelectedBlock();
334:
335: if (selectedBlock.getType().equals(BlockType.EVENT)) {
336: long startTime = this .trackModel
337: .getStartTime(selectedBlock);
338: this .parentFrame.setSliderTime(startTime);
339: repaint();
340: }
341:
342: checkPopupMenu(selectedBlock);
343: }
344:
345: /**
346: * <p>
347: * Called when a complete range has been selected.
348: * </p>
349: *
350: * <p>
351: * Functionality not implemented.
352: * </p>
353: *
354: * @param event {@link TrackSelectionEvent}
355: */
356: public void rangeSelected(TrackSelectionEvent event) {
357: Validate.notNull(event);
358: repaint();
359: }
360:
361: /**
362: * <p>
363: * Called when the selection has been removed. Repaints the panel with the updated {@link
364: * TrackModel}.
365: * </p>
366: *
367: * @param event {@link TrackSelectionEvent}
368: */
369: public void selectionRemoved(TrackSelectionEvent event) {
370: Validate.notNull(event);
371: repaint();
372: }
373:
374: /**
375: * <p>
376: * Sets the tooltip for the block the mouse is currently over depending on the {@link
377: * jacareto.track.block.BlockType}.
378: * </p>
379: *
380: * @param block {@link Block}
381: */
382: private void setBlockToolTip(Block block) {
383: if (block instanceof MediaBlock) {
384: this .setToolTipText(((MediaBlock) block).getToolTipText());
385: }
386: }
387:
388: /**
389: * <p>
390: * Sets the start time of the currently selected block to the value of the slider.
391: * </p>
392: */
393: public void setToTime() {
394: this .trackModel.setStartTime(viewPanelHelper.getMarkedBlock(),
395: parentFrame.getSliderTime());
396: viewPanelHelper.setModel(this .trackModel);
397: ((DefaultTrackModel) this .trackModel)
398: .fireBlockTimeChangedEvent(viewPanelHelper
399: .getMarkedBlock());
400: repaint();
401: }
402:
403: /**
404: * Replays the currently selected {@link Block}
405: */
406: public void playBlock() {
407: this .parentFrame.playBlock(viewPanelHelper.getMarkedBlock());
408: }
409:
410: /**
411: * Sets the end alignment of the currently selected block to the selected block in the {@link
412: * jacareto.struct.StructureTree}
413: */
414: public void setEndAlignment() {
415: this .trackModel.setEndAlignment(viewPanelHelper
416: .getMarkedBlock(), this .parentFrame
417: .getStructureTreeSelection());
418: this .popupMenu.setClearAlignmentMenuItemEnabled(true);
419: }
420:
421: /**
422: * Clears the alignment of the selected block
423: */
424: public void clearAlignment() {
425: this .trackModel.setEndAlignment(viewPanelHelper
426: .getMarkedBlock(), null);
427: this .popupMenu.setClearAlignmentMenuItemEnabled(false);
428: }
429:
430: /**
431: * Sets the trackModel this panel is working on
432: *
433: * @param trackModel TrackModel
434: */
435: public void setTrackModel(TrackModel trackModel) {
436: this .trackModel = trackModel;
437: this .viewPanelHelper.setModel(trackModel);
438: repaint();
439: }
440:
441: /**
442: * <p>
443: * Check, if popup menu should be shown.
444: * </p>
445: *
446: * @param block the selected {@link Block}
447: */
448: public void checkPopupMenu(Block block) {
449: if ((block.getType().equals(BlockType.EVENT))
450: && (block != null)) {
451: popupMenu.setAlignmentMenuItemEnabled(true);
452: } else {
453: popupMenu.setAlignmentMenuItemEnabled(false);
454: }
455:
456: if ((block != null)
457: && ((block.getType() == BlockType.AUDIO) || (block
458: .getType() == BlockType.VIDEO))) {
459: if (this .trackModel.isEndAligned(block)) {
460: popupMenu.setClearAlignmentMenuItemEnabled(true);
461: } else {
462: popupMenu.setClearAlignmentMenuItemEnabled(false);
463: }
464:
465: Block currentBlock = this .parentFrame
466: .getTrackSynchronizer().getSyncModel()
467: .getCurrentBlock(block.getType());
468:
469: if ((currentBlock != null) && (block.equals(currentBlock))) {
470: popupMenu.setPlayBlockMenuItemEnabled(false);
471: popupMenu.setStopBlockMenuItemEnabled(true);
472: } else {
473: popupMenu.setPlayBlockMenuItemEnabled(true);
474: popupMenu.setStopBlockMenuItemEnabled(false);
475: }
476: }
477: }
478:
479: /**
480: * <p>
481: * MouseMotionListener to check and repaint the panel if mouse is dragged or moved over a
482: * {@link Block}.
483: * </p>
484: *
485: * <p>
486: * This class is private for hiding the interface methods.
487: * </p>
488: *
489: * @author Oliver Specht
490: * @version $revision$
491: */
492: private class BlockMouseMotionListener extends MouseMotionAdapter {
493: //~ Methods --------------------------------------------------------------------------------
494:
495: /**
496: * Called when mouse is dragged. Stores the last position the mouse has been dragged to.
497: *
498: * @param event {@link MouseEvent}
499: */
500: public void mouseDragged(MouseEvent event) {
501: // store the last dragged point
502: Point draggedPoint = event.getPoint();
503:
504: // check, if block has been marked
505: Block block = viewPanelHelper.getMarkedBlock();
506:
507: if (block != null) {
508: long newStartTime = viewPanelHelper
509: .getTime(draggedPoint);
510: newStartTime -= viewPanelHelper
511: .getMousePointerTimeOffset();
512:
513: if (!viewPanelHelper.isOutOfTrackModelTimes(block,
514: newStartTime)) {
515: if (viewPanelHelper.isOverlapping(block,
516: newStartTime)) {
517: newStartTime = viewPanelHelper
518: .getNextPossibleStartTime(block,
519: newStartTime);
520: }
521:
522: trackModel.setStartTime(block, newStartTime);
523: repaint();
524: }
525: }
526: }
527:
528: /**
529: * Called when mouse is moved. Checks if mouse was moved into a block and marks the block
530: * in another color if so.
531: *
532: * @param event {@link MouseEvent}
533: */
534: public void mouseMoved(MouseEvent event) {
535: Block block = viewPanelHelper.getBlock(event.getPoint());
536:
537: if ((block != null)
538: && (viewPanelHelper.getMarkedBlock() != block)) {
539: setBlockToolTip(block);
540:
541: repaint();
542: }
543:
544: /*else if (viewPanelHelper.getMarkedBlock() != null){
545:
546: viewPanelHelper.unmarkBlock ();
547: setToolTipText (parentFrame.getSessionName ());
548: removeMouseListener (popupListener);
549: repaint ();
550: }*/
551: }
552: }
553:
554: /**
555: * Listener for the popup menu which will show up when mouse is over a block. This class is
556: * private for hiding the interface methods.
557: *
558: * @author Oliver Specht
559: * @version $revision$
560: */
561: private class PopupListener extends MouseAdapter {
562: //~ Methods --------------------------------------------------------------------------------
563:
564: public void mousePressed(MouseEvent event) {
565: maybeShowPopup(event);
566: }
567:
568: public void mouseReleased(MouseEvent event) {
569: maybeShowPopup(event);
570: }
571:
572: private void maybeShowPopup(MouseEvent event) {
573: if (event.isPopupTrigger()) {
574: rightClickPosition = event.getPoint();
575: popupMenu.show(event.getComponent(), event.getX(),
576: event.getY());
577: } else {
578: rightClickPosition = null;
579: }
580: }
581: }
582:
583: /**
584: * MouseListener to get click and released events to drag and drop the blocks. This class is
585: * private for hiding the interface methods.
586: *
587: * @author Oliver Specht
588: * @version $revision$
589: */
590: private class BlockMouseListener extends MouseAdapter {
591: //~ Methods --------------------------------------------------------------------------------
592:
593: /**
594: * Called when mouse is clicked.
595: *
596: * @param event {@link MouseEvent}
597: */
598: public void mouseClicked(MouseEvent event) {
599: Validate.notNull(event);
600: }
601:
602: /**
603: * Marks the {@link Block} the user has clicked on.
604: *
605: * @param event {@link MouseEvent}
606: */
607: private void selectTrackBar(MouseEvent event) {
608: Block block = viewPanelHelper.getClickedBlock(event
609: .getPoint());
610:
611: if (block != null) {
612: if (viewPanelHelper.getMarkedBlock() != block) {
613: viewPanelHelper.markBlock(block);
614: addMouseListener(popupListener);
615: checkPopupMenu(block);
616: repaint();
617: }
618:
619: viewPanelHelper.setMousePointerTimeOffset(event
620: .getPoint(), block);
621: } else {
622: viewPanelHelper.unmarkBlock();
623: removeMouseListener(popupListener);
624: repaint();
625: }
626: }
627:
628: /**
629: * Called when mouse has been pressed. Checks, if the mouse was pressed in a block and
630: * stores the block and position if true.
631: *
632: * @param event {@link MouseEvent}
633: */
634: public void mousePressed(MouseEvent event) {
635: Validate.notNull(event);
636:
637: if (!event.isPopupTrigger()) {
638: selectTrackBar(event);
639: }
640: }
641:
642: /**
643: * Called when the mouse has been released. If a block was dragged, sets the new start
644: * time for the dragged block. Additionaly checks for overlapping. If two blocks overlap,
645: * the dragged block is shifted left until it is fitting.
646: *
647: * @param event {@link MouseEvent}
648: */
649: public void mouseReleased(MouseEvent event) {
650: if (!event.isPopupTrigger()) {
651: if (viewPanelHelper.getMarkedBlock() != null) {
652: if (!viewPanelHelper.getRectangle(
653: viewPanelHelper.getMarkedBlock()).contains(
654: event.getPoint())) {
655: long newStartTime = viewPanelHelper
656: .getTime(event.getPoint());
657: newStartTime -= viewPanelHelper
658: .getMousePointerTimeOffset();
659:
660: if (!viewPanelHelper.isOutOfTrackModelTimes(
661: viewPanelHelper.getMarkedBlock(),
662: newStartTime)) {
663: if (viewPanelHelper.isOverlapping(
664: viewPanelHelper.getMarkedBlock(),
665: newStartTime)) {
666: newStartTime = viewPanelHelper
667: .getNextPossibleStartTime(
668: viewPanelHelper
669: .getMarkedBlock(),
670: newStartTime);
671: }
672:
673: trackModel.setStartTime(viewPanelHelper
674: .getMarkedBlock(), newStartTime);
675:
676: repaint();
677: }
678: }
679:
680: // only fire update event if mouse is released for performance reasons
681: // ((DefaultTrackModel) trackModel).fireBlockTimeChangedEvent (viewPanelHelper.getMarkedBlock ());
682: }
683: }
684: }
685: }
686: }
|