net.pms.newgui.StatusTab.java Source code

Java tutorial

Introduction

Here is the source code for net.pms.newgui.StatusTab.java

Source

/*
 * PS3 Media Server, for streaming any medias to your PS3.
 * Copyright (C) 2008  A.Brochard
 *
 * 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; version 2
 * of the License only.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package net.pms.newgui;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.layout.*;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
import net.pms.Messages;
import net.pms.PMS;
import net.pms.configuration.PmsConfiguration;
import net.pms.configuration.RendererConfiguration;
import net.pms.util.BasicPlayer;
import net.pms.util.FormLayoutUtil;
import net.pms.util.StringUtil;
import net.pms.util.UMSUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StatusTab {
    private static final Logger LOGGER = LoggerFactory.getLogger(StatusTab.class);
    private static final Color memColor = new Color(119, 119, 119, 128);
    private static final Color bufColor = new Color(75, 140, 181, 128);

    public static class RendererItem implements ActionListener {
        public ImagePanel icon;
        public JLabel label;
        public GuiUtil.MarqueeLabel playingLabel;
        //      public GuiUtil.ScrollLabel playingLabel;
        public GuiUtil.FixedPanel playing;
        public JLabel time;
        public JFrame frame;
        public GuiUtil.SmoothProgressBar rendererProgressBar;
        public RendererPanel panel;
        public String name = " ";
        private JPanel _panel = null;

        public RendererItem(RendererConfiguration r) {
            icon = addRendererIcon(r.getRendererIcon());
            icon.enableRollover();
            label = new JLabel(r.getRendererName());
            playingLabel = new GuiUtil.MarqueeLabel(" ");
            //         playingLabel = new GuiUtil.ScrollLabel(" ");
            playingLabel.setForeground(Color.gray);
            int h = (int) playingLabel.getSize().getHeight();
            playing = new GuiUtil.FixedPanel(0, h);
            playing.add(playingLabel);
            time = new JLabel(" ");
            time.setForeground(Color.gray);
            rendererProgressBar = new GuiUtil.SmoothProgressBar(0, 100,
                    new GuiUtil.SimpleProgressUI(Color.gray, Color.gray));
            rendererProgressBar.setStringPainted(true);
            rendererProgressBar.setBorderPainted(false);
            rendererProgressBar.setString(r.getAddress().getHostAddress());
            rendererProgressBar.setForeground(bufColor);
        }

        @Override
        public void actionPerformed(final ActionEvent e) {
            BasicPlayer.State state = ((BasicPlayer) e.getSource()).getState();
            time.setText((state.playback == BasicPlayer.STOPPED || StringUtil.isZeroTime(state.position)) ? " "
                    : UMSUtils.playedDurationStr(state.position, state.duration));
            rendererProgressBar.setValue((int) (100 * state.buffer / bufferSize));
            String n = (state.playback == BasicPlayer.STOPPED || StringUtils.isBlank(state.name)) ? " "
                    : state.name;
            if (!name.equals(n)) {
                name = n;
                playingLabel.setText(name);
            }
        }

        public void addTo(Container parent) {
            parent.add(getPanel());
            parent.validate();
            // Maximize the playing label width
            int w = _panel.getWidth() - _panel.getInsets().left - _panel.getInsets().right;
            playing.setSize(w, (int) playingLabel.getSize().getHeight());
            playingLabel.setMaxWidth(w);
        }

        public void delete() {
            try {
                // Delete the popup if open
                if (frame != null) {
                    frame.dispose();
                    frame = null;
                }
                Container parent = _panel.getParent();
                parent.remove(_panel);
                parent.revalidate();
                parent.repaint();
            } catch (Exception e) {
            }
        }

        public JPanel getPanel() {
            if (_panel == null) {
                PanelBuilder b = new PanelBuilder(new FormLayout("center:pref",
                        "max(140px;pref), 3dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref"));
                b.opaque(true);
                CellConstraints cc = new CellConstraints();
                b.add(icon, cc.xy(1, 1));
                b.add(label, cc.xy(1, 3, CellConstraints.CENTER, CellConstraints.DEFAULT));
                b.add(rendererProgressBar, cc.xy(1, 5));
                b.add(playing, cc.xy(1, 7, CellConstraints.CENTER, CellConstraints.DEFAULT));
                b.add(time, cc.xy(1, 9));
                _panel = b.getPanel();
            }
            return _panel;
        }
    }

    private ImagePanel imagePanel;
    private JPanel renderers;
    private JLabel jl;
    private JProgressBar memoryProgressBar;
    private GuiUtil.SegmentedProgressBarUI memBarUI;
    private JLabel bitrateLabel;
    private JLabel currentBitrate;
    private JLabel currentBitrateLabel;
    private JLabel peakBitrate;
    private JLabel peakBitrateLabel;
    private long rc = 0;
    private long peak;
    private static DecimalFormat formatter = new DecimalFormat("#,###");
    private static int bufferSize;

    StatusTab(PmsConfiguration configuration) {
        bufferSize = configuration.getMaxMemoryBufferSize();
    }

    public void updateCurrentBitrate() {
        long total = 0;
        List<RendererConfiguration> foundRenderers = PMS.get().getFoundRenderers();
        synchronized (foundRenderers) {
            for (RendererConfiguration r : foundRenderers) {
                total += r.getBuffer();
            }
        }
        if (total == 0) {
            currentBitrate.setText("0");
        }
    }

    public JLabel getJl() {
        return jl;
    }

    public ImagePanel getImagePanel() {
        return imagePanel;
    }

    public JComponent build() {
        // Apply the orientation for the locale
        ComponentOrientation orientation = ComponentOrientation.getOrientation(PMS.getLocale());

        String colSpec = FormLayoutUtil.getColSpec("pref, 30dlu, fill:pref:grow, 30dlu, pref", orientation);
        //                                             1     2          3           4     5

        FormLayout layout = new FormLayout(colSpec,
                //                          1     2          3            4     5
                //                   //////////////////////////////////////////////////
                "p," // Detected Media Renderers --------------------//  1
                        + "9dlu," //                                              //
                        + "fill:p:grow," //                 <renderers>                  //  3
                        + "3dlu," //                                              //
                        + "p," // ---------------------------------------------//  5
                        + "3dlu," //           |                       |          //
                        + "p," // Connected |  Memory Usage         |<bitrate> //  7
                        + "3dlu," //           |                       |          //
                        + "p," //  <icon>   |  <statusbar>          |          //  9
                               //////////////////////////////////////////////////
        );

        PanelBuilder builder = new PanelBuilder(layout);
        builder.border(Borders.DIALOG);
        builder.opaque(true);
        CellConstraints cc = new CellConstraints();

        // Renderers
        JComponent cmp = builder.addSeparator(Messages.getString("StatusTab.9"),
                FormLayoutUtil.flip(cc.xyw(1, 1, 5), colSpec, orientation));
        cmp = (JComponent) cmp.getComponent(0);
        Font bold = cmp.getFont().deriveFont(Font.BOLD);
        Color fgColor = new Color(68, 68, 68);
        cmp.setFont(bold);

        renderers = new JPanel(new GuiUtil.WrapLayout(FlowLayout.CENTER, 20, 10));
        JScrollPane rsp = new JScrollPane(renderers, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        rsp.setBorder(BorderFactory.createEmptyBorder());
        rsp.setPreferredSize(new Dimension(0, 260));
        rsp.getHorizontalScrollBar().setLocation(0, 250);

        builder.add(rsp, cc.xyw(1, 3, 5));

        cmp = builder.addSeparator(null, FormLayoutUtil.flip(cc.xyw(1, 5, 5), colSpec, orientation));

        // Connected
        jl = new JLabel(Messages.getString("StatusTab.3"));
        builder.add(jl, FormLayoutUtil.flip(cc.xy(1, 7, "center, top"), colSpec, orientation));
        jl.setFont(bold);
        jl.setForeground(fgColor);

        imagePanel = buildImagePanel("/resources/images/icon-status-connecting.png");
        builder.add(imagePanel, FormLayoutUtil.flip(cc.xy(1, 9), colSpec, orientation));

        // Memory
        memBarUI = new GuiUtil.SegmentedProgressBarUI(Color.white, Color.gray);
        memBarUI.setActiveLabel("{}", Color.white, 0);
        memBarUI.setActiveLabel("{}", Color.red, 90);
        memBarUI.addSegment("", memColor);
        memBarUI.addSegment("", bufColor);
        memBarUI.setTickMarks(getTickMarks(), "{}");
        memoryProgressBar = new GuiUtil.CustomUIProgressBar(0, 100, memBarUI);
        memoryProgressBar.setStringPainted(true);
        memoryProgressBar.setForeground(new Color(75, 140, 181));
        memoryProgressBar.setString(Messages.getString("StatusTab.5"));

        JLabel mem = builder
                .addLabel(
                        "<html><b>" + Messages.getString("StatusTab.6") + "</b> ("
                                + Messages.getString("StatusTab.12") + ")</html>",
                        FormLayoutUtil.flip(cc.xy(3, 7), colSpec, orientation));
        mem.setForeground(fgColor);
        builder.add(memoryProgressBar, FormLayoutUtil.flip(cc.xyw(3, 9, 1), colSpec, orientation));

        // Bitrate
        String bitColSpec = "left:pref, 3dlu, right:pref:grow";
        PanelBuilder bitrateBuilder = new PanelBuilder(new FormLayout(bitColSpec, "p, 1dlu, p, 1dlu, p"));

        bitrateLabel = new JLabel("<html><b>" + Messages.getString("StatusTab.13") + "</b> ("
                + Messages.getString("StatusTab.11") + ")</html>");
        bitrateLabel.setForeground(fgColor);
        bitrateBuilder.add(bitrateLabel, FormLayoutUtil.flip(cc.xy(1, 1), bitColSpec, orientation));

        currentBitrateLabel = new JLabel(Messages.getString("StatusTab.14"));
        currentBitrateLabel.setForeground(fgColor);
        bitrateBuilder.add(currentBitrateLabel, FormLayoutUtil.flip(cc.xy(1, 3), bitColSpec, orientation));

        currentBitrate = new JLabel("0");
        currentBitrate.setForeground(fgColor);
        bitrateBuilder.add(currentBitrate, FormLayoutUtil.flip(cc.xy(3, 3), bitColSpec, orientation));

        peakBitrateLabel = new JLabel(Messages.getString("StatusTab.15"));
        peakBitrateLabel.setForeground(fgColor);
        bitrateBuilder.add(peakBitrateLabel, FormLayoutUtil.flip(cc.xy(1, 5), bitColSpec, orientation));

        peakBitrate = new JLabel("0");
        peakBitrate.setForeground(fgColor);
        bitrateBuilder.add(peakBitrate, FormLayoutUtil.flip(cc.xy(3, 5), bitColSpec, orientation));

        builder.add(bitrateBuilder.getPanel(),
                FormLayoutUtil.flip(cc.xywh(5, 7, 1, 3, "left, top"), colSpec, orientation));

        JPanel panel = builder.getPanel();

        // Apply the orientation to the panel and all components in it
        panel.applyComponentOrientation(orientation);

        JScrollPane scrollPane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_NEVER,
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scrollPane.setBorder(BorderFactory.createEmptyBorder());
        startMemoryUpdater();
        return scrollPane;
    }

    public void setReadValue(long v, String msg) {
        if (v < rc) {
            rc = v;
        } else {
            int sizeinMb = (int) ((v - rc) / 125) / 1024;

            if (sizeinMb > peak) {
                peak = sizeinMb;
            }

            currentBitrate.setText(formatter.format(sizeinMb));
            peakBitrate.setText(formatter.format(peak));
            rc = v;
        }
    }

    public ImagePanel buildImagePanel(String url) {
        BufferedImage bi = null;

        if (url != null) {
            try {
                bi = ImageIO.read(LooksFrame.class.getResourceAsStream(url));
            } catch (IOException e) {
                LOGGER.debug("Caught exception", e);
            }
        }

        return new ImagePanel(bi);
    }

    public void addRenderer(final RendererConfiguration renderer) {
        final RendererItem r = new RendererItem(renderer);
        r.addTo(renderers);
        renderer.setGuiComponents(r);
        r.icon.setAction(new AbstractAction() {
            private static final long serialVersionUID = -6316055325551243347L;

            @Override
            public void actionPerformed(ActionEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        if (r.frame == null) {
                            JFrame top = (JFrame) SwingUtilities
                                    .getWindowAncestor((Component) PMS.get().getFrame());
                            // We're using JFrame instead of JDialog here so as to have a minimize button. Since the player panel
                            // is intrinsically a freestanding module this approach seems valid to me but some frown on it: see
                            // http://stackoverflow.com/questions/9554636/the-use-of-multiple-jframes-good-bad-practice
                            r.frame = new JFrame();
                            r.panel = new RendererPanel(renderer);
                            r.frame.add(r.panel);
                            r.panel.update();
                            r.frame.setResizable(false);
                            r.frame.setIconImage(((JFrame) PMS.get().getFrame()).getIconImage());
                            r.frame.setLocationRelativeTo(top);
                            r.frame.setVisible(true);
                        } else {
                            r.frame.setVisible(true);
                            r.frame.toFront();
                        }
                    }
                });
            }
        });
    }

    public static void updateRenderer(final RendererConfiguration renderer) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                if (renderer.gui != null) {
                    renderer.gui.icon.set(getRendererIcon(renderer.getRendererIcon()));
                    renderer.gui.label.setText(renderer.getRendererName());
                    // Update the popup panel if it's been opened
                    if (renderer.gui.panel != null) {
                        renderer.gui.panel.update();
                    }
                }
            }
        });
    }

    public static ImagePanel addRendererIcon(String icon) {
        BufferedImage bi = getRendererIcon(icon);
        return bi != null ? new ImagePanel(bi) : null;
    }

    public static BufferedImage getRendererIcon(String icon) {
        BufferedImage bi = null;

        if (icon != null) {

            if (icon.matches(".*\\S+://.*")) {
                try {
                    bi = ImageIO.read(new URL(icon));
                } catch (Exception e) {
                    LOGGER.debug("Error reading icon url: " + e);
                }
                if (bi != null) {
                    return bi;
                } else {
                    LOGGER.debug("Unable to read icon url \"{}\", using '{}' instead.",
                            RendererConfiguration.UNKNOWN_ICON);
                    icon = RendererConfiguration.UNKNOWN_ICON;
                }
            }

            try {
                InputStream is = null;

                /**
                 * Check for a custom icon file first
                 *
                 * The file can be a) the name of a file in the renderers directory b) a path relative
                 * to the PMS working directory or c) an absolute path. If no file is found,
                 * the built-in resource (if any) is used instead.
                 *
                 * The File constructor does the right thing for the relative and absolute path cases,
                 * so we only need to detect the bare filename case.
                 *
                 * RendererIcon = foo.png // e.g. $PMS/renderers/foo.png
                 * RendererIcon = images/foo.png // e.g. $PMS/images/foo.png
                 * RendererIcon = /path/to/foo.png
                 */

                File f = new File(icon);

                if (!f.isAbsolute() && f.getParent() == null) { // filename
                    f = new File("renderers", icon);
                }

                if (f.isFile()) {
                    is = new FileInputStream(f);
                }

                if (is == null) {
                    is = LooksFrame.class.getResourceAsStream("/resources/images/clients/" + icon);
                }

                if (is == null) {
                    is = LooksFrame.class.getResourceAsStream("/renderers/" + icon);
                }

                if (is != null) {
                    bi = ImageIO.read(is);
                }
            } catch (IOException e) {
                LOGGER.debug("Caught exception", e);
            }
        }
        if (bi == null) {
            LOGGER.debug("Failed to load icon: " + icon);
        }
        return bi;
    }

    private int getTickMarks() {
        int mb = (int) (Runtime.getRuntime().maxMemory() / 1048576);
        return mb < 1000 ? 100 : mb < 2500 ? 250 : mb < 5000 ? 500 : 1000;
    }

    public void updateMemoryUsage() {
        final long max = Runtime.getRuntime().maxMemory() / 1048576;
        final long used = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1048576;
        long buf = 0;
        List<RendererConfiguration> foundRenderers = PMS.get().getFoundRenderers();
        synchronized (foundRenderers) {
            for (RendererConfiguration r : PMS.get().getFoundRenderers()) {
                buf += (r.getBuffer());
            }
        }
        final long buffer = buf;
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                memBarUI.setValues(0, (int) max, (int) (used - buffer), (int) buffer);
            }
        });
    }

    private void startMemoryUpdater() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (;;) {
                    updateMemoryUsage();
                    try {
                        Thread.sleep(2000);
                    } catch (Exception e) {
                        return;
                    }
                }
            }
        };
        new Thread(r).start();
    }
}