org.jajuk.ui.views.SuggestionView.java Source code

Java tutorial

Introduction

Here is the source code for org.jajuk.ui.views.SuggestionView.java

Source

/*
 *  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.views;

import ext.FlowScrollPanel;
import ext.services.lastfm.AlbumInfo;
import ext.services.lastfm.AlbumListInfo;
import ext.services.lastfm.ArtistInfo;
import ext.services.lastfm.LastFmService;
import ext.services.lastfm.SimilarArtistsInfo;

import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.BoxLayout;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import net.miginfocom.swing.MigLayout;

import org.apache.commons.lang.StringUtils;
import org.jajuk.base.Album;
import org.jajuk.base.AlbumManager;
import org.jajuk.base.File;
import org.jajuk.events.JajukEvent;
import org.jajuk.events.JajukEvents;
import org.jajuk.events.ObservationManager;
import org.jajuk.services.players.QueueModel;
import org.jajuk.ui.perspectives.PerspectiveManager;
import org.jajuk.ui.thumbnails.AbstractThumbnail;
import org.jajuk.ui.thumbnails.LastFmAlbumThumbnail;
import org.jajuk.ui.thumbnails.LastFmArtistThumbnail;
import org.jajuk.ui.thumbnails.LocalAlbumThumbnail;
import org.jajuk.ui.thumbnails.ThumbnailManager;
import org.jajuk.util.Conf;
import org.jajuk.util.Const;
import org.jajuk.util.DownloadManager;
import org.jajuk.util.Messages;
import org.jajuk.util.UtilGUI;
import org.jajuk.util.log.Log;
import org.jdesktop.swingx.JXBusyLabel;

/**
 * Show suggested albums based on current collection (bestof, novelties) and
 * LAstFM.
 */
@SuppressWarnings("serial")
public class SuggestionView extends ViewAdapter {
    private JTabbedPane tabs;
    protected String artist;

    //Remove tab border, see
    // http://forum.java.sun.com/thread.jspa?threadID=260746&messageID=980405
    class MyTabbedPaneUI extends javax.swing.plaf.basic.BasicTabbedPaneUI {
        @Override
        protected Insets getContentBorderInsets(int tabPlacement) {
            return new Insets(0, 0, 0, 0);
        }

        @Override
        protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
            // nothing to do here...
        }
    }

    enum SuggestionType {
        BEST_OF, NEWEST, RARE, OTHERS_ALBUMS, SIMILAR_ARTISTS
    }

    JScrollPane jpBestof;
    JScrollPane jpNewest;
    JScrollPane jpRare;
    JScrollPane jpOthersAlbums;
    JScrollPane jpSimilarArtists;
    private int comp = 0;
    List<Album> albumsNewest;
    List<Album> albumsPrefered;
    List<Album> albumsRare;
    /** Currently selected thumb. */
    AbstractThumbnail selectedThumb;
    private AlbumListInfo albums;
    private SimilarArtistsInfo similar;
    JXBusyLabel busyLocal1 = new JXBusyLabel();
    JXBusyLabel busyLocal2 = new JXBusyLabel();
    JXBusyLabel busyLocal3 = new JXBusyLabel();
    JXBusyLabel busyLastFM1 = new JXBusyLabel();
    JXBusyLabel busyLastFM2 = new JXBusyLabel();

    private class ThumbMouseListener extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            AbstractThumbnail thumb = (AbstractThumbnail) ((JLabel) e.getSource()).getParent();
            // remove red border on previous item if
            // different from this one
            if (selectedThumb != null && selectedThumb != thumb) {
                selectedThumb.setSelected(false);
            }
            // select the new selected thumb
            thumb.setSelected(true);
            selectedThumb = thumb;
        }
    }

    public SuggestionView() {
        super();
    }

    @Override
    public String getDesc() {
        return Messages.getString("SuggestionView.0");
    }

    @Override
    public void initUI() {
        tabs = new JTabbedPane();
        // Now use the new TabbedPaneUI
        tabs.setUI(new MyTabbedPaneUI());
        // Fill tabs with empty tabs
        tabs.addTab(Messages.getString("SuggestionView.1"),
                UtilGUI.getCentredPanel(new JLabel(Messages.getString("WikipediaView.3"))));
        tabs.addTab(Messages.getString("SuggestionView.2"),
                UtilGUI.getCentredPanel(new JLabel(Messages.getString("WikipediaView.3"))));
        tabs.addTab(Messages.getString("SuggestionView.5"),
                UtilGUI.getCentredPanel(new JLabel(Messages.getString("WikipediaView.3"))));
        tabs.addTab(Messages.getString("SuggestionView.3"), new JLabel(Messages.getString("SuggestionView.7")));
        tabs.addTab(Messages.getString("SuggestionView.4"), new JLabel(Messages.getString("SuggestionView.7")));
        addTabChangeListener();
        selectTabFromConf();
        refreshLocalCollectionTabs();
        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        add(tabs);
        ObservationManager.register(this);
    }

    private void selectTabFromConf() {
        if (Conf.containsProperty(
                getClass().getName() + "_" + ((getPerspective() == null) ? "solo" : getPerspective().getID()))) {
            int index = Conf.getInt(
                    getClass().getName() + "_" + ((getPerspective() == null) ? "solo" : getPerspective().getID()));
            if (index > 0 && index < tabs.getTabCount()) {
                tabs.setSelectedIndex(index);
            }
        }
    }

    private void addTabChangeListener() {
        // Refresh tabs on demand only, add changeListerner after tab creation to
        // avoid the stored tab to be overwriten at startup
        tabs.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent arg0) {
                refreshLastFMCollectionTabs();
                // store the selected tab
                Conf.setProperty(
                        getClass().getName() + "_"
                                + ((getPerspective() == null) ? "solo" : getPerspective().getID()),
                        Integer.toString(tabs.getSelectedIndex()).toString());
            }
        });
    }

    @Override
    public Set<JajukEvents> getRegistrationKeys() {
        Set<JajukEvents> eventSubjectSet = new HashSet<JajukEvents>();
        eventSubjectSet.add(JajukEvents.FILE_LAUNCHED);
        eventSubjectSet.add(JajukEvents.PARAMETERS_CHANGE);
        eventSubjectSet.add(JajukEvents.COVER_DEFAULT_CHANGED);
        eventSubjectSet.add(JajukEvents.SUGGESTIONS_REFRESH);
        return eventSubjectSet;
    }

    /**
     * Refresh local thumbs.
     */
    private void refreshLocalCollectionTabs() {
        // Display a busy panel in the mean-time
        // For some reasons, if we put that code into an invokeLater() call
        // it is executed after the next done() in next swing worker, no clue why
        // As a compromise, we only show busy label when called in EDT (not the case when the 
        // call is from an update() )
        if (SwingUtilities.isEventDispatchThread()) {
            busyLocal1.setBusy(true);
            busyLocal2.setBusy(true);
            busyLocal3.setBusy(true);
            // stop all existing busy labels before we add the new ones...
            //stopAllBusyLabels();
            tabs.setComponentAt(0, UtilGUI.getCentredPanel(busyLocal1));
            tabs.setComponentAt(1, UtilGUI.getCentredPanel(busyLocal2));
            tabs.setComponentAt(2, UtilGUI.getCentredPanel(busyLocal3));
        }
        SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
            JScrollPane jsp1;
            JScrollPane jsp2;
            JScrollPane jsp3;

            @Override
            public Void doInBackground() {
                albumsPrefered = AlbumManager.getInstance()
                        .getBestOfAlbums(Conf.getBoolean(Const.CONF_OPTIONS_HIDE_UNMOUNTED), NB_BESTOF_ALBUMS);
                albumsNewest = AlbumManager.getInstance()
                        .getNewestAlbums(Conf.getBoolean(Const.CONF_OPTIONS_HIDE_UNMOUNTED), NB_BESTOF_ALBUMS);
                albumsRare = AlbumManager.getInstance().getRarelyListenAlbums(
                        Conf.getBoolean(Const.CONF_OPTIONS_HIDE_UNMOUNTED), NB_BESTOF_ALBUMS);
                refreshThumbsForLocalAlbums();
                return null;
            }

            private void refreshThumbsForLocalAlbums() {
                // Refresh thumbs for required albums
                List<Album> albums = new ArrayList<Album>(10);
                albums.addAll(albumsPrefered);
                albums.addAll(albumsNewest);
                albums.addAll(albumsRare);
                if (albums.size() > 0) {
                    for (Album album : albums) {
                        // Try creating the thumbnail
                        ThumbnailManager.refreshThumbnail(album, 100);
                    }
                }
            }

            @Override
            public void done() {
                jsp1 = getLocalSuggestionsPanel(SuggestionType.BEST_OF);
                jsp2 = getLocalSuggestionsPanel(SuggestionType.NEWEST);
                jsp3 = getLocalSuggestionsPanel(SuggestionType.RARE);
                busyLocal1.setBusy(false);
                busyLocal2.setBusy(false);
                busyLocal3.setBusy(false);
                tabs.setComponentAt(0, jsp1);
                tabs.setComponentAt(1, jsp2);
                tabs.setComponentAt(2, jsp3);
            }
        };
        sw.execute();
    }

    /**
     * Refresh last fm collection tabs.
     * 
     */
    private void refreshLastFMCollectionTabs() {
        String newArtist = null;
        File current = QueueModel.getPlayingFile();
        if (current != null) {
            newArtist = current.getTrack().getArtist().getName2();
        }
        // if none track playing
        if (current == null
                // Last.FM infos is disable
                || !Conf.getBoolean(Const.CONF_LASTFM_INFO)
                // None internet access option is set
                || Conf.getBoolean(Const.CONF_NETWORK_NONE_INTERNET_ACCESS)
                // If unknown artist
                || (newArtist == null || newArtist.equals(Messages.getString(UNKNOWN_ARTIST)))) {
            // Set empty panels
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    tabs.setComponentAt(3, new JLabel(Messages.getString("SuggestionView.7")));
                    tabs.setComponentAt(4, new JLabel(Messages.getString("SuggestionView.7")));
                }
            });
            return;
        }
        // Check if artist changed, otherwise, just leave
        if (newArtist.equals(this.artist)) {
            return;
        }
        // Save current artist
        artist = newArtist;
        // Display a busy panel in the mean-time
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                busyLastFM1.setBusy(true);
                busyLastFM2.setBusy(true);
                tabs.setComponentAt(3, UtilGUI.getCentredPanel(busyLastFM1));
                tabs.setComponentAt(4, UtilGUI.getCentredPanel(busyLastFM2));
            }
        });
        // Use a swing worker as construct takes a lot of time
        SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
            JScrollPane jsp1;
            JScrollPane jsp2;

            @Override
            public Void doInBackground() {
                try {
                    // Fetch last.fm calls and downloads covers
                    preFetchOthersAlbum();
                    preFetchSimilarArtists();
                } catch (Exception e) {
                    Log.error(e);
                }
                return null;
            }

            @Override
            public void done() {
                jsp1 = getLastFMSuggestionsPanel(SuggestionType.OTHERS_ALBUMS, false);
                jsp2 = getLastFMSuggestionsPanel(SuggestionType.SIMILAR_ARTISTS, false);
                busyLastFM1.setBusy(false);
                busyLastFM2.setBusy(false);
                tabs.setComponentAt(3, jsp1);
                tabs.setComponentAt(4, jsp2);
            }
        };
        sw.execute();
    }

    /**
     * Pre-load other album (done outside the EDT).
        
     *
     * @throws Exception the exception
     */
    void preFetchOthersAlbum() throws Exception {
        albums = LastFmService.getInstance().getAlbumList(artist, true, 0);
        // Perform images downloads and caching
        if (albums != null && albums.getAlbums().size() > 0) {
            for (AlbumInfo album : albums.getAlbums()) {
                // stop this list of albums if there was another file launched in the meantime 
                String albumUrl = album.getBigCoverURL();
                if (StringUtils.isBlank(albumUrl)) {
                    continue;
                }
                // Download thumb
                URL remote = new URL(albumUrl);
                // Download image and store file reference (to generate the
                // popup thumb for ie)
                DownloadManager.downloadToCache(remote);
            }
        }
    }

    /**
     * Pre-load other album (done outside the EDT).
       *
     * @throws Exception the exception
     */
    void preFetchSimilarArtists() throws Exception {
        // Perform last.fm calls
        similar = LastFmService.getInstance().getSimilarArtists(artist);
        // artists is null for void (unknown) similar artists
        if (similar != null && similar.getArtists() != null) {
            List<ArtistInfo> artists = similar.getArtists();
            for (ArtistInfo similarArtist : artists) {
                // stop this list of albums if there was another file launched in the meantime, another refresh will take place anyway 
                String artistUrl = similarArtist.getImageUrl();
                if (StringUtils.isBlank(artistUrl)) {
                    continue;
                }
                // Download thumb
                URL remote = new URL(artistUrl);
                // Download the picture and store file reference (to
                // generate the popup thumb for ie)
                DownloadManager.downloadToCache(remote);
            }
        }
    }

    /**
     * Return the result panel for local albums.
     *
     * @param type 
     *
     * @return the local suggestions panel
     */
    JScrollPane getLocalSuggestionsPanel(SuggestionType type) {
        FlowScrollPanel out = new FlowScrollPanel();
        out.setLayout(new FlowLayout(FlowLayout.LEFT));
        JScrollPane jsp = new JScrollPane(out, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        jsp.setBorder(null);
        out.setScroller(jsp);
        List<Album> albums = null;
        if (type == SuggestionType.BEST_OF) {
            albums = albumsPrefered;
        } else if (type == SuggestionType.NEWEST) {
            albums = albumsNewest;
        } else if (type == SuggestionType.RARE) {
            albums = albumsRare;
        }
        if (albums != null && albums.size() > 0) {
            for (Album album : albums) {
                LocalAlbumThumbnail thumb = new LocalAlbumThumbnail(album, 100, false);
                thumb.populate();
                thumb.getIcon().addMouseListener(new ThumbMouseListener());
                out.add(thumb);
            }
        } else {
            out.add(UtilGUI.getCentredPanel(new JLabel(Messages.getString("WikipediaView.3"))));
        }
        return jsp;
    }

    /**
     * Return the result panel for lastFM information.
     *
     * @param type 
     * @param artistView 
     *
     * @return the last fm suggestions panel
     */
    JScrollPane getLastFMSuggestionsPanel(SuggestionType type, boolean artistView) {
        FlowScrollPanel flowPanel = new FlowScrollPanel();
        JScrollPane jsp = new JScrollPane(flowPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        jsp.setBorder(null);
        flowPanel.setScroller(jsp);
        flowPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
        if (type == SuggestionType.OTHERS_ALBUMS) {
            if (albums != null && albums.getAlbums().size() > 0) {
                for (AlbumInfo album : albums.getAlbums()) {
                    AbstractThumbnail thumb = new LastFmAlbumThumbnail(album);
                    thumb.setArtistView(artistView);
                    thumb.populate();
                    if (thumb.getIcon() != null) {
                        thumb.getIcon().addMouseListener(new ThumbMouseListener());
                        flowPanel.add(thumb);
                    }
                }
            }
            // No result found
            else {
                return new JScrollPane(getNothingFoundPanel());
            }
        } else if (type == SuggestionType.SIMILAR_ARTISTS) {
            if (similar != null) {
                List<ArtistInfo> artists = similar.getArtists();
                for (ArtistInfo similarArtist : artists) {
                    AbstractThumbnail thumb = new LastFmArtistThumbnail(similarArtist);
                    thumb.setArtistView(artistView);
                    thumb.populate();
                    if (thumb.getIcon() != null) {
                        thumb.getIcon().addMouseListener(new ThumbMouseListener());
                        flowPanel.add(thumb);
                    }
                }
            }
            // No result found
            else {
                return new JScrollPane(getNothingFoundPanel());
            }
        }
        return jsp;
    }

    @Override
    public void update(JajukEvent event) {
        JajukEvents subject = event.getSubject();
        if (subject.equals(JajukEvents.FILE_LAUNCHED)) {
            // Change local collection suggestions every 10 track plays
            if (comp % 10 == 0) {
                refreshLocalCollectionTabs();
            }
            comp++;
            // update last.fm panels
            refreshLastFMCollectionTabs();
        } else if (subject.equals(JajukEvents.PARAMETERS_CHANGE) && isLastFMTabsVisible()) {
            // The show/hide unmounted may have changed, refresh local
            // collection panels
            refreshLastFMCollectionTabs();
        } else if (subject.equals(JajukEvents.COVER_DEFAULT_CHANGED)
                || subject.equals(JajukEvents.SUGGESTIONS_REFRESH)) {
            // New default cover, refresh the view
            refreshLocalCollectionTabs();
        }
    }

    /**
     * [Perf].
     *
     * @return whether LastFM tabs are visible or not
     */
    private boolean isLastFMTabsVisible() {
        // Refresh artists only if user selected similar artists or albums tab
        return (tabs.getSelectedIndex() == 3 || tabs.getSelectedIndex() == 4)
                // Check this view perspective is visible
                && PerspectiveManager.getCurrentPerspective().equals(this.getPerspective());
    }

    /**
     * Refresh lastFM tabs on perspective selection if tabs visible.
     */
    @Override
    public void onPerspectiveSelection() {
        refreshLastFMCollectionTabs();
    }

    /**
     * Gets the nothing found panel.
     *
     * @return a panel with text explaining why no item has been found
     */
    JPanel getNothingFoundPanel() {
        JPanel out = new JPanel(new MigLayout("ins 5", "grow"));
        JEditorPane jteNothing = new JEditorPane("text/html", Messages.getString("SuggestionView.7"));
        jteNothing.setBorder(null);
        jteNothing.setEditable(false);
        jteNothing.setOpaque(false);
        jteNothing.setToolTipText(Messages.getString("SuggestionView.7"));
        out.add(jteNothing, "center,grow");
        return out;
    }
}