/*
* Copyright (C) 2008 Richard Dallaway <richard@dallaway.com>
*
* This file is part of Sloppy.
*
* Sloppy 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
* (at your option) any later version.
*
* Sloppy 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 Sloppy; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.dallaway.sloppy;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.net.MalformedURLException;
import java.net.URL;
import javax.jnlp.BasicService;
import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
/**
* Implementation of a graphical front end for Sloppy.
*/
public class MatisseGUI extends javax.swing.JFrame implements UserInterface
{
private static final long serialVersionUID = 834144118087699736L;
private final Configuration conf;
/** Create the GUI
*
* @param conf the configuration object, used to read/write values.
*/
public MatisseGUI(final Configuration conf)
{
this.conf = conf;
initComponents();
// Provide an icon for the window:
String icon16 = "/sloppy_16.png"; //$NON-NLS-1$
setIconImage(new ImageIcon(getClass().getResource(icon16)).getImage());
// Set the destination URL
url_textfield.setText( conf.getDestination() != null ? conf.getDestination().toExternalForm() : "http://" );
// Set the focus on the URL, with the cursor at the end of the address:
url_textfield.setCaretPosition(url_textfield.getText().length());
url_textfield.requestFocus();
// port_textfield and about_textfield are set up in initComponents()
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
tabs = new javax.swing.JTabbedPane();
sloppy_tab = new javax.swing.JPanel();
address_label = new javax.swing.JLabel();
url_textfield = new javax.swing.JTextField();
connection_speed_label = new javax.swing.JLabel();
go_label = new javax.swing.JLabel();
go_button = new javax.swing.JButton();
connection_speed_options = new javax.swing.JComboBox();
options_tab = new javax.swing.JPanel();
port_label = new javax.swing.JLabel();
port_textfield = new javax.swing.JTextField();
apply_button = new javax.swing.JButton();
about_tab = new javax.swing.JPanel();
about_logo = new javax.swing.JLabel();
about_text_scroller = new javax.swing.JScrollPane();
about_pane = new javax.swing.JEditorPane();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("messages"); // NOI18N
setTitle(bundle.getString("name")); // NOI18N
setName("main_frame"); // NOI18N
tabs.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
aboutSelected(evt);
}
});
address_label.setText(bundle.getString("prompt.enterUrl")); // NOI18N
url_textfield.setText(bundle.getString("MatisseGUI.url_textfield.text")); // NOI18N
url_textfield.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
url_textfieldActionPerformed(evt);
}
});
connection_speed_label.setText(bundle.getString("prompt.selectSpeed")); // NOI18N
go_label.setText(bundle.getString("prompt.launch")); // NOI18N
go_button.setIcon(new javax.swing.ImageIcon(getClass().getResource("/play16x16.gif"))); // NOI18N
go_button.setText(bundle.getString("button.launch")); // NOI18N
go_button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
go_buttonActionPerformed(evt);
}
});
connection_speed_options.setModel(new BandwidthList(conf.getBytesPerSecond()));
connection_speed_options.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connection_speed_optionsActionPerformed(evt);
}
});
org.jdesktop.layout.GroupLayout sloppy_tabLayout = new org.jdesktop.layout.GroupLayout(sloppy_tab);
sloppy_tab.setLayout(sloppy_tabLayout);
sloppy_tabLayout.setHorizontalGroup(
sloppy_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(sloppy_tabLayout.createSequentialGroup()
.add(sloppy_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(org.jdesktop.layout.GroupLayout.TRAILING, sloppy_tabLayout.createSequentialGroup()
.add(44, 44, 44)
.add(url_textfield, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 336, Short.MAX_VALUE))
.add(sloppy_tabLayout.createSequentialGroup()
.addContainerGap()
.add(sloppy_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(sloppy_tabLayout.createSequentialGroup()
.add(21, 21, 21)
.add(go_button))
.add(sloppy_tabLayout.createSequentialGroup()
.add(21, 21, 21)
.add(connection_speed_options, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.add(org.jdesktop.layout.GroupLayout.TRAILING, address_label, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 360, Short.MAX_VALUE)
.add(connection_speed_label)
.add(go_label))))
.addContainerGap())
);
sloppy_tabLayout.setVerticalGroup(
sloppy_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(sloppy_tabLayout.createSequentialGroup()
.add(70, 70, 70)
.add(address_label)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(url_textfield, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(18, 18, 18)
.add(connection_speed_label)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(connection_speed_options, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(18, 18, 18)
.add(go_label)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(go_button)
.addContainerGap(47, Short.MAX_VALUE))
);
tabs.addTab(bundle.getString("tab.sloppy.title"), sloppy_tab); // NOI18N
port_label.setText(bundle.getString("prompt.port")); // NOI18N
port_textfield.setText(Integer.toString(conf.getLocalPort()));
port_textfield.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
port_textfieldActionPerformed(evt);
}
});
apply_button.setText(bundle.getString("button.apply")); // NOI18N
apply_button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
apply_buttonActionPerformed(evt);
}
});
org.jdesktop.layout.GroupLayout options_tabLayout = new org.jdesktop.layout.GroupLayout(options_tab);
options_tab.setLayout(options_tabLayout);
options_tabLayout.setHorizontalGroup(
options_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(options_tabLayout.createSequentialGroup()
.addContainerGap()
.add(port_label)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(options_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(apply_button)
.add(port_textfield, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 98, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addContainerGap(191, Short.MAX_VALUE))
);
options_tabLayout.setVerticalGroup(
options_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(options_tabLayout.createSequentialGroup()
.add(73, 73, 73)
.add(options_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(port_label)
.add(port_textfield, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(apply_button)
.addContainerGap(166, Short.MAX_VALUE))
);
tabs.addTab(bundle.getString("tab.options.title"), options_tab); // NOI18N
about_logo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/sloppy_logo64.png"))); // NOI18N
about_pane.setContentType("text/html"); // NOI18N
about_pane.setEditable(false);
about_pane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
onLinkClick(evt);
}
});
about_text_scroller.setViewportView(about_pane);
about_pane.getAccessibleContext().setAccessibleDescription("text/plain"); // NOI18N
org.jdesktop.layout.GroupLayout about_tabLayout = new org.jdesktop.layout.GroupLayout(about_tab);
about_tab.setLayout(about_tabLayout);
about_tabLayout.setHorizontalGroup(
about_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(about_tabLayout.createSequentialGroup()
.addContainerGap()
.add(about_logo)
.addContainerGap(316, Short.MAX_VALUE))
.add(about_text_scroller, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
about_tabLayout.setVerticalGroup(
about_tabLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(about_tabLayout.createSequentialGroup()
.add(about_logo)
.add(18, 18, 18)
.add(about_text_scroller, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 223, Short.MAX_VALUE))
);
tabs.addTab(bundle.getString("tab.about.title"), about_tab); // NOI18N
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(tabs, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 421, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(tabs, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 351, Short.MAX_VALUE)
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void url_textfieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_url_textfieldActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_url_textfieldActionPerformed
/**
* Validate the desitnation URL
*
* @return true if a good URL was set; false otherwise.
*/
private boolean validateDestination()
{
final String url = url_textfield.getText();
if (url == null)
{
error(Messages.getString("error.noAddress")); //$NON-NLS-1$
return false;
}
// Get the URL entered by the user, and clean it up if required:
final URL destination = Util.clean(url);
if (destination == null)
{
// The destination can't be parsed as a good URL
error(Messages.getString("error.badAddress")); //$NON-NLS-1$
return false;
}
// The URL is good, so set it in the configuration, and in case clean()
// changed the URL, we update the textbox:
conf.setDestination(destination);
url_textfield.setText(destination.toExternalForm());
return true;
}
private void go_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_go_buttonActionPerformed
// Check for a good URL from the user.
if (validateDestination() == false)
{
return; // No good URL, so don't start the browser
}
// Save the current settings:
conf.saveMuffins();
// Make sure the server code is running:
if (conf.getServer().isRunning() == false)
{
error(Messages.getString("error.noRunning")); //$NON-NLS-1$
return;
}
// Open the browser:
String local_url = Util.makeLocalURL(conf.getLocalPort(), conf.getDestination());
try
{
if (!showDocument(new URL(local_url)))
{
error(Messages.getString("error.cannotLaunchBrowser")); //$NON-NLS-1$
}
}
catch (MalformedURLException mx)
{
// We are here if the new URL(local_url) call failed
// This, of course, "Will Never Happen".
error(Messages.getString("error.openBrowserFailed") + local_url); //$NON-NLS-1$
}
}//GEN-LAST:event_go_buttonActionPerformed
/**
* The user change connection speed, so we need to record that in the
* Configuration instance.
* @param evt the action event.
*/
private void connection_speed_optionsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connection_speed_optionsActionPerformed
conf.setBytesPerSecond( ((Bandwidth)(connection_speed_options.getSelectedItem())).getBytesPerSecond() );
}//GEN-LAST:event_connection_speed_optionsActionPerformed
private void port_textfieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_port_textfieldActionPerformed
}//GEN-LAST:event_port_textfieldActionPerformed
/** Update the "about text" when the tab is selected. */
private void aboutSelected(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_aboutSelected
about_pane.setText(new AboutText(conf, (Bandwidth)(connection_speed_options.getSelectedItem())).toString());
about_pane.setCaretPosition(0);
}//GEN-LAST:event_aboutSelected
private void apply_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_apply_buttonActionPerformed
if (port_textfield.getText() == null)
{
error(Messages.getString("error.noPort")); //$NON-NLS-1$
return;
}
final int value;
try
{
value = Integer.parseInt(port_textfield.getText());
}
catch (NumberFormatException nfx)
{
error(Messages.getString("error.badPort")); //$NON-NLS-1$
return;
}
// Has the port number changed?
if (value == conf.getLocalPort())
{
// No change; nothing to do
return;
}
conf.setLocalPort(value);
conf.saveMuffins();
// If we get here, we need to restart the listener/server.
JDialog progress = new JDialog(this, Messages.getString("info.wait"), false); //$NON-NLS-1$
progress.getContentPane().setLayout(new GridBagLayout());
JPanel panel = new JPanel(new GridBagLayout());
progress.getContentPane().add(panel);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.CENTER;
panel.add(new JLabel(Messages.getString("info.restarting")), gbc); //$NON-NLS-1$
progress.setSize(100, 50);
progress.setLocationRelativeTo(this);
progress.pack();
progress.setVisible(true);
debug("Stopping"); //$NON-NLS-1$
conf.getServer().stop();
debug("Starting"); //$NON-NLS-1$
conf.setLocalPort(value);
Thread thread = new Thread(conf.getServer());
thread.start();
debug("Disposing of please wait message"); //$NON-NLS-1$
progress.setVisible(false);
progress.dispose();
}//GEN-LAST:event_apply_buttonActionPerformed
/**
* For handling links in the "About" box.
*
* @see javax.swing.event.HyperlinkListener#hyperlinkUpdate(HyperlinkEvent)
*/
private void onLinkClick(javax.swing.event.HyperlinkEvent evt) {//GEN-FIRST:event_onLinkClick
if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
{
showDocument(evt.getURL());
}
}//GEN-LAST:event_onLinkClick
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel about_logo;
private javax.swing.JEditorPane about_pane;
private javax.swing.JPanel about_tab;
private javax.swing.JScrollPane about_text_scroller;
private javax.swing.JLabel address_label;
private javax.swing.JButton apply_button;
private javax.swing.JLabel connection_speed_label;
private javax.swing.JComboBox connection_speed_options;
private javax.swing.JButton go_button;
private javax.swing.JLabel go_label;
private javax.swing.JPanel options_tab;
private javax.swing.JLabel port_label;
private javax.swing.JTextField port_textfield;
private javax.swing.JPanel sloppy_tab;
private javax.swing.JTabbedPane tabs;
private javax.swing.JTextField url_textfield;
// End of variables declaration//GEN-END:variables
/** Are we showing debug output? */
private boolean debug = false;
/**
* @see UserInterface#setDebug(boolean)
*/
public void setDebug(final boolean isDebug)
{
this.debug = isDebug;
}
/**
* @see UserInterface#debug(java.lang.String)
*/
public void debug(String message)
{
if (debug)
{
System.out.println(message);
}
}
/**
* @see UserInterface#error(String, Exception)
*/
public void error(String message, Exception ex)
{
JOptionPane.showMessageDialog(this, message + "\n" + ex, Messages.getString("error.title"), JOptionPane.INFORMATION_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
if (debug)
{
ex.printStackTrace(System.err);
}
}
/**
* @see UserInterface#error(String)
*/
public void error(String message)
{
JOptionPane.showMessageDialog(this, message, Messages.getString("error.title"), JOptionPane.INFORMATION_MESSAGE); //$NON-NLS-1$
}
/**
* @see UserInterface#event(String, String)
*/
public void event(String client, String msg)
{
System.out.println(msg);
}
/**
* @see UserInterface#notice(String)
*/
public void notice(String message)
{
// Notices are really only relevant to non-GUI running.
System.out.println(message);
}
/**
* Open the user's browser at the given URL.
*
* @param url the URL to show.
* @return true if the browser was opened; false if there was
* any problem such as the JNLP service not available.
*/
private boolean showDocument(final URL url)
{
try
{
// Lookup the javax.jnlp.BasicService object
BasicService bs = (BasicService)ServiceManager.lookup("javax.jnlp.BasicService"); //$NON-NLS-1$
// Invoke the showDocument method
return bs.showDocument(url);
}
catch(UnavailableServiceException ue)
{
// Not running under Web Start?
ue.printStackTrace();
return false;
}
}
}