/**
* MediaSniper 3.0 (2008-08-02)
* Copyright 2007 - 2008 Zach Scrivena
* zachscrivena@gmail.com
* http://mediasniper.sourceforge.net/
*
* Simple program for downloading media files from popular websites.
*
* TERMS AND CONDITIONS:
* 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 3 of the License, or
* (at your option) 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, see <http://www.gnu.org/licenses/>.
*/
package org.freeshell.zs.mediasniper;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumnModel;
import javax.swing.text.JTextComponent;
import org.freeshell.zs.common.Debug;
import org.freeshell.zs.common.Downloader;
import org.freeshell.zs.common.ResourceManipulator;
import org.freeshell.zs.common.SimpleProperties;
import org.freeshell.zs.common.StringManipulator;
import org.freeshell.zs.common.SwingManipulator;
import org.freeshell.zs.common.TerminatingException;
/**
* Simple program for downloading media files from popular websites.
*/
class MediaSniper
extends JFrame
{
/** default program properties file */
private static final String DEFAULT_PROGRAM_PROPERTIES =
"/org/freeshell/zs/mediasniper/resources/properties.txt";
/** refresh interval, in milliseconds */
private static final long REFRESH_INTERVAL_MILLISECONDS = 100L;
/* thread pool for webpage parsers */
private final ExecutorService parsersThreadPool =
Executors.newCachedThreadPool();
/* thread pool for media file downloads */
private final ExecutorService downloadsThreadPool;
/** webpage parsers */
private final HashSet<WebpageParser> parsers = new HashSet<WebpageParser>();
/** media file downloads */
private final List<Download> downloads = new ArrayList<Download>();
/** table model for table of downloads */
private final DownloadsTableModel downloadsTableModel = new DownloadsTableModel();
/** program properties */
final SimpleProperties properties;
/** definitions for parsing webpages */
volatile List<Definition> definitions = null;
/** local filename manager */
final LocalFilenameManager localFilenameManager = new LocalFilenameManager();
/** "About" form */
private About about = null;
/** tray icon for program */
private TrayIcon trayIcon = null;
/** perform periodic refresh of the status label? */
volatile private boolean refreshStatus = true;
/**
* Main entry point for the program.
*
* @param args
* command-line arguments
*/
public static void main(
final String args[])
{
/************************************
* SCHEDULE GUI CREATION ON THE EDT *
************************************/
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
/* use system look and feel if possible */
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e)
{
/* ignore */
}
try
{
final MediaSniper ms = new MediaSniper(args);
ms.setVisible(true);
}
catch (TerminatingException e)
{
SwingManipulator.showErrorDialog(
null,
"Initialization Error",
e.getMessage() +
"\nPlease see Help > Usage for more information.");
System.exit(1);
}
catch (Exception e)
{
SwingManipulator.showErrorDialog(
null,
"Initialization Error",
"Failed to initialize program because of an unexpected error: " + e +
"\nPlease file a bug report to help improve this program." +
"\n\n" + Debug.getSystemInformationString() +
"\n\n" + Debug.getStackTraceString(e));
System.exit(1);
}
}
});
}
/**
* Constructor.
*
* @param args
* command-line arguments
*/
MediaSniper(
final String[] args)
{
/*********************************
* INITIALIZE PROGRAM PROPERTIES *
*********************************/
/* read default program properties */
try
{
properties = new SimpleProperties(ResourceManipulator.resourceAsString(MediaSniper.DEFAULT_PROGRAM_PROPERTIES));
}
catch (Exception ex)
{
throw new TerminatingException("(INTERNAL) Failed to load default program properties (" + ex + ").");
}
/* read user-editable program properties */
final SimpleProperties editableProperties;
try
{
editableProperties = new SimpleProperties(ResourceManipulator.resourceAsString(properties.getString("editable.properties")));
}
catch (Exception ex)
{
throw new TerminatingException("(INTERNAL) Failed to load user-editable program properties (" + ex + ").");
}
/* add user-specified properties */
for (String s : args)
{
final String[] kv = StringManipulator.parseKeyValueString(s);
if (editableProperties.get(kv[0]) == null)
{
/* not a valid user-editable program property */
if (properties.get(kv[0]) == null)
{
throw new TerminatingException("\"" + kv[0] + "\" is not a valid program property.");
}
else
{
throw new TerminatingException("\"" + kv[0] + "\" is not a user-editable program property.");
}
}
else
{
properties.set(kv[0], kv[1]);
}
}
downloadsThreadPool = Executors.newFixedThreadPool(properties.getInt("max.concurrent.downloads"));
/******************************
* INITIALIZE FORM COMPONENTS *
******************************/
initComponents();
/*****************************
* CONFIGURE FORM COMPONENTS *
*****************************/
setTitle(properties.getString("name"));
setAlwaysOnTop(properties.getBoolean("always.on.top"));
addWindowListener(new WindowAdapter()
{
@Override
public void windowIconified(WindowEvent e)
{
minimizeProgram();
}
@Override
public void windowDeiconified(WindowEvent e)
{
restoreProgram();
}
@Override
public void windowClosing(WindowEvent e)
{
closeProgram();
}
});
/* tooltips */
ToolTipManager.sharedInstance().setInitialDelay(0);
/* menu item: "Exit" */
exitItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
closeProgram();
}
});
/* menu item: "Always on Top" */
topItem.setSelected(properties.getBoolean("always.on.top"));
topItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
properties.setBoolean("always.on.top", topItem.isSelected());
refreshAlwaysOnTopMode();
}
});
/* menu item: "Minimize to Tray" */
trayItem.setSelected(properties.getBoolean("minimize.to.tray"));
trayItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
properties.setBoolean("minimize.to.tray", trayItem.isSelected());
}
});
/* menu item: "Restricted ASCII Filenames" */
restrictedItem.setSelected(properties.getBoolean("restricted.ascii.filenames"));
restrictedItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
properties.setBoolean("restricted.ascii.filenames", restrictedItem.isSelected());
}
});
/* menu item: "Short (8.3) Filenames" */
shortItem.setSelected(properties.getBoolean("short.filenames"));
shortItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
properties.setBoolean("short.filenames", shortItem.isSelected());
}
});
/* menu item: "SOCKS Proxy" */
proxyItem.setSelected(properties.getBoolean("proxy"));
proxyItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
properties.setBoolean("proxy", proxyItem.isSelected());
refreshProxyMode();
}
});
/* menu item: "Usage..." */
usageItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
/* list user-editable program properties and their current values */
final List<String> lines = new ArrayList<String>();
synchronized (editableProperties)
{
for (String s : editableProperties.keySet())
{
lines.add("* " + s + " - " +
editableProperties.getString(s)
+ " (\"" + properties.getAsString(s) + "\")");
}
}
Collections.sort(lines);
final StringBuilder sb = new StringBuilder();
for (String s : lines)
{
sb.append("\n");
sb.append(s);
}
try
{
SwingManipulator.showInfoDialog(
MediaSniper.this,
"Usage - " + properties.getString("name"),
"Usage information for " + properties.getString("name"),
ResourceManipulator.resourceAsString(properties.getString("usage")) +
sb.toString(),
10);
}
catch (Exception ee)
{
SwingManipulator.showErrorDialog(
MediaSniper.this,
getTitle(),
"(INTERNAL) Failed to read \"Usage\" text from JAR file.");
}
}
});
/* menu item: "About..." */
aboutItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if (about == null)
{
about = new About(MediaSniper.this);
}
about.setVisible(true);
about.setExtendedState(JFrame.NORMAL);
about.toFront();
}
});
/* menu item: "Check for Update" */
checkItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
new Thread(new Runnable()
{
public void run()
{
checkForUpdate(true, true);
}
}).start();
}
});
/* webpage URL text area */
SwingManipulator.addStandardEditingPopupMenu(new JTextComponent[] {urlText});
/* button: "Go!" */
goButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
urlText.requestFocus();
parseWebpage();
}
});
/* button: "Paste and Go!" */
pasteButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
urlText.selectAll();
urlText.paste();
urlText.requestFocus();
parseWebpage();
}
});
/* button: "Clear" */
clearButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
urlText.setText("");
urlText.requestFocus();
}
});
/* table of downloads */
downloadsTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
final TableColumnModel colModel = downloadsTable.getColumnModel();
colModel.getColumn(0).setMinWidth(0);
colModel.getColumn(1).setMinWidth(0);
colModel.getColumn(2).setMinWidth(0);
colModel.getColumn(0).setPreferredWidth(80);
colModel.getColumn(1).setPreferredWidth(10);
colModel.getColumn(2).setPreferredWidth(40);
downloadsTable.setToolTipText("Mouse-over any row for more information");
downloadsTable.setDefaultRenderer(TableStringCell.class, new TableStringCellRenderer(
downloadsTable.getForeground(),
downloadsTable.getBackground(),
downloadsTable.getSelectionForeground(),
downloadsTable.getSelectionBackground()));
downloadsTable.setDefaultRenderer(TableProgressCell.class, new TableProgressCellRenderer());
/*************************************************
* SETUP PROGRAM ICON, TRAY ICON, AND POPUP MENU *
*************************************************/
final Image iconImage;
try
{
iconImage = ImageIO.read(MediaSniper.class.getResource("/org/freeshell/zs/mediasniper/resources/icon.png"));
}
catch (Exception e)
{
throw new TerminatingException("Failed to load program icon image.");
}
/* set program icon */
setIconImage(iconImage);
/* setup tray icon */
try
{
trayIcon = new TrayIcon(iconImage, properties.getString("name") + " (click to restore)");
trayIcon.setImageAutoSize(true);
trayIcon.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
restoreProgram();
}
});
trayIcon.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
restoreProgram();
}
});
}
catch (Exception e)
{
trayIcon = null;
}
/* center form on the screen */
setLocationRelativeTo(null);
/*********************************************
* START CHECK FOR UPDATE & LOAD DEFINITIONS *
*********************************************/
new Thread(new Runnable()
{
public void run()
{
checkForUpdate(false, false);
loadDefinitions();
}
}).start();
/***********************************************
* START MONITORING NUMBER OF ACTIVE DOWNLOADS *
***********************************************/
new Thread(new Runnable()
{
public void run()
{
while (true)
{
if (refreshStatus)
{
int numActiveDownloads = 0;
synchronized (downloads)
{
for (Download d : downloads)
{
if (!d.isCompleted())
{
numActiveDownloads++;
}
}
}
final String statusString;
if (numActiveDownloads == 0)
{
statusString = "No active downloads";
}
else if (numActiveDownloads == 1)
{
statusString = "1 active download";
}
else
{
statusString = numActiveDownloads + " active downloads";
}
SwingManipulator.updateLabel(status, statusString);
}
Debug.sleep(10 * REFRESH_INTERVAL_MILLISECONDS);
}
}
}).start();
/****************************
* REFRESH PROGRAM SETTINGS *
****************************/
refreshAlwaysOnTopMode();
refreshProxyMode();
refreshDownloadDirectory();
}
/**
* Check for update.
* This method should run on a dedicated worker thread, not the EDT.
*
* @param showPromptIfLatest
* should a user prompt be displayed even if the current program is the latest release?
* @param showPromptOnError
* should a user prompt be displayed if an error occurs?
*/
private void checkForUpdate(
final boolean showPromptIfLatest,
final boolean showPromptOnError)
{
refreshStatus = false;
SwingManipulator.updateLabel(status, "Checking for update...");
try
{
/* try to retrieve latest defintions from homepage */
final StringBuilder sb = new StringBuilder();
final Downloader d = new Downloader(
new URL(properties.getString("update.url")),
sb);
new Thread(d).start();
while (true)
{
if (d.isProgressUpdated())
{
final int percent = d.getProgressPercent();
SwingManipulator.updateLabel(
status,
"Checking for update: " + d.getProgressString() +
((percent >= 0) ? " (" + percent + "%)" : ""));
}
if (d.isCompleted())
{
d.waitUntilCompleted();
break;
}
Debug.sleep(REFRESH_INTERVAL_MILLISECONDS);
}
final String[] lines;
synchronized (sb)
{
lines = sb.toString().split("[\n\r\u0085\u2028\u2028]++");
}
/* process update information */
SwingManipulator.updateLabel(status, "Checking for update...");
for (String s : lines)
{
if (s.isEmpty() || s.startsWith("#"))
{
/* ignore empty lines and comments */
}
else if (s.contains(":"))
{
/* process "key:value" pair */
final String[] kv = StringManipulator.parseKeyValueString(s);
properties.setString(kv[0], kv[1]);
}
}
}
catch (Exception e)
{
if (showPromptOnError)
{
SwingManipulator.showErrorDialog(
this,
"Check for Update - " + getTitle(),
"Failed to retrieve update information from the " + properties.getString("name") +
" homepage (" + e + ").\nPlease try again later. If the problem persists, please visit the homepage manually.");
}
SwingManipulator.updateLabel(status, " ");
refreshStatus = true;
return;
}
/* show user prompt if an update is available */
try
{
final String version = properties.getString("update.version");
final String date = properties.getString("update.date");
final String download = properties.getString("update.download");
final String comment = properties.getString("update.comment");
if (properties.getString("date").compareTo(date) < 0)
{
/* new release is available */
final int choice = SwingManipulator.showOptionTextDialog(
this,
"A new release of " + properties.getString("name") + " is available",
properties.getString("name") + " " + version + " (" + date +
") is available for download. " +
((Double.parseDouble(properties.getString("version")) < Double.parseDouble(version)) ?
"This is a major update and is strongly recommended." :
"This is a minor update and is recommended if you are currently experiencing problems.") +
"\n\n" + comment,
8,
"Check for Update - " + getTitle(),
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null,
new String[] {"Download Now", "Cancel"},
0);
/* download now */
if ((choice == 0) &&
Desktop.isDesktopSupported())
{
try
{
Desktop.getDesktop().browse(new URI(download));
}
catch (Exception ex)
{
/* ignore */
}
}
}
else
{
/* this release is up-to-date */
if (showPromptIfLatest)
{
SwingManipulator.showInfoDialog(
this,
"Check for Update - " + getTitle(),
"No update found",
"This release of " + properties.getString("name") + " " + properties.getString("version") + " (" +
properties.getString("date") + ") is already up-to-date.",
5);
}
}
}
catch (Exception e)
{
/* unable to parse */
if (showPromptOnError)
{
SwingManipulator.showWarningDialog(
this,
"Check for Update - " + getTitle(),
"Failed to parse update information from the " + properties.getString("name") +
" homepage (" + e + ").\nPlease try again later. If the problem persists, please visit the homepage manually.");
}
}
SwingManipulator.updateLabel(status, " ");
refreshStatus = true;
}
/**
* Load definitions, from the homepage if possible.
* This method should run on a dedicated worker thread, not the EDT.
*/
private void loadDefinitions()
{
refreshStatus = false;
SwingManipulator.updateLabel(status, "Loading definitions...");
String content = null;
try
{
/* try to retrieve latest defintions from homepage */
final StringBuilder sb = new StringBuilder();
final Downloader d = new Downloader(
new URL(properties.getString("definitions.url")),
sb);
new Thread(d).start();
while (true)
{
if (d.isProgressUpdated())
{
final int percent = d.getProgressPercent();
SwingManipulator.updateLabel(
status,
"Loading definitions: " + d.getProgressString() +
((percent >= 0) ? " (" + percent + "%)" : ""));
}
if (d.isCompleted())
{
d.waitUntilCompleted();
break;
}
Debug.sleep(REFRESH_INTERVAL_MILLISECONDS);
}
synchronized (sb)
{
content = sb.toString();
}
}
catch (Exception e)
{
SwingManipulator.showWarningDialog(
this,
"Loading Definitions - " + getTitle(),
"Failed to retrieve the latest definitions from the " + properties.getString("name") +
" webpage (" + e + ").\nBuilt-in definitions will be used instead.");
try
{
/* try to read built-in defintions */
content = ResourceManipulator.resourceAsString(properties.getString("definitions.local"));
}
catch (Exception ex)
{
SwingManipulator.showErrorDialog(
this,
"Loading Definitions - " + getTitle(),
"(INTERNAL) Failed to read built-in definitions (" + ex + ").\nProgram terminated.");
System.exit(1);
}
}
final String[] lines = content.split("[\n\r\u0085\u2028\u2028]++");
/* process definitions */
SwingManipulator.updateLabel(status, "Loading definitions...");
final List<Definition> defs = new ArrayList<Definition>();
Definition current = null;
for (String s : lines)
{
if (s.isEmpty() || s.startsWith("#"))
{
/* ignore empty lines and comments */
}
else if (s.startsWith("[") && s.endsWith("]"))
{
/* start collecting mappings for a new definition */
current = new Definition(s.substring(1, s.length() - 1).trim());
defs.add(current);
}
else if (s.contains(":"))
{
/* process "key:value" pair */
final String[] kv = StringManipulator.parseKeyValueString(s);
if (current == null)
{
/* meta-data about the definitions */
properties.setString(kv[0], kv[1]);
}
else
{
/* add mapping to current definition */
current.put(kv[0], kv[1]);
}
}
}
definitions = defs;
SwingManipulator.updateLabel(status, " ");
refreshStatus = true;
}
/**
* Parse the displayed webpage URL.
* This method must run on the EDT.
*/
private void parseWebpage()
{
final String u = urlText.getText().replace("\n", "").trim();
if (u.isEmpty())
{
return;
}
final URL url;
try
{
if (u.contains("://"))
{
url = new URL(u);
}
else
{
/* assume HTTP protocol */
url = new URL("http://" + u);
}
}
catch (Exception e)
{
/* invalid URL */
SwingManipulator.showErrorDialog(
this,
properties.getString("name"),
"Invalid webpage URL.");
return;
}
try
{
/* start new thread for webpage parser */
final WebpageParser parser = new WebpageParser(this, url);
parser.setVisible(true);
parser.toFront();
parsersThreadPool.submit(parser);
urlText.setText("");
}
catch (UnsupportedOperationException e)
{
SwingManipulator.showErrorDialog(
this,
properties.getString("name"),
properties.getString("name") + " does not know how to parse this webpage.");
}
}
/**
* Close the program.
*/
private void closeProgram()
{
/* count number of active downloads */
int numActiveDownloads = 0;
synchronized (downloads)
{
for (Download d : downloads)
{
if (!d.isCompleted())
{
numActiveDownloads++;
}
}
}
if (numActiveDownloads > 0)
{
/* prompt user about active downloads */
final int choice = JOptionPane.showConfirmDialog(
MediaSniper.this,
"There" +
((numActiveDownloads == 1) ? " is" : " are") +
" still " + numActiveDownloads + " active" +
((numActiveDownloads == 1) ? " download" : " downloads") +
". Exit now?",
"Confirm Exit - " + properties.getString("name"),
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (choice != JOptionPane.YES_OPTION)
{
return;
}
}
/* count number of active parsers */
final int numActiveParsers;
synchronized (parsers)
{
numActiveParsers = parsers.size();
}
if (numActiveParsers > 0)
{
/* prompt user about active parsers */
final int choice = JOptionPane.showConfirmDialog(
MediaSniper.this,
"There" +
((numActiveParsers == 1) ? " is" : " are") +
" still " + numActiveParsers + " active webpage" +
((numActiveParsers == 1) ? " parser" : " parsers") +
". Exit now?",
"Confirm Exit - " + properties.getString("name"),
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (choice != JOptionPane.YES_OPTION)
{
return;
}
}
setVisible(false);
dispose();
System.exit(0);
}
/**
* Minimize the program, to the tray if necessary.
* This method must run on the EDT.
*/
private void minimizeProgram()
{
if (properties.getBoolean("minimize.to.tray") &&
(trayIcon != null))
{
try
{
SystemTray.getSystemTray().add(trayIcon);
setVisible(false);
}
catch (Exception e)
{
/* ignore */
}
}
setExtendedState(JFrame.ICONIFIED);
final JFrame[] frames = new JFrame[]
{
about
};
for (JFrame f : frames)
{
if (f != null)
{
f.setVisible(false);
}
}
/* minimize frame of webpage parsers */
synchronized (parsers)
{
for (WebpageParser parser : parsers)
{
parser.setVisible(false);
}
}
}
/**
* Restore the program from the tray.
* This method must run on the EDT.
*/
private void restoreProgram()
{
setVisible(true);
/* restore frame of opened webpage parsers */
synchronized (parsers)
{
for (WebpageParser parser : parsers)
{
parser.setVisible(true);
}
}
if (trayIcon != null)
{
try
{
SystemTray.getSystemTray().remove(trayIcon);
}
catch (Exception e)
{
/* ignore */
}
}
setExtendedState(JFrame.NORMAL);
toFront();
}
/**
* Refresh the specified download on the table.
*
* @param d
* download to be refreshed
*/
void refreshDownloadOnTable(
final Download d)
{
synchronized (downloads)
{
final int i = downloads.indexOf(d);
if (i >= 0)
{
downloadsTableModel.fireTableRowsUpdated(i, i);
}
}
}
/**
* Add the specified download.
*
* @param d
* download to be added
*/
void addDownload(
final Download d)
{
synchronized (downloads)
{
downloads.add(d);
}
downloadsThreadPool.submit(d);
downloadsTableModel.fireTableDataChanged();
}
/**
* Add the specified webpage parser.
*
* @param p
* parser to be added
*/
void addParser(
final WebpageParser p)
{
synchronized (parsers)
{
parsers.add(p);
}
}
/**
* Remove the specified webpage parser.
*
* @param p
* parser to be removed
*/
void removeParser(
final WebpageParser p)
{
synchronized (parsers)
{
parsers.remove(p);
}
}
/**
* Refresh the "Always on Top" mode of the program.
* This method must run on the EDT.
*/
private void refreshAlwaysOnTopMode()
{
final boolean state = properties.getBoolean("always.on.top");
final JFrame[] frames = new JFrame[]
{
this,
about
};
for (JFrame f : frames)
{
if (f != null)
{
try
{
f.setAlwaysOnTop(state);
}
catch (Exception ee)
{
/* ignore */
}
}
}
synchronized (parsers)
{
/* propagate mode to other forms */
for (WebpageParser p : parsers)
{
try
{
p.setAlwaysOnTop(state);
}
catch (Exception ee)
{
/* ignore */
}
}
}
}
/**
* Refresh SOCKS proxy mode.
*/
private void refreshProxyMode()
{
if (properties.getBoolean("proxy"))
{
Debug.setSystemProperty("socksProxyHost", properties.getString("proxy.host"));
Debug.setSystemProperty("socksProxyPort", properties.getString("proxy.port"));
Debug.setSystemProperty("java.net.socks.username", properties.getString("proxy.username"));
Debug.setSystemProperty("java.net.socks.password", properties.getString("proxy.password"));
}
else
{
Debug.setSystemProperty("socksProxyHost", "");
Debug.setSystemProperty("socksProxyPort", "");
Debug.setSystemProperty("java.net.socks.username", "");
Debug.setSystemProperty("java.net.socks.password", "");
}
}
/**
* Refresh download directory.
*/
private void refreshDownloadDirectory()
{
try
{
properties.setFile("download.directory", properties.getFile("download.directory").getCanonicalFile());
}
catch (Exception e)
{
try
{
properties.setFile("download.directory", new File(".").getCanonicalFile());
}
catch (Exception ex)
{
try
{
properties.setFile("download.directory", new File(System.getProperty("user.dir")).getCanonicalFile());
}
catch (Exception exx)
{
/* ignore */
}
}
}
}
/*****************
* INNER CLASSES *
*****************/
/**
* Represent the model for the table of media file downloads.
*/
private class DownloadsTableModel
extends AbstractTableModel
{
/** name of each column (headers) */
private final String[] columnNames =
{
"Title",
"Size",
"Progress"
};
/** class of each column */
private final Class[] columnClasses =
{
TableStringCell.class,
TableStringCell.class,
TableProgressCell.class
};
/** cached copy of the table cell contents, for rendering */
private final Map<Download,Object[]> contents = new HashMap<Download,Object[]>();
@Override
public int getRowCount()
{
synchronized (downloads)
{
return downloads.size();
}
}
@Override
public int getColumnCount()
{
return columnNames.length;
}
@Override
public String getColumnName(
int col)
{
return columnNames[col];
}
@Override
public Class getColumnClass(
int col)
{
return columnClasses[col];
}
@Override
public Object getValueAt(
int row,
int col)
{
Download d;
synchronized (downloads)
{
d = downloads.get(row);
}
Object[] c = contents.get(d);
if (c == null)
{
c = new Object[]
{
new TableStringCell(d),
new TableStringCell(d),
new TableProgressCell(d)
};
contents.put(d, c);
/* initialize invariant properties of the cell contents */
/* Title */
((TableStringCell) c[0]).align = JLabel.LEFT;
/* Size */
((TableStringCell) c[1]).align = JLabel.RIGHT;
}
switch (col)
{
case 0:
/* Title */
((TableStringCell) c[0]).text = "<html>" + d.getTitle() + "</html>";
break;
case 1:
/* Size */
((TableStringCell) c[1]).value = d.getLength();
((TableStringCell) c[1]).text = d.getLengthString();
break;
case 2:
/* Progress */
((TableProgressCell) c[2]).percent = d.getProgressPercent();
((TableProgressCell) c[2]).text = d.getProgressString();
break;
default:
return null;
}
return c[col];
}
}
/***************************
* NETBEANS-GENERATED CODE *
***************************/
/** 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.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
status = new javax.swing.JLabel();
urlPanel = new javax.swing.JPanel();
buttonsPanel = new javax.swing.JPanel();
goButton = new javax.swing.JButton();
pasteButton = new javax.swing.JButton();
clearButton = new javax.swing.JButton();
urlPane = new javax.swing.JScrollPane();
urlText = new javax.swing.JTextArea();
downloadsPanel = new javax.swing.JPanel();
downloadsPane = new javax.swing.JScrollPane();
downloadsTable = new javax.swing.JTable();
menuBar = new javax.swing.JMenuBar();
fileMenu = new javax.swing.JMenu();
exitItem = new javax.swing.JMenuItem();
optionsMenu = new javax.swing.JMenu();
topItem = new javax.swing.JCheckBoxMenuItem();
trayItem = new javax.swing.JCheckBoxMenuItem();
optionsSeparator = new javax.swing.JSeparator();
restrictedItem = new javax.swing.JCheckBoxMenuItem();
shortItem = new javax.swing.JCheckBoxMenuItem();
proxyItem = new javax.swing.JCheckBoxMenuItem();
helpMenu = new javax.swing.JMenu();
usageItem = new javax.swing.JMenuItem();
aboutItem = new javax.swing.JMenuItem();
checkItem = new javax.swing.JMenuItem();
setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
status.setText(" ");
getContentPane().add(status, java.awt.BorderLayout.PAGE_END);
urlPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Webpage URL"));
urlPanel.setLayout(new java.awt.BorderLayout());
buttonsPanel.setLayout(new java.awt.GridLayout(3, 1));
goButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/freeshell/zs/mediasniper/resources/wand.png"))); // NOI18N
goButton.setMnemonic('g');
goButton.setText("Go!");
goButton.setToolTipText("Parse this webpage URL");
goButton.setHorizontalAlignment(javax.swing.SwingConstants.LEADING);
buttonsPanel.add(goButton);
pasteButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/freeshell/zs/mediasniper/resources/page_paste.png"))); // NOI18N
pasteButton.setMnemonic('p');
pasteButton.setText("Paste and Go!");
pasteButton.setToolTipText("Paste the webpage URL from the clipboard and parse it");
pasteButton.setHorizontalAlignment(javax.swing.SwingConstants.LEADING);
buttonsPanel.add(pasteButton);
clearButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/freeshell/zs/mediasniper/resources/bin_closed.png"))); // NOI18N
clearButton.setMnemonic('c');
clearButton.setText("Clear");
clearButton.setToolTipText("Clear this webpage URL");
clearButton.setHorizontalAlignment(javax.swing.SwingConstants.LEADING);
buttonsPanel.add(clearButton);
urlPanel.add(buttonsPanel, java.awt.BorderLayout.LINE_END);
urlPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
urlText.setColumns(20);
urlText.setLineWrap(true);
urlText.setRows(5);
urlPane.setViewportView(urlText);
urlPanel.add(urlPane, java.awt.BorderLayout.CENTER);
getContentPane().add(urlPanel, java.awt.BorderLayout.PAGE_START);
downloadsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Media File Downloads"));
downloadsPane.setPreferredSize(new java.awt.Dimension(500, 250));
downloadsTable.setAutoCreateRowSorter(true);
downloadsTable.setModel(downloadsTableModel);
downloadsPane.setViewportView(downloadsTable);
javax.swing.GroupLayout downloadsPanelLayout = new javax.swing.GroupLayout(downloadsPanel);
downloadsPanel.setLayout(downloadsPanelLayout);
downloadsPanelLayout.setHorizontalGroup(
downloadsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(downloadsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 474, Short.MAX_VALUE)
);
downloadsPanelLayout.setVerticalGroup(
downloadsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(downloadsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 195, Short.MAX_VALUE)
);
getContentPane().add(downloadsPanel, java.awt.BorderLayout.CENTER);
fileMenu.setMnemonic('f');
fileMenu.setText("File");
exitItem.setMnemonic('x');
exitItem.setText("Exit");
fileMenu.add(exitItem);
menuBar.add(fileMenu);
optionsMenu.setMnemonic('o');
optionsMenu.setText("Options");
topItem.setMnemonic('t');
topItem.setSelected(true);
topItem.setText("Always on Top");
optionsMenu.add(topItem);
trayItem.setMnemonic('m');
trayItem.setSelected(true);
trayItem.setText("Minimize to Tray");
optionsMenu.add(trayItem);
optionsMenu.add(optionsSeparator);
restrictedItem.setMnemonic('r');
restrictedItem.setSelected(true);
restrictedItem.setText("Restricted ASCII Filenames");
optionsMenu.add(restrictedItem);
shortItem.setMnemonic('s');
shortItem.setSelected(true);
shortItem.setText("Short (8.3) Filenames");
optionsMenu.add(shortItem);
proxyItem.setSelected(true);
proxyItem.setText("SOCKS Proxy");
optionsMenu.add(proxyItem);
menuBar.add(optionsMenu);
helpMenu.setMnemonic('h');
helpMenu.setText("Help");
usageItem.setMnemonic('u');
usageItem.setText("Usage...");
helpMenu.add(usageItem);
aboutItem.setMnemonic('a');
aboutItem.setText("About...");
helpMenu.add(aboutItem);
checkItem.setMnemonic('c');
checkItem.setText("Check for Update");
helpMenu.add(checkItem);
menuBar.add(helpMenu);
setJMenuBar(menuBar);
pack();
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem aboutItem;
private javax.swing.JPanel buttonsPanel;
private javax.swing.JMenuItem checkItem;
private javax.swing.JButton clearButton;
private javax.swing.JScrollPane downloadsPane;
private javax.swing.JPanel downloadsPanel;
private javax.swing.JTable downloadsTable;
private javax.swing.JMenuItem exitItem;
private javax.swing.JMenu fileMenu;
private javax.swing.JButton goButton;
private javax.swing.JMenu helpMenu;
private javax.swing.JMenuBar menuBar;
private javax.swing.JMenu optionsMenu;
private javax.swing.JSeparator optionsSeparator;
private javax.swing.JButton pasteButton;
private javax.swing.JCheckBoxMenuItem proxyItem;
private javax.swing.JCheckBoxMenuItem restrictedItem;
private javax.swing.JCheckBoxMenuItem shortItem;
private javax.swing.JLabel status;
private javax.swing.JCheckBoxMenuItem topItem;
private javax.swing.JCheckBoxMenuItem trayItem;
private javax.swing.JScrollPane urlPane;
private javax.swing.JPanel urlPanel;
private javax.swing.JTextArea urlText;
private javax.swing.JMenuItem usageItem;
// End of variables declaration//GEN-END:variables
}