License text

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER 
 * Copyright  2008, 2010 Oracle and/or its affiliates.  All rights reserved. 
 * Use is subject to license terms.
 * 
 * This file is available and licensed under the following license:
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 
 *   * Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer. 
 *
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *   * Neither the name of Oracle Corporation nor the names of its contributors 
 *     may be used to endorse or promote products derived from this software 
 *     without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

    

package draggablemp3player;

import javafx.animation.*;
import javafx.stage.*;
import javafx.scene.input.*;
import javafx.scene.*;
import javafx.scene.shape.*;
import javafx.scene.paint.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.text.*;
import javafx.scene.media.*;
import javafx.scene.effect.*;

var stage:Stage;

var isApplet = "true".equals(FX.getArgument("isApplet") as String);
var inBrowser = isApplet;
var draggable = AppletStageExtension.appletDragSupported;
var dragTextVisible = bind inBrowser and draggable and dragRect.hover;

var homePage = "http://javafx.com/samples/DraggableMP3Player/index.html";
var baseURL = "http://sun.edgeboss.net/download/sun/javafx_music/";

var artist = "La Bonne Soupe";
var album = "lbs album";

var edmonton_cover = Image { url: "{__DIR__}covers/edmonton.jpg" backgroundLoading: true};
var forward_cover  = Image { url: "{__DIR__}covers/forward.png"  backgroundLoading: true};
var scottish_cover = Image { url: "{__DIR__}covers/scottish.jpg" backgroundLoading: true};
var literary_cover = Image { url: "{__DIR__}covers/literary.png" backgroundLoading: true};

var playlist:Playlist = Playlist {
    songs: [
        Song {
            filename: "09_oaf_king__again.mp3"
            name: "Oaf King, Again"
            album: "Literary Gentlemen"
            artist: artist
            baseURL: baseURL
            cover: literary_cover
        },
        Song {
            filename: "10_fool_conditioning_our_grannie_liu.mp3"
            name: "Fool Conditioning (Our Grannie Liu)"
            album: "Literary Gentlemen"
            artist: artist
            baseURL: baseURL
            cover: literary_cover
        },
        Song {
            filename: "13_delusion.mp3"
            name: "Delusion"
            album: "Literary Gentlemen"
            artist: artist
            baseURL: baseURL
            cover: literary_cover
        },
        Song {
            filename: "01_nana__girl_from_hungary.mp3"
            name: "Nana, Girl From Hungary"
            album: "Scottish Insufficiency"
            artist: artist
            baseURL: baseURL
            cover: scottish_cover
        },
        Song {
            filename: "04_im_cold_and_i_want_to_go_home.mp3"
            name: "I'm Cold And I Want To Go Home"
            album: "Scottish Insufficiency"
            artist: artist
            baseURL: baseURL
            cover: scottish_cover
        },
        Song {
            filename: "01_good_day_things_can_change.mp3"
            name: "Good Day Things Can Change"
            album: "Edmonton Tussle"
            artist: artist
            baseURL: baseURL
            cover: edmonton_cover
        },
        Song {
            filename: "02_i_guess__amen.mp3"
            name: "I Guess, Amen"
            album: "Edmonton Tussle"
            artist: artist
            baseURL: baseURL
            cover: edmonton_cover
        },
        Song {
            filename: "04_flash_flood_participant.mp3"
            name: "Flash Flood Participant"
            album: "Forward To The Forward People"
            artist: artist
            baseURL: baseURL
            cover: forward_cover
        },
        Song {
            filename: "09_ellowen_deeowen.mp3"
            name: "Ellowen Deeowen"
            album: "Forward To The Forward People"
            artist: artist
            baseURL: baseURL
            cover: forward_cover
        }
    ]
};

function stopCurrentSong():Void {
    mediaPlayer.stop();
    mediaPlayer.media = null;
    if (playlist.currentPlayingSong != null) {
        playlist.currentPlayingSong.closeMedia();
    }
}

function playCurrentSong():Void {
    playlist.currentPlayingSong = playlist.songs[playlist.currentSong];
    mediaPlayer.media = playlist.currentPlayingSong.getMedia();
    mediaPlayer.play();
}

FX.addShutdownAction(function() { stopCurrentSong(); });

var mediaPlayer:MediaPlayer = MediaPlayer {
    volume: 0.5
    autoPlay: false
    onError: function(e:MediaError) {
        println("got a MediaPlayer error : {e.cause} {e}");
        stopCurrentSong();
    }
    onEndOfMedia: function() {
        println("reached end of media");
        stopCurrentSong();
        playlist.currentSong++;
        playCurrentSong();
    }
};

var playlist_bg = ImageView {
    id: "playlist_bg"
    opacity: 1.0
    visible: true
    translateX: 198
    translateY: 120
    image: Image { url: "{__DIR__}images/MP3_handoff_playlist.png" }
};

var song_info_current_song:Song = null;
var song_info_popup:Group = Group {
    visible: false
    translateX: 250
    translateY: 40
    blocksMouse: true
    content: [
        Rectangle {
            width: 220
            height: 290
            fill: Color.BLACK
            arcWidth: 20
            arcHeight: 20
            stroke: Color.rgb(50,50,50)
        },
        ImageView {
            x: 5
            y: 5
            image:Image { url: "{__DIR__}images/MP3_handoff_drag_close.png" }
            onMousePressed: function(e) {
                song_info_popup.visible = false;
            }
        },
        ImageView {
            x: 10
            y: 46
            image: bind song_info_current_song.cover
        },
        Text {
            x: 10
            y: 40
            content: bind song_info_current_song.artist
            fill: Color.WHITE
        },
        Text {
            x: 10
            y: 260
            content: bind song_info_current_song.album
            fill: Color.WHITE
        },
        Text {
            x: 10
            y: 275
            content: bind song_info_current_song.name
            fill: Color.WHITE
        },
    ]
}

var playlist_text = Group {
    content: for(song in playlist.songs) {
        var i_image = ImageView {
            x: 357
            image:Image { url: "{__DIR__}images/MP3_handoff_i.png" }
        };
        var i_overlay_image = ImageView {
            x: 353
            y:-2
            opacity: 0.0
            image:Image { url: "{__DIR__}images/MP3_handoff_Group_1_copy_2.png" }
            onMouseClicked:function(e) {
                song_info_current_song = song;
                song_info_popup.visible = true;
            }
        };
        RolloverBehavior { target: i_overlay_image };

        Group {
            translateX: 210
            translateY: 15 * indexof song + 190
            content: [
                ImageView {
                    x: 0
                    visible: bind (playlist.currentSong == indexof song)
                    image: Image { url: "{__DIR__}images/MP3_handoff_playlist_volume.png" }
                },
                Text {
                    x: 20
                    y: 8
                    fill: Color.WHITE
                    content: "{1 + indexof song}  {song.album} - {song.name}"
                    font:Font {
                        size: 11
                        name: "Arial"
                    }
                },
                i_image,
                i_overlay_image,

            ]
        }
    }
};

var playlist_innerGroup = Group {
    translateY: -230
    content: [
        playlist_bg,
        playlist_text
    ]
};
var playlist_group = Group {
    content: playlist_innerGroup
    clip:Rectangle {
        x: 200
        y: 100
        width: 400
        height: 300
    }
};

var playlist_reveal = Timeline {
    rate: -1.0
  def temp = playlist_innerGroup;
    keyFrames: [
        at (0s)   { temp.translateY => -230 },
        at (0.4s) { temp.translateY => 0 }
    ]
};


// the body of the player
var player_normal = ImageView {
    id: "player_normal"
    opacity: 1.0
    visible: true
    x: 83
    y: 29
    image:Image { url: "{__DIR__}images/MP3_handoff_player_normal.png" },
};




// the various button rollovers
var fx_rollover:ImageView = ImageView {
    id: "fx_rollover"
    opacity: 0.0
    visible: true
    x: 196
    y: 91
    image:Image { url: "{__DIR__}images/MP3_handoff_fx_rollover.png" },
    onMouseClicked:function(e) {
        println("going to the fx homepage");
        AppletStageExtension.showDocument(homePage);
    }
};
RolloverBehavior { target: fx_rollover };

var playlist_rollover:ImageView = ImageView {
    id: "playlist_rollover"
    opacity: 0.0
    visible: true
    x: 115
    y: 157
    blocksMouse:true
    image: Image { url: "{__DIR__}images/MP3_handoff_playlist_rollover.png" },
    onMousePressed:function(e) {
        playlist_reveal.rate *= -1.0;
        playlist_reveal.play();
    }
};
RolloverBehavior { target: playlist_rollover };


var playBack = ImageView {
    id: "playBack"
    opacity: 0.0
    visible: true
    x: 99
    y: 104
    image:Image { url: "{__DIR__}images/MP3_handoff_playBack.png" },
    onMousePressed:function(e) {
        var paused = mediaPlayer.paused;
        if (not paused) {
            stopCurrentSong();
            playlist.currentSong--;
            playCurrentSong();
        } else {
            playlist.currentSong--;
        }
    }
};
RolloverBehavior { target: playBack };

var playForward = ImageView {
    id: "playFoward"
    opacity: 0.0
    visible: true
    x: 165
    y: 107
    image:Image { url: "{__DIR__}images/MP3_handoff_playFoward.png" },
    onMousePressed:function(e) {
        var paused = mediaPlayer.paused;
        if (not paused) {
            stopCurrentSong();
            playlist.currentSong++;
            playCurrentSong();
        } else {
            playlist.currentSong++;
        }
    }
};
RolloverBehavior { target: playForward };


var stop = ImageView {
    id: "stop"
    opacity: 0.0
    visible: true
    x: 116
    y: 88
    image:Image { url: "{__DIR__}images/MP3_handoff_stop.png" },
    onMousePressed:function(e) {
        mediaPlayer.stop();
    }
};
RolloverBehavior { target: stop };


var play_normal_image = Image { url: "{__DIR__}images/play.png" };
var play_rollover_image = Image { url: "{__DIR__}images/play_selected.png" };
var pause_normal_image = Image { url: "{__DIR__}images/pause.png" };
var pause_rollover_image = Image { url: "{__DIR__}images/pause_selected.png" };
var play_rollover:ImageView = ImageView {
    id: "play_rollover"
    opacity: 1.0
    visible: true
    x: 122
    y: 114
    image: bind if (not mediaPlayer.paused) {
        if (play_rollover.hover) { pause_rollover_image } else { pause_normal_image }
    } else {
        if (play_rollover.hover) { play_rollover_image } else { play_normal_image }
    }
    onMousePressed: function(e) {
        if (playlist.currentPlayingSong != playlist.songs[playlist.currentSong]) {
            stopCurrentSong();
            playCurrentSong();
        } else {
            if (mediaPlayer.paused) {
                mediaPlayer.play();
            } else {
                mediaPlayer.pause();
            }
        }
    }
};

var drag_opacity = 0.0;
var drag_close_rollover = Image { url: "{__DIR__}images/close_rollover.png" };
var drag_close_normal = Image { url: "{__DIR__}images/close.png" };
var drag_out_rollover = Image { url: "{__DIR__}images/dragOut_rollover.png" };
var drag_out_normal = Image { url: "{__DIR__}images/dragOut.png" };

var drag_closers = Group {
    opacity: bind drag_opacity;
    content: [
        ImageView {
            x: 541
            y: 44
            image: drag_close_normal
            visible: bind not inBrowser
        },
        ImageView {
            x: 541
            y: 44
            image: drag_out_normal
            visible: bind inBrowser and draggable
        },
        Rectangle {
                x: 541
                y: 44
                width: 10
                height: 10
                fill: Color.TRANSPARENT
                onMouseClicked: function(e:MouseEvent):Void { stage.close(); }
        }
    ]
    };

var dragAreaFader = Timeline {
    keyFrames: [
        at (0s)   { drag_opacity => 0.0 },
        at (0.2s) { drag_opacity => 1.0 },
    ]
};

var dragRect:Rectangle = Rectangle {
    x: 155
    y: 30
    width: 420
    height: 40
    fill: Color.TRANSPARENT
    onMouseEntered:function(e) {
        dragAreaFader.rate = 1.0;
        dragAreaFader.play();
    }
    onMouseExited: function(e) {
        dragAreaFader.rate = -1.0;
        dragAreaFader.play();
    }
};

var stageDragInitialX:Number;
var stageDragInitialY:Number;

var can_drag_me: ImageView = ImageView {
    id: "can_drag_me"
    opacity: bind drag_opacity
    visible: true
    x: 83
    y: 29
    image: Image { url: "{__DIR__}images/MP3_handoff_can_drag_me.png" },

    onMousePressed: function(e) {
        stageDragInitialX = e.screenX - stage.x;
        stageDragInitialY = e.screenY - stage.y;
    }
    onMouseDragged: function(e) {
        stage.x = e.screenX - stageDragInitialX;
        stage.y = e.screenY - stageDragInitialY;
    }
};

var can_drag_me_text:Text = Text {
        content: "You can drag me out of the browser"
        fill: Color.WHITE
        font:Font {
            size: 12
            embolden: true
            name: "Arial"
        }
    effect:DropShadow { 
    offsetX: 0
    offsetY: 1
    color: Color.BLACK
    spread: 0.5
    radius: 14 }
    opacity: bind drag_opacity
    visible: bind dragTextVisible
    y: 60
    x: 165
};



// the time control slider
var time_control_length = 240.0;
var time_control_xoff = 210;
var currentTime = bind
    if (mediaPlayer.media != null and
        mediaPlayer.media.duration != null and
        mediaPlayer.media.duration.toMillis() > 0 and
        mediaPlayer.currentTime.toMillis() > 0)
    {
        time_control_length * mediaPlayer.currentTime.toMillis() / mediaPlayer.media.duration.toMillis()
    } else {
        0.0
    };
var bufferedTime = bind
    if (mediaPlayer.media != null and
        mediaPlayer.media.duration != null and
        mediaPlayer.media.duration.toMillis() > 0 and
        mediaPlayer.bufferProgressTime.toMillis() > 0)
    {
        time_control_length * mediaPlayer.bufferProgressTime.toMillis() / mediaPlayer.media.duration.toMillis()
    } else {
        0.0
    };
var time_control:ImageView = ImageView {
    id: "time_control"
    cursor: Cursor.HAND
    opacity: 1.0 visible: true
    x: bind currentTime + time_control_xoff  y: 143
    image: Image { url: "{__DIR__}images/MP3_handoff_time_control.png" },
    blocksMouse: true
    onMouseDragged: function(e) {
        if (mediaPlayer.media != null and mediaPlayer.media.duration > 0s) {
            var x = e.x;
            var available = mediaPlayer.bufferProgressTime.toMillis() / mediaPlayer.media.duration.toMillis();
            var xmin = time_control_xoff;
            var xmax = xmin + (available*time_control_length);
            if (x > xmin and x < xmax) {
                var t = (x - xmin) / time_control_length * mediaPlayer.media.duration.toMillis();
                mediaPlayer.currentTime = Duration.valueOf(t);
            }
        }
    }
};

var current_time_indicator = ImageView {
    id: "current_time_indicator"
    opacity: 1.0
    visible: true
    translateX: 214
    translateY: 154
    image:Image { url: "{__DIR__}images/MP3_handoff_blue_play_fill.png" }
    clip:Rectangle {
        x: 0
        y: 0
        width: bind currentTime
        height: 20
        }
};

var buffering_indicator = Rectangle {
    fill:LinearGradient {
        startX: 0
        startY: 0
        endX: 1
        endY: 0
        stops: [
            Stop {
                offset: 0.9
                color: Color.rgb(255, 255, 255, 0.1)
            },
            Stop {
                offset: 1.0
                color: Color.TRANSPARENT
            }
        ]
    }
    translateX: 216
    translateY: 157
    width: bind bufferedTime
    height: 2
};

function getTimeString(dur:Duration):String {
    if (dur == null or dur < 0s) {
        return "0:00";
    } else {
        var minutes = dur.toMinutes() as Integer;
        var seconds = dur.toSeconds() mod 60 as Integer;
        return "{minutes}:{%02d seconds}";
    }
}

var current_time = Text {
    fill: Color.web("#0d9c0d")
    x: 465 y: 160 font: Font { size: 9 }
    content: bind getTimeString(mediaPlayer.currentTime)
};
var total_time = Text {
    fill: Color.web("#c5c5c5")
    x: 490 
    y: 160
    font:Font { size: 9 }
    content: bind "/ {getTimeString(mediaPlayer.media.duration)}"
};



    
var volume_icon = ImageView {
    id: "volume_icon"
    opacity: 1.0
    x: 542
    y: 150
    image: Image {
        url: "{__DIR__}images/MP3_handoff_volume_icon.png"
    },
};

var volume_group:Group = Group {
    translateY: 110
    content: [
        ImageView { image: Image { url: "{__DIR__}images/MP3_handoff_bkg.png" } 
            blocksMouse: true
            onMouseDragged:function(e) {
                var v = (e.y-14)/65;
                if(v < 0) { v = 0; }
                if(v > 1.0) { v = 1.0; }
                v = 1-v;
                mediaPlayer.volume = v;
            }
        },
        Rectangle {
            x: 22
            y: 16
            width: 12
            height: 68
            fill: bind LinearGradient {
                startX:0
                endX: 0
                startY: 0
                endY: 1
                proportional: true
                stops: [
                    Stop { color: Color.BLACK offset: 0.0 },
                    Stop { color: Color.GREEN offset: 0.99-(mediaPlayer.volume*0.98) },
                    Stop { color: Color.GREEN offset: 1.0 },
                ]
            }
        },
        ImageView {
            x: 21
            y: 15
            image:Image { url: "{__DIR__}images/MP3_handoff_outline_inner_shadow.png" }
        },
        ImageView {
            image:Image { url: "{__DIR__}images/MP3_handoff_slider.png" }
            x: 13
            y: bind (1-mediaPlayer.volume) * 65 + 14
        },
    ]
};
var volume_group_wrapper = Group { 
    blocksMouse: true
    translateX: 523
    translateY: 30
    content: volume_group
    clip:Rectangle {
        width: 100
        height: 130
    }
};

var reveal_volume = Timeline {
    def temp = volume_group;
    keyFrames: [ 
        at (0s)   { temp.translateY => 110 },
        at (0.3s) { temp.translateY => 0 },
    ]
}

var volume_rollover = ImageView {
    id: "volume_rollover"
    opacity: 0.0
    x: 542
    y: 150
    blocksMouse: true
    image:Image {url: "{__DIR__}images/MP3_handoff_volume_rollover.png"
    },
    effect: Glow { }
    onMousePressed: function(e) {
        reveal_volume.rate *= -1.0;
        reveal_volume.play();
    }
};
RolloverBehavior { target: volume_rollover };

function createGradient(color:Color) {
    return LinearGradient {
        proportional: false
        startX: 0 startY: 0 endX: 275 endY: 0
        stops: [
            Stop { offset: 0.0 color: color },
            Stop { offset: 0.8 color: color },
            Stop { offset: 1.0 color: Color.TRANSPARENT }
        ]
    }
}

var songInfo = Group {
    translateX: 300
    translateY: 100
    content: [
        Text {
            content: bind "{playlist.songs[playlist.currentSong].name}"
            x: 0 
            y: 0
            fill: createGradient(Color.WHITE);
        },
        Text {
            content: bind "{playlist.songs[playlist.currentSong].artist}"
                       " - {playlist.songs[playlist.currentSong].album}"
            x: 0 
            y: 18
            fill: createGradient(Color.color(0.7, 0.7, 0.7));
        }
    ]
};



stage = Stage {
    title: "MP3 Player"
    visible: true
    width: 510
    height: 345
    style:StageStyle.TRANSPARENT
    scene: Scene {
        fill: bind if (inBrowser) Color.WHITE else Color.TRANSPARENT
        content: Group {
            translateX: -75
            translateY: -25
            content: [
                can_drag_me,
    
                playlist_group,
                player_normal,                  // the player itself

                // the rollovers
                fx_rollover,
                playlist_rollover,
                playBack,
                playForward,
                stop,
                play_rollover,

                // the time control and labels
                buffering_indicator,
                current_time_indicator,
                time_control,
                current_time,
                total_time,

                // drag controls
                dragRect,
                can_drag_me_text,
                drag_closers,

                //volume control,
                volume_group_wrapper,
                ImageView { image: Image { url: "{__DIR__}images/MP3_handoff_volume_bkg.png" } x: 491 y: 130 },
                volume_icon,
                volume_rollover,
                songInfo,                       // text on the player itself
                song_info_popup,                // the song info popup
            ]
        }
    }
    extensions: [
        AppletStageExtension {
            shouldDragStart: function(e): Boolean {
                return inBrowser and e.primaryButtonDown and dragRect.hover;
            }
            onDragStarted: function(): Void {
                inBrowser = false;
            }
            onAppletRestored: function(): Void {
                inBrowser = true;
            }
            useDefaultClose: false
        }
    ]
}