package com.pugh.sockso.gui;
import com.pugh.sockso.Sockso;
import com.pugh.sockso.Constants;
import com.pugh.sockso.Manager;
import com.pugh.sockso.Properties;
import com.pugh.sockso.PropertiesListener;
import com.pugh.sockso.db.Database;
import com.pugh.sockso.events.LatestVersionEvent;
import com.pugh.sockso.resources.Resources;
import com.pugh.sockso.resources.Locale;
import com.pugh.sockso.web.IpFinder;
import com.pugh.sockso.web.Server;
import com.pugh.sockso.music.CollectionManager;
import java.awt.Font;
import java.awt.Cursor;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Dimension;
import java.awt.event.WindowEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.IllegalComponentStateException;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.JOptionPane;
import javax.swing.JTabbedPane;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import javax.swing.JPopupMenu;
import javax.swing.JMenuItem;
import org.apache.log4j.Logger;
import com.jgoodies.looks.plastic.Plastic3DLookAndFeel;
import edu.stanford.ejalbert.BrowserLauncher;
import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
@Singleton
public class AppFrame extends JFrame implements PropertiesListener, Manager {
private static final Logger log = Logger.getLogger( AppFrame.class );
private final Properties p;
private final Locale locale;
private final Injector injector;
protected final Server sv;
protected final Resources r;
private TrayIcon tray = null;
private JLabel urlLabel;
private ConsoleFrame consoleFrame;
/**
* Creates a new instance of AppFrame
*
*/
@Inject
public AppFrame( final Injector injector, final Properties p, final Server sv,
final Resources r, final Locale locale ) {
super( locale == null ? "" : locale.getString("gui.window.main") + " (" +Sockso.VERSION+ ")" );
this.injector = injector;
this.p = p;
this.sv = sv;
this.r = r;
this.locale = locale;
urlLabel = new JLabel();
tray = new TrayIcon( this, r );
}
protected void initComponents() {
// set application L&F
log.debug( "Setting Look & Feel" );
try {
UIManager.setLookAndFeel( new Plastic3DLookAndFeel() );
}
catch ( Exception e ) {
log.error( e.getMessage() );
}
log.debug( "Initialising TrayIcon" );
tray.init();
log.debug( "Initialising GUI components" );
}
/**
* lays out the GUI components in their right places
*
*/
protected void layoutComponents() {
log.debug( "Laying out GUI components" );
setIconImage( r.getImage("icons/16x16/sockso.png") );
setLayout( new BorderLayout() );
add( getMainPane(), BorderLayout.CENTER );
add( getBottomPane(), BorderLayout.SOUTH );
addWindowListener( new WindowAdapter() {
@Override
public void windowClosing( final WindowEvent evt ) {
if ( tray.isActive() ) toggleVisibility();
else exit();
}
});
setSize( new Dimension(700,500) );
setLocationRelativeTo( null );
p.addPropertiesListener( this );
// done with splash now
Splash.close();
}
public void open() {
initComponents();
layoutComponents();
// check to see if we should start minimized or not
boolean setVisible = true;
if ( tray.isActive() && p.get(Constants.APP_START_MINIMIZED).equals(Properties.YES) )
setVisible = false;
setVisible( setVisible );
}
/**
* checks with the user they want to do it, then closes the app
*
*/
public void exit() {
if ( p.get(Constants.APP_CONFIRM_EXIT).equals(Properties.YES) )
if ( JOptionPane.showConfirmDialog( this, "Are you sure you want to exit?",
"Exit Sockso", JOptionPane.YES_NO_OPTION ) != JOptionPane.YES_OPTION )
return;
com.pugh.sockso.Main.exit();
}
/**
* toggles the visibility of the window
*
*/
public void toggleVisibility() {
setVisible( !isVisible() );
}
/**
* the panel across the bottom of the window
*
*/
private JPanel getBottomPane() {
final JButton exit = new JButton( locale.getString("gui.label.exit"), new ImageIcon(r.getImage("icons/22x22/exit.png")) );
exit.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
AppFrame.this.exit();
}
});
final JButton hide = new JButton( locale.getString("gui.label.hide"), new ImageIcon(r.getImage("icons/22x22/hide.png")) );
hide.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
AppFrame.this.setVisible( false );
}
});
final JButton console = new JButton( locale.getString("gui.label.showConsoleWindow"), new ImageIcon(r.getImage("icons/22x22/console.png")) );
console.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
if ( consoleFrame == null ) {
consoleFrame = injector.getInstance( ConsoleFrame.class );
consoleFrame.setBounds( 100, 100, 600, 500 );
}
consoleFrame.setVisible( true );
}
});
// in admin mode we won't have a server
if ( sv != null ) {
urlLabel.setText( locale.getString("gui.label.gettingIPAddress") );
urlLabel.addMouseListener( injector.getInstance(UrlLabelMouseAdapter.class) );
urlLabel.setToolTipText( "Click to open Sockso in your browser." );
updateUrlLabel();
}
final JPanel buttons = new JPanel();
buttons.setLayout( new FlowLayout(FlowLayout.RIGHT) );
buttons.add( console );
buttons.add( exit );
if ( tray.isActive() )
buttons.add( hide );
final JPanel panel = new JPanel( new BorderLayout() );
panel.add( buttons, BorderLayout.EAST );
panel.add( urlLabel, BorderLayout.WEST );
return panel;
}
/**
* updates the URL label to our current address
*
*/
protected void updateUrlLabel() {
final String myurl = sv.getProtocol() +"://" + sv.getHost();
urlLabel.setText( "<html><head><title></title></head><body>" +
" " + locale.getString("gui.label.yourAddress") + ": <a href='" + myurl + "'>" + myurl + "</a>" +
"</body></html>" );
}
/**
* returns the URL label
*
* @return
*
*/
protected JLabel getUrlLabel() {
return urlLabel;
}
/**
* the main tab control
*
*/
private JTabbedPane getMainPane() {
final JTabbedPane pane = new JTabbedPane();
final MusicPanel musicPanel = injector.getInstance( MusicPanel.class );
musicPanel.init();
final UsersPanel usersPanel = injector.getInstance( UsersPanel.class );
usersPanel.init();
pane.addTab( locale.getString("gui.label.music"), new ImageIcon(r.getImage("icons/22x22/music.png")),
musicPanel, locale.getString("gui.tooltip.music") );
pane.addTab( locale.getString("gui.label.collection"), new ImageIcon(r.getImage("icons/22x22/collection.png")),
injector.getInstance(CollectionPanel.class), locale.getString("gui.tooltip.collection") );
pane.addTab( locale.getString("gui.label.users"), new ImageIcon(r.getImage("icons/22x22/users.png")),
usersPanel, locale.getString("gui.tooltip.users") );
pane.addTab( locale.getString("gui.label.general"), new ImageIcon(r.getImage("icons/22x22/general.png")),
injector.getInstance(GeneralPanel.class), locale.getString("gui.tooltip.general") );
pane.addTab( locale.getString("gui.label.encoders"), new ImageIcon(r.getImage("icons/22x22/encoders.png")),
injector.getInstance(EncodersPanel.class), locale.getString("gui.tooltip.encoders") );
return pane;
}
/**
* closes the window and disposes of it
*
*/
public void close() {
log.info( "Closing GUI" );
setVisible( false );
dispose();
}
/**
* properties have been saved
*
* @param p the new properties
*
*/
public void propertiesSaved( Properties p ) {
updateUrlLabel();
}
/**
* checks for a newer version
*
*/
public void latestVersionReceived( final LatestVersionEvent evt ) {
final String latestVersion = evt.getVersion();
if ( latestVersion != null && !latestVersion.equals(Sockso.VERSION) ) {
Splash.closeNow();
final String message = locale.getString(
"misc.msg.updateAvailable",
new String[] { latestVersion }
);
JOptionPane.showMessageDialog( this, message, "Sockso", JOptionPane.INFORMATION_MESSAGE );
}
}
}
/**
* mouse adapter for the URL label
*
*/
class UrlLabelMouseAdapter extends MouseAdapter {
private static final Logger log = Logger.getLogger( UrlLabelMouseAdapter.class );
private final JFrame parent;
private final Server sv;
private final Resources r;
private final JPopupMenu menu;
private final IpFinder ipFinder;
private final Locale locale;
/**
* creates the mouse adapter
*
* @param parent the frame the label is on
*
*/
@Inject
public UrlLabelMouseAdapter( final AppFrame parent, final Server sv, final Resources r,
final IpFinder ipFinder, final Locale locale ) {
this.parent = parent;
this.sv = sv;
this.r = r;
this.ipFinder = ipFinder;
this.locale = locale;
this.menu = createPopupMenu();
}
/**
* mouse moved over the label
*
* @param evt the mouse event
*
*/
@Override
public void mouseEntered( final MouseEvent evt ) {
parent.setCursor( new Cursor(Cursor.HAND_CURSOR) );
}
/**
* mouse moved off the label
*
* @param evt the mouse event
*
*/
@Override
public void mouseExited( final MouseEvent evt ) {
parent.setCursor( new Cursor(Cursor.DEFAULT_CURSOR) );
}
/**
* mouse clicked, show popup
*
* @param evt the mouse event
*
*/
@Override
public void mouseClicked( final MouseEvent evt ) {
// this error was thrown once, but i'm not sure why. so we'll
// just catch it and report it, the user can try clicking
// again and it'll probably work.
try {
menu.show( (JLabel) evt.getSource(), evt.getX(), evt.getY() );
}
catch ( final IllegalComponentStateException e ) {
log.error( e );
}
}
/**
* creates and returns a popup menu for this component
*
* @return a popup menu
*
*/
private JPopupMenu createPopupMenu() {
final JPopupMenu popup = new JPopupMenu();
final JMenuItem inet = new JMenuItem( locale.getString("gui.label.internetAddress") );
inet.setFont( inet.getFont().deriveFont(Font.BOLD) );
inet.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
openUrl( sv.getProtocol() +"://" + sv.getHost() );
}
});
final JMenuItem local = new JMenuItem( locale.getString("gui.label.myComputer") );
local.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
openUrl( sv.getProtocol() +"://127.0.0.1:" + sv.getPort() );
}
});
final JMenuItem refresh = new JMenuItem( locale.getString("gui.label.refresh"), new ImageIcon(r.getImage("icons/16x16/refresh.png")) );
refresh.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
ipFinder.refresh();
JOptionPane.showMessageDialog( parent, "IP Updated!" );
}
});
popup.add( inet );
popup.add( local );
popup.addSeparator();
popup.add( refresh );
return popup;
}
/**
* open the URL in the users default browser (hopefully)
*
* @param url the url to open
*
*/
private void openUrl( String url ) {
String errMsg = "Error attempting to launch web browser: ";
boolean error = true;
log.debug( "Opening URL " + url );
try {
final BrowserLauncher launcher = new BrowserLauncher();
launcher.openURLinBrowser( url );
error = false;
}
catch ( UnsupportedOperatingSystemException e ) {
log.error( e );
errMsg += e.getLocalizedMessage();
}
catch ( BrowserLaunchingInitializingException e ) {
log.error( e );
errMsg += e.getLocalizedMessage();
}
if ( error )
JOptionPane.showMessageDialog( null, errMsg );
}
}