/*
* Jajuk
* Copyright (C) The Jajuk Team
* http://jajuk.info
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.jajuk.ui.thumbnails;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.event.HyperlinkListener;
import org.jajuk.base.Artist;
import org.jajuk.base.ArtistManager;
import org.jajuk.base.File;
import org.jajuk.base.Genre;
import org.jajuk.base.GenreManager;
import org.jajuk.base.Track;
import org.jajuk.base.TrackManager;
import org.jajuk.base.Year;
import org.jajuk.base.YearManager;
import org.jajuk.services.players.QueueModel;
import org.jajuk.util.Conf;
import org.jajuk.util.Const;
import org.jajuk.util.UtilFeatures;
import org.jajuk.util.UtilGUI;
import org.jajuk.util.log.Log;
/**
* HTML popup displayed over a thumbnail, it details album informations and
* tracks
* <p>
* It is displayed nicely from provided jlabel position
* </p>
*/
@SuppressWarnings("serial")
public class ThumbnailPopup extends JDialog {
private final JEditorPane text;
private KeyEventDispatcher dispatcher = null;
private boolean autoclose = false;
/**
* Launch selection and set right cursor.
*
* @param tracks
*/
private void launchLink(List<Track> tracks) {
List<org.jajuk.base.File> toPlay = new ArrayList<org.jajuk.base.File>(1);
for (Track track : tracks) {
File file = track.getBestFile(true);
if (file != null) {
toPlay.add(file);
}
}
text.setCursor(UtilGUI.WAIT_CURSOR);
QueueModel.push(
UtilFeatures.createStackItems(UtilFeatures.applyPlayOption(toPlay),
Conf.getBoolean(Const.CONF_STATE_REPEAT), true),
Conf.getBoolean(Const.CONF_OPTIONS_PUSH_ON_CLICK));
// Change icon cursor and wait a while so user can see it in case
// the PUSH_ON_CLICK option is set, otherwise, user may think
// nothing appended.
try {
Thread.sleep(250);
} catch (InterruptedException e1) {
Log.error(e1);
}
text.setCursor(UtilGUI.LINK_CURSOR);
}
/**
* The Constructor.
*
* @param description HTML text to display (HTML 3.0)
* @param origin :
* coordinates of the origin item on which we want to display the popup
* @param autoclose :
* whether the popup should close when mouse leave the origin item or
* is displayed as a regular Dialog
*/
public ThumbnailPopup(String description, Rectangle origin, boolean autoclose) {
this.autoclose = autoclose;
getRootPane().setOpaque(true);
text = new JEditorPane("text/html", description);
text.setEditable(false);
setHyperlinkHandling();
final JScrollPane jspText = new JScrollPane(text);
add(jspText);
setUndecorated(true);
if (autoclose) {
getRootPane().setWindowDecorationStyle(JRootPane.NONE);
setAlwaysOnTop(false);
addAutoCloseHandling();
} else {
setDefaultLookAndFeelDecorated(true);
setAlwaysOnTop(true);
}
setSizeAndLocation(origin);
pack();
setVisible(true);
setKeystrokes();
// Force scrollbar to stay on top (otherwise, it scrolls automatically to the bottom)
forceScrollbarToTop(jspText);
}
private void setHyperlinkHandling() {
text.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == EventType.ACTIVATED) {
URL url = e.getURL();
if (Const.XML_ARTIST.equals(url.getHost())) {
Artist artist = (Artist) ArtistManager.getInstance().getItemByID(url.getQuery());
List<Track> tracks = TrackManager.getInstance().getAssociatedTracks(artist, false);
Collections.shuffle(tracks);
launchLink(tracks);
} else if (Const.XML_GENRE.equals(url.getHost())) {
Genre genre = (Genre) GenreManager.getInstance().getItemByID(url.getQuery());
List<Track> tracks = TrackManager.getInstance().getAssociatedTracks(genre, false);
Collections.shuffle(tracks);
launchLink(tracks);
} else if (Const.XML_YEAR.equals(url.getHost())) {
Year year = (Year) YearManager.getInstance().getItemByID(url.getQuery());
List<Track> tracks = TrackManager.getInstance().getAssociatedTracks(year, false);
Collections.shuffle(tracks);
launchLink(tracks);
} else if (Const.XML_URL.equals(url.getHost())) {
try {
java.awt.Desktop.getDesktop().browse(new URI(url.getQuery()));
} catch (Exception e1) {
Log.error(e1);
}
} else if (Const.XML_TRACK.equals(url.getHost())) {
List<Track> tracks = new ArrayList<Track>(1);
Track track = (Track) TrackManager.getInstance().getItemByID(url.getQuery());
tracks.add(track);
launchLink(tracks);
}
}
// change cursor on entering or leaving
// hyperlinks
else if (e.getEventType() == EventType.ENTERED) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
text.setCursor(UtilGUI.LINK_CURSOR);
}
});
} else if (e.getEventType() == EventType.EXITED) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
text.setCursor(UtilGUI.DEFAULT_CURSOR);
}
});
}
}
});
}
/**
* @param jspText
*/
private void forceScrollbarToTop(final JScrollPane jspText) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
jspText.getVerticalScrollBar().setValue(0);
}
});
}
/**
* @param origin
*/
private void setSizeAndLocation(Rectangle origin) {
if (origin != null) {
setLocationRelativeToOrigin(origin);
} else {
UtilGUI.centerWindow(this);
}
}
/**
* @param autoclose
*/
private void addAutoCloseHandling() {
// Make sure to close this popup when it lost focus
getContentPane().addMouseListener(new MouseAdapter() {
@Override
public void mouseExited(MouseEvent e) {
// Test if mouse is really outside the popup, for unknown reason,
// this event is catch when entering the popup (Windows)
if (!text.contains(e.getPoint())) {
dispose();
}
}
});
}
/**
* @param origin
*/
private void setLocationRelativeToOrigin(Rectangle origin) {
// compute dialog position ( note that setRelativeTo
// is buggy and that we need more advanced positioning)
int x = (int) origin.getX() + (int) (0.6 * origin.getWidth());
// set position at 60 % of the picture
int y = (int) origin.getY() + (int) (0.6 * origin.getHeight());
int screenWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
int screenHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
// Adjust position if details are located outside
// the screen
// in x-axis
if ((x + getWidth()) > screenWidth) {
x = screenWidth - (getWidth() + 10);
}
if ((y + getHeight()) > screenHeight) {
x = (int) origin.getX() + (int) (0.6 * origin.getWidth());
if ((x + getWidth()) > screenWidth) {
x = screenWidth - (getWidth() + 10);
}
y = (int) origin.getY() + (int) (0.4 * origin.getHeight()) - (getHeight() - 50);
}
setLocation(x, y);
}
/* (non-Javadoc)
* @see java.awt.Window#dispose()
*/
@Override
public void dispose() {
removeKeystrokes();
super.dispose();
}
public void closeIfAutoclose() {
if (autoclose) {
dispose();
}
}
/**
* Add keystroke to dispose the popup when escape is pressed For unknown
* reasons, registerKeyboardAction() against this dialog has no effect (maybe because
* it doesn't get the focus)
*/
private void setKeystrokes() {
removeKeystrokes();
dispatcher = new KeyEventDispatcher() {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
dispose();
return false;
}
};
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(dispatcher);
}
/**
* Removes the keystrokes.
*
*/
private void removeKeystrokes() {
if (dispatcher != null) {
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(dispatcher);
dispatcher = null;
}
}
}