/*******************************************************************************
* HelloNzb -- The Binary Usenet Tool
* Copyright (C) 2010-2013 Matthias F. Brandstetter
*
* 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 me.mabra.hellonzb;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import me.mabra.hellonzb.listener.DownloadFileListKeyListener;
import me.mabra.hellonzb.listener.DownloadFileListPopupListener;
import me.mabra.hellonzb.listener.NzbFileListPopupListener;
import me.mabra.hellonzb.listener.actions.*;
import me.mabra.hellonzb.nntpclient.BackupFileDownloader;
import me.mabra.hellonzb.nntpclient.NntpFileDownloader;
import me.mabra.hellonzb.nntpclient.nioengine.NettyNioClient;
import me.mabra.hellonzb.parser.NzbParser;
import me.mabra.hellonzb.preferences.HelloNzbPreferences;
import me.mabra.hellonzb.renderer.AlignedTableHeaderRenderer;
import me.mabra.hellonzb.renderer.MyTableCellRenderer;
import me.mabra.hellonzb.renderer.ProgressRenderer;
import me.mabra.hellonzb.tablemodels.FilesToDownloadTableModel;
import me.mabra.hellonzb.tablemodels.NzbFileQueueTableModel;
import me.mabra.hellonzb.tablemodels.TableRowTransferHandler;
import me.mabra.hellonzb.tablemodels.ThreadViewTableModel;
import me.mabra.hellonzb.util.*;
import javax.swing.*;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.table.TableColumn;
import javax.xml.stream.XMLStreamException;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Vector;
/**
* This is the base class of the main application class HelloNzb.
* It contains private/protected members and methods for this main
* application class. These are mainly "background" and low-level
* functionalities.
*
* @author Matthias F. Brandstetter
*/
public abstract class HelloNzbCradle implements HelloNzbConstants
{
/** The main JFrame object of the application window */
protected JFrame jframe;
/** The menu bar object */
protected JMenuBar menuBar;
/** The tool bar object */
protected JToolBar toolBar;
/** download speed history graph */
protected SpeedGraphPanel speedGraph;
/** "start" toggle button on toolbar */
protected JToggleButton startToggleButton;
/** "pause" toggle button on toolbar */
protected JToggleButton pauseToggleButton;
/** "web access" toggle button on toolbar */
protected JToggleButton webAccessToggleButton;
/** "shutdown" toggle button on toolbar */
protected JToggleButton shutdownToggleButton;
/** Menu and toolbar items (actions) */
protected HashMap<String,AbstractAction> actions;
/** A JPanel representing the status bar of the window */
protected JPanel statusBarPanel;
/** A text element for the status bar */
protected JLabel statusBarText;
protected JLabel memoryUsage;
/** download speed label */
protected JLabel currDlSpeed;
/** limit download speed button */
protected JButton limitDlSpeedButton;
/** A text element for the ETA and total file size label on the status bar */
protected JLabel etaAndTotalText;
/** The list of nzb's to process on the left side of the main split pane */
protected JTable nzbListTab;
/** A list of all connections in main window */
protected JTable threadViewTab;
/** left/right split pane */
protected JSplitPane lrSplitPane;
/** up/down split pane */
protected JSplitPane udSplitPane;
/** The table model for the left JTable */
protected NzbFileQueueTableModel nzbFileQueueTabModel;
/** The list of files to download for the selected nzb in the left nzb list */
protected JTable filesToDownloadTab;
/** Default NIO client */
protected NettyNioClient nioClient;
/** Alternative NIO client */
protected NettyNioClient backupNioClient;
/** A pointer to the file downloader currently active */
protected NntpFileDownloader currentFileDownloader;
/** A pointer to the file alternative downloader currently active */
protected BackupFileDownloader backupFileDownloader;
/** A pointer to the nzb parser currently active */
protected NzbParser currentNzbParser;
/** the total amount of bytes loaded for the current file download */
protected long totalBytesLoaded;
/** The table model for the right JTable */
protected FilesToDownloadTableModel filesToDownloadTabModel;
/** Application preferences container (incl. preferences dialog) */
protected HelloNzbPreferences prefContainer;
/** background worker for misc. background tasks */
protected BackgroundWorker bgWorker;
/** task manager responsible for controlling the background progress bar */
protected TaskManager taskMgr;
/** last known count of active threads */
protected int lastActThreadCount;
/** String localer */
protected StringLocaler localer;
/** cental logger object */
protected MyLogger logger;
/** logging window */
protected LoggingWindow logWnd;
/** the system tray icon object */
protected MyTrayIcon trayIcon;
/** if set to true then we can start another parser content saver */
protected Boolean contentSaverDone;
/** popup window of the NZB file queue */
protected JPopupMenu leftPopup;
/** popup window of the download file queue */
protected JPopupMenu rightPopup;
/** Class constructor. */
public HelloNzbCradle(JFrame frame, String title)
{
this.jframe = frame;
// String localer and JVM default locale
this.localer = new StringLocaler();
Locale.setDefault(localer.getLocale());
JOptionPane.setDefaultLocale(localer.getLocale());
JFileChooser.setDefaultLocale(localer.getLocale());
// create logging window and logger
logWnd = new LoggingWindow(localer, frame);
this.logger = MyLogger.getInstance(this, logWnd.getTextArea());
// set application logo/icon
URL url = ClassLoader.getSystemResource("resources/icons/HelloNzb_logo.png");
Toolkit kit = Toolkit.getDefaultToolkit();
Image img = kit.createImage(url);
this.jframe.setIconImage(img);
// set the (default) settings for the main JFrame
this.jframe.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.jframe.setSize(MAINWIN_WIDTH, MAINWIN_HEIGHT);
this.jframe.setLocationRelativeTo(null);
this.jframe.setLayout(new BorderLayout());
// create data for nzb file queue and file download queue
this.nzbFileQueueTabModel = new NzbFileQueueTableModel(localer);
this.filesToDownloadTabModel = new FilesToDownloadTableModel(localer);
// dynamically set the widgets while window size is changed
Toolkit.getDefaultToolkit().setDynamicLayout(true);
// some default values
this.currentFileDownloader = null;
this.backupFileDownloader = null;
this.currentNzbParser = null;
this.lastActThreadCount = 0;
this.totalBytesLoaded = 0;
this.contentSaverDone = true;
this.leftPopup = null;
this.rightPopup = null;
this.prefContainer = null;
}
/**
* Initialise the system tray icon.
*/
protected void initSystemTray(AbstractAction quitAction)
{
// system tray activate in preferences?
boolean isActive = prefContainer.getBooleanPrefValue("GeneralSettingsShowTrayIcon");
if(!isActive)
{
trayIcon = null;
return;
}
// system tray supported by OS?
if(!SystemTray.isSupported())
{
trayIcon = null;
return;
}
trayIcon = new MyTrayIcon(this, "HelloNzb -- The Binary Usenet Tool", actions);
SystemTray tray = SystemTray.getSystemTray();
try
{
tray.add(trayIcon);
}
catch(Exception ex)
{
logger.printStackTrace(ex);
trayIcon = null;
}
}
/**
* Used by the constructor to create the content panes on the main window.
*
* These are:
* - headerPanel (NORTH)
* - splitPane (CENTER, local object in this method)
* - statusBarPanel (SOUTH)
*
* The split pane in the CENTER of the window contains two scrollable panes:
* - nzbList (LEFT, list of all nzb's, the queue of nzb's to download)
* - filesToDownload (RIGHT, list of all files within the selected nzb)
*
* @param progBar The previously create JProgressBar object
*/
protected QuitAction addContentPanes(JProgressBar progBar)
{
// create spacer panels
jframe.add(new JPanel(), BorderLayout.WEST);
jframe.add(new JPanel(), BorderLayout.EAST);
// create menu and tool bars
actions = new HashMap<String,AbstractAction>();
QuitAction quitAction = createMenuAndToolBars();
// header panel -- icons and speed graph
JPanel headerPanel = new JPanel();
headerPanel.setLayout(new BoxLayout(headerPanel, BoxLayout.LINE_AXIS));
headerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 12));
headerPanel.add(toolBar);
headerPanel.add(Box.createHorizontalGlue());
// text area and button left of the speed graph
JPanel tmpPanel = new JPanel();
tmpPanel.setLayout(new BoxLayout(tmpPanel, BoxLayout.PAGE_AXIS));
limitDlSpeedButton = new JButton();
limitDlSpeedButton.setAlignmentX(Component.RIGHT_ALIGNMENT);
limitDlSpeedButton.setAction(actions.get("MenuHelloNzbSpeedLimit"));
limitDlSpeedButton.setToolTipText(localer.getBundleText("MenuHelloNzbSpeedLimit"));
setConnSpeedLimit();
currDlSpeed = new JLabel(HelloNzbToolkit.prettyPrintBps(0));
currDlSpeed.setAlignmentX(Component.RIGHT_ALIGNMENT);
tmpPanel.add(currDlSpeed);
tmpPanel.add(Box.createRigidArea(new Dimension(0, 10)));
tmpPanel.add(limitDlSpeedButton);
tmpPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
headerPanel.add(tmpPanel);
// speed graph
speedGraph = new SpeedGraphPanel(200);
headerPanel.add(speedGraph);
jframe.add(headerPanel, BorderLayout.NORTH);
// create left and right tables
createDataTables();
// restore window size
String savedLrDivider = this.prefContainer.getPrefValue("AutoSettingsLrDivider");
String savedUdDivider = this.prefContainer.getPrefValue("AutoSettingsUdDivider");
// create a split pane for the download queues (left/right)
this.lrSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
new JScrollPane(nzbListTab), new JScrollPane(filesToDownloadTab));
lrSplitPane.setContinuousLayout(true);
if(savedLrDivider.isEmpty())
lrSplitPane.setDividerLocation((int) (jframe.getWidth() / 3));
else
lrSplitPane.setDividerLocation(Integer.valueOf(savedLrDivider));
// create a scroll pane for the thread view
JScrollPane threadView = createThreadViewPane();
// create a split pane for the main split pane and the thread view (up/down)
this.udSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, lrSplitPane, threadView);
udSplitPane.setContinuousLayout(true);
if(savedUdDivider.isEmpty())
udSplitPane.setDividerLocation((int) (jframe.getHeight() * 0.5));
else
udSplitPane.setDividerLocation(Integer.valueOf(savedUdDivider));
jframe.add(udSplitPane, BorderLayout.CENTER);
// create status bar
statusBarPanel = createStatusBar(progBar);
jframe.add(statusBarPanel, BorderLayout.SOUTH);
// return QuitAction object to use for the window listener
return quitAction;
}
/**
* Create the status bar inclusive all contents.
*
* @param progBar The previously create JProgressBar object
* @return The newly created JPanel object
*/
protected JPanel createStatusBar(JProgressBar progBar)
{
// create layout for this tab/panel
FormLayout layout = new FormLayout(
"pref, 10dlu:grow, pref, 10dlu:grow, pref, 10dlu, [100dlu,pref,150dlu]", // cols
"p"); // rows
// create builder
PanelBuilder builder = new PanelBuilder(layout);
builder.setDefaultDialogBorder();
CellConstraints cc = new CellConstraints();
// left status bar text field
statusBarText = new JLabel(localer.getBundleText("StatusBarRunningThreads") + " 0");
builder.add(statusBarText, cc.xy(1, 1));
// Memory usage label
memoryUsage = new JLabel("");
builder.add(memoryUsage, cc.xy(3, 1));
// ETA & total file size text field
etaAndTotalText = new JLabel("");
builder.add(etaAndTotalText, cc.xy(5, 1));
etaAndTotalText.setToolTipText(
localer.getBundleText("StatusBarEtaAndTotalTooltip"));
// create background task progress bar in status bar
builder.add(progBar, cc.xy(7, 1));
return builder.getPanel();
}
/**
* Creates the table for the thread view and adds it to a scroll pane.
*
* @return The newly created scroll pane
*/
protected JScrollPane createThreadViewPane()
{
// create table data and table object
int threadcount = 1;
String tmp = prefContainer.getPrefValue("ServerSettingsThreadCount");
if(tmp.length() > 0)
threadcount = Integer.valueOf(tmp);
ThreadViewTableModel tm = new ThreadViewTableModel(localer);
tm.setRowCount(threadcount);
JTable table = new JTable(tm);
// set center alignment for the contents of the first colunn
table.getColumnModel().getColumn(0).setCellRenderer(new MyTableCellRenderer(SwingConstants.CENTER));
table.getColumnModel().getColumn(1).setCellRenderer(new MyTableCellRenderer());
// set header renderers
table.getColumnModel().getColumn(0).setHeaderRenderer(new AlignedTableHeaderRenderer(SwingConstants.CENTER));
table.getColumnModel().getColumn(1).setHeaderRenderer(new AlignedTableHeaderRenderer(SwingConstants.LEFT));
// misc. table settings
table.setRowSelectionAllowed(false);
table.setColumnSelectionAllowed(false);
table.setCellSelectionEnabled(false);
table.setDragEnabled(false);
table.getTableHeader().setReorderingAllowed(false);
TableColumn column = table.getColumnModel().getColumn(1);
column.setPreferredWidth((int) (jframe.getWidth() * 0.66));
// set table header height
table.getTableHeader().setPreferredSize(new Dimension(table.getColumnModel().getTotalColumnWidth(), 25));
this.threadViewTab = table;
return new JScrollPane(table);
}
/**
* Used by the constructor to create the menu bar for the main window.
*/
protected QuitAction createMenuAndToolBars()
{
menuBar = new JMenuBar();
toolBar = new JToolBar();
Icon icon = null;
toolBar.setFloatable(false);
/////////////////////////////////////////////////////////////////////
// create "HelloNzb" menu (main menu, like "File")
JMenu helloNzbMenu = new JMenu(localer.getBundleText("MenuHelloNzb"));
menuBar.add(helloNzbMenu);
// open nzb file action
icon = new ImageIcon(HelloNzb.class.getResource("/resources/icons/open_nzb.png"));
OpenNzbFileAction openNzbFileAction = new OpenNzbFileAction(this, icon, "MenuHelloNzbOpenNzbFile");
helloNzbMenu.add(openNzbFileAction);
actions.put("MenuHelloNzbOpenNzbFile", openNzbFileAction);
// preferences popup
icon = new ImageIcon(HelloNzb.class.getResource("/resources/icons/preferences.png"));
PrefAction prefAction = new PrefAction(this, icon, "MenuHelloNzbPref");
helloNzbMenu.add(prefAction);
actions.put("MenuHelloNzbPref", prefAction);
helloNzbMenu.addSeparator();
// shutdown computer after download has finished
icon = new ImageIcon(HelloNzb.class.getResource("/resources/icons/shutdown.png"));
Icon iconActive = new ImageIcon(HelloNzb.class.getResource("/resources/icons/shutdown_active.png"));
ShutdownAction shutdownAction = new ShutdownAction(this, icon, iconActive, "MenuHelloNzbShutdown");
actions.put("MenuHelloNzbShutdown", shutdownAction);
// check for program updates
icon = null;
CheckForUpdateAction updateAction = new CheckForUpdateAction(this, icon, "MenuHelloNzbUpdate");
if(BETA_VERSION) updateAction.setEnabled(false);
helloNzbMenu.add(updateAction);
actions.put("MenuHelloNzbUpdate", updateAction);
// show program log
icon = null;
ShowLoggingWindowAction showLogAction = new ShowLoggingWindowAction(this, icon, "MenuHelloNzbShowLog", logWnd);
helloNzbMenu.add(showLogAction);
actions.put("MenuHelloNzbShowLog", updateAction);
helloNzbMenu.addSeparator();
// quit application
icon = new ImageIcon(HelloNzb.class.getResource("/resources/icons/quit.png"));
QuitAction quitAction = new QuitAction(this, icon, "MenuHelloNzbQuit", bgWorker, taskMgr);
helloNzbMenu.add(quitAction);
actions.put("MenuHelloNzbQuit", quitAction);
/////////////////////////////////////////////////////////////////////
// create "Download" menu (timed downloads, ...)
JMenu downloadMenu = new JMenu(localer.getBundleText("MenuDownload"));
menuBar.add(downloadMenu);
downloadMenu.addMenuListener(new MenuListener()
{
@Override
public void menuSelected(MenuEvent e)
{
TimedDownloadsAction action = (TimedDownloadsAction) actions.get("MenuDownloadTimedDownloads");
String startTime = getPrefValue("DownloadSettingsTimedDownloadStart");
if(startTime == null || startTime.isEmpty())
{
action.setEnabled(false);
return;
}
String stopTime = getPrefValue("DownloadSettingsTimedDownloadStop");
if(stopTime == null || stopTime.isEmpty())
{
action.setEnabled(false);
return;
}
action.setEnabled(true);
}
@Override
public void menuDeselected(MenuEvent e)
{
}
@Override
public void menuCanceled(MenuEvent e)
{
}
});
// timed downloads
icon = null;
TimedDownloadsAction timedDownloadsAction = new TimedDownloadsAction(this, icon, "MenuDownloadTimedDownloads");
JCheckBoxMenuItem timedDownloads = new JCheckBoxMenuItem(timedDownloadsAction);
downloadMenu.add(timedDownloads);
actions.put("MenuDownloadTimedDownloads", timedDownloadsAction);
/////////////////////////////////////////////////////////////////////
// create "Server" menu (connect, disconnect, ...)
JMenu serverMenu = new JMenu(localer.getBundleText("MenuServer"));
menuBar.add(serverMenu);
// start download
icon = new ImageIcon(HelloNzb.class.getResource("/resources/icons/start_download.png"));
StartDownloadAction startDownloadAction =
new StartDownloadAction(this, icon, "MenuServerStartDownload");
actions.put("MenuServerStartDownload", startDownloadAction);
// pause download
icon = new ImageIcon(HelloNzb.class.getResource("/resources/icons/pause_download.png"));
PauseDownloadAction pauseDownloadAction =
new PauseDownloadAction(this, icon, "MenuServerPauseDownload");
actions.put("MenuServerPauseDownload", pauseDownloadAction);
// test server connection
icon = null;
ConnectAction connectAction = new ConnectAction(this, icon, "MenuServerConnect");
serverMenu.add(connectAction);
actions.put("MenuServerConnect", connectAction);
/////////////////////////////////////////////////////////////////////
// create "Web Access" menu (built-in HTTP(S) server)
JMenu webAccessMenu = null;
WebAccessAction webAccessAction = null;
if(WEB_ACCESS)
{
// webAccessMenu = new JMenu(localer.getBundleText("MenuWebAccess"));
// menuBar.add(webAccessMenu);
// start/stop HTTP(S) server
icon = new ImageIcon(HelloNzb.class.getResource("/resources/icons/web_interface.png"));
webAccessAction = new WebAccessAction(this, icon, "MenuWebAccessStartStop");
// webAccessMenu.add(webAccessAction);
actions.put("MenuWebAccessStartStop", webAccessAction);
}
/////////////////////////////////////////////////////////////////////
// create "Help" menu (help, about, ...)
JMenu helpMenu = new JMenu(localer.getBundleText("MenuHelp"));
menuBar.add(helpMenu);
// for help menu first check whether Deskop operations are supported on current platform
try
{
@SuppressWarnings("unused")
Desktop desktop = Desktop.getDesktop();
// get help (open browser window)
icon = null;
GetHelpAction getHelpAction = new GetHelpAction(this, icon, "MenuHelpGetHelp");
helpMenu.add(getHelpAction);
actions.put("MenuHelpGetHelp", getHelpAction);
// report a bug (open browser window)
icon = null;
ReportBugAction reportBugAction = new ReportBugAction(this, icon, "MenuHelpReportBug");
helpMenu.add(reportBugAction);
actions.put("MenuHelpReportBug", reportBugAction);
// donate to HelloNzb (open browser window)
icon = null;
DonateToHelloNzbAction donateAction = new DonateToHelloNzbAction(this, icon, "MenuHelpDonateToHelloNzb");
helpMenu.add(donateAction);
actions.put("MenuHelpDonateToHelloNzb", donateAction);
}
catch(UnsupportedOperationException ex) { }
// about HelloNzb
icon = null;
AboutHelloNzbAction aboutAction = new AboutHelloNzbAction(this, icon, "MenuHelpAbout");
helpMenu.add(aboutAction);
actions.put("MenuHelpAbout", aboutAction);
// set the new menu bar to the main window
jframe.setJMenuBar(menuBar);
/////////////////////////////////////////////////////////////////////
// create tool bar
// load NZB file
JButton button = new JButton(openNzbFileAction);
button.setFocusPainted(false);
button.setText(null);
toolBar.add(button);
// start toggle button
startToggleButton = new JToggleButton(startDownloadAction);
startToggleButton.setFocusPainted(false);
startToggleButton.setText(null);
startToggleButton.setToolTipText(localer.getBundleText("StartButtonTooltip"));
toolBar.add(startToggleButton);
// pause toggle button
pauseToggleButton = new JToggleButton(pauseDownloadAction);
pauseToggleButton.setFocusPainted(false);
pauseToggleButton.setText(null);
toolBar.add(pauseToggleButton);
if(WEB_ACCESS)
{
webAccessToggleButton = new JToggleButton(webAccessAction);
webAccessToggleButton.setFocusPainted(false);
webAccessToggleButton.setText(null);
toolBar.add(webAccessToggleButton);
}
// preferences
button = new JButton(prefAction);
button.setFocusPainted(false);
button.setText(null);
toolBar.add(button);
toolBar.addSeparator();
// shutdown toggle button
shutdownToggleButton = new JToggleButton(shutdownAction);
shutdownToggleButton.setFocusable(false);
shutdownToggleButton.setText(null);
toolBar.add(shutdownToggleButton);
// quit application
button = new JButton(quitAction);
button.setFocusPainted(false);
button.setText(null);
toolBar.add(button);
// speed limit popup
SpeedLimitAction speedLimitAction = new SpeedLimitAction(this, icon, "MenuHelloNzbSpeedLimit");
actions.put("MenuHelloNzbSpeedLimit", speedLimitAction);
startDownloadAction.setEnabled(false);
pauseDownloadAction.setEnabled(false);
// return QuitAction object to use for the window listener
return quitAction;
}
/**
* This method creates the data vectors for the left and right tables
* (left = nzb files, right = download file queue).
*/
protected void createDataTables()
{
DownloadFileListPopupListener dlFileListener = null;
DownloadFileListKeyListener dlFileKeyListener = null;
// create left JTable (nzb files)
nzbListTab = new JTable(nzbFileQueueTabModel)
{
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)
{
super.changeSelection(rowIndex, columnIndex, !extend, extend);
}
};
nzbListTab.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
nzbListTab.getTableHeader().setReorderingAllowed(false);
nzbListTab.addMouseListener(new NzbFileListPopupListener(this, nzbListTab));
nzbListTab.setDragEnabled(true);
nzbListTab.setDropMode(DropMode.INSERT_ROWS);
nzbListTab.setTransferHandler(new TableRowTransferHandler(nzbListTab, this));
nzbListTab.setRowHeight(23);
// set table cell renderers (left)
ProgressRenderer cellRenderer = new ProgressRenderer(true);
nzbListTab.getColumnModel().getColumn(0).setCellRenderer(cellRenderer);
// set table header renderers (left)
nzbListTab.getColumnModel().getColumn(0).setHeaderRenderer(
new AlignedTableHeaderRenderer(SwingConstants.CENTER));
// create right JTable (files to download)
filesToDownloadTab = new JTable(filesToDownloadTabModel);
filesToDownloadTab.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
dlFileListener = new DownloadFileListPopupListener(this, filesToDownloadTab);
dlFileKeyListener = new DownloadFileListKeyListener(this, filesToDownloadTab);
filesToDownloadTab.addMouseListener(dlFileListener);
filesToDownloadTab.addKeyListener(dlFileKeyListener);
filesToDownloadTab.getSelectionModel().addListSelectionListener(dlFileListener);
filesToDownloadTab.getColumnModel().getColumn(0).setMinWidth(333);
filesToDownloadTab.getColumnModel().getColumn(1).setMinWidth(50);
filesToDownloadTab.getColumnModel().getColumn(2).setMinWidth(50);
filesToDownloadTab.getColumnModel().getColumn(3).setMinWidth(50);
filesToDownloadTab.getTableHeader().setReorderingAllowed(false);
// set table cell renderers (right)
filesToDownloadTab.getColumnModel().getColumn(0).setCellRenderer(new MyTableCellRenderer());
filesToDownloadTab.getColumnModel().getColumn(1).setCellRenderer(new MyTableCellRenderer(SwingConstants.CENTER));
filesToDownloadTab.getColumnModel().getColumn(2).setCellRenderer(new MyTableCellRenderer(SwingConstants.CENTER));
filesToDownloadTab.getColumnModel().getColumn(3).setCellRenderer(new ProgressRenderer(false));
// set table header renderers (right)
filesToDownloadTab.getColumnModel().getColumn(0).setHeaderRenderer(
new AlignedTableHeaderRenderer(SwingConstants.CENTER));
filesToDownloadTab.getColumnModel().getColumn(1).setHeaderRenderer(
new AlignedTableHeaderRenderer(SwingConstants.CENTER));
filesToDownloadTab.getColumnModel().getColumn(2).setHeaderRenderer(
new AlignedTableHeaderRenderer(SwingConstants.CENTER));
filesToDownloadTab.getColumnModel().getColumn(3).setHeaderRenderer(
new AlignedTableHeaderRenderer(SwingConstants.CENTER));
// set table header height
nzbListTab.getTableHeader().setPreferredSize(
new Dimension(nzbListTab.getColumnModel().getTotalColumnWidth(), 25));
filesToDownloadTab.getTableHeader().setPreferredSize(
new Dimension(filesToDownloadTab.getColumnModel().getTotalColumnWidth(), 25));
}
/**
* Set the status bar to a "Running threads: <count>" message.
*
* @param threads The count of running download threads
*/
public void updStatusBar(int threads)
{
if(pauseToggleButton.isSelected())
return;
if(!startToggleButton.isSelected() && threads >= lastActThreadCount)
return;
String text = localer.getBundleText("StatusBarRunningThreads") + " " + threads;
String tc = prefContainer.getPrefValue("ServerSettingsThreadCount");
final String statusText = text + "/" + tc;
lastActThreadCount = threads;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
statusBarText.setText(statusText);
}
} );
}
/**
* This method is called if either
* a) the last file in download queue has been downloaded, or
* b) the user has manually removed the first entry in the nzb file queue.
*
* It reads the next item in the nzb file queue, retrieves all items from
* it and updates the download file queue.
*/
protected void loadNextNzbFile()
{
if(nzbFileQueueTabModel.getRowCount() > 0)
{
// start to download first file of the next nzb file in queue
NzbParser parser = nzbFileQueueTabModel.getNzbParser(0);
if(parser != null)
{
filesToDownloadTabModel.addNzbParserContents(parser);
currentNzbParser = parser;
}
}
}
/**
* This method adds the content of a nzb file (i.e. a NzbParser object)
* to the download queue. It first checks if another nzb file with that
* name already exists in the nzb queue.
*
* @param parser The NzbParser object to add
*/
public void addNzbToQueue(NzbParser parser)
{
// check if an nzb file with this name already exists in queue
if(nzbFileQueueTabModel.containsNzb(parser))
{
String title = localer.getBundleText("PopupErrorTitle");
String msg = localer.getBundleText("PopupNzbFilenameAlreadyInQueue");
JOptionPane.showMessageDialog(null, msg, title, JOptionPane.ERROR_MESSAGE);
return;
}
// no, so add the new nzb file to the queue
nzbFileQueueTabModel.addRow(parser);
if(filesToDownloadTabModel.getRowCount() == 0)
{
if(!filesToDownloadTabModel.addNzbParserContents(parser))
logger.msg("Failed to add at least one file to the queue -- is it too large?", MyLogger.SEV_WARNING);
currentNzbParser = parser;
totalBytesLoaded = currentNzbParser.getDownloadedBytes();
}
logger.msg("Added content of " + parser.getName() + ".nzb to queue", MyLogger.SEV_INFO);
// enable toolbar and menu action
AbstractAction action = actions.get("MenuServerStartDownload");
action.setEnabled(true);
}
/**
* Load the last session (NZB files that were open).
*/
protected void loadLastSession()
{
// does data directory exists?
String datadirPath = System.getProperty("user.home") + File.separator + ".HelloNzb" + File.separator;
File datadir = new File(datadirPath);
if(!datadir.isDirectory() || !datadir.canRead() || !datadir.canExecute())
{
logger.msg("Could not load last session, specified folder is no directory or non-readable", MyLogger.SEV_DEBUG);
return;
}
// set background status bar
taskMgr.loadSession(true);
// directory exists, so load all nzb files from it
File [] files = datadir.listFiles();
File tmpFile = null;
for(File file : files)
{
try
{
// directory
if(file.isDirectory())
continue;
// nzb file?
String filename = file.getCanonicalPath();
if(!filename.substring(filename.length() - 4, filename.length()).equalsIgnoreCase(".nzb"))
{
if(file.canWrite())
{
logger.msg("Deleting non-NZB file " + file.getName(), MyLogger.SEV_INFO);
file.delete();
}
continue;
}
if(!filename.contains("-"))
{
if(file.canWrite())
{
logger.msg("Deleting NZB file without '-' (" + file.getName() + ")", MyLogger.SEV_INFO);
file.delete();
}
continue;
}
// yes, so remove index part of the filename (e.g. "1-filename.nzb")
while(!filename.startsWith("-"))
filename = filename.substring(1, filename.length());
filename = filename.substring(1, filename.length());
// tmp. rename original source file
tmpFile = new File(datadirPath + filename);
file.renameTo(tmpFile);
NzbParser parser = new NzbParser(tmpFile.getAbsolutePath(), getPrefValue("GeneralSettingsDownloadDir"));
addNzbToQueue(parser);
logger.msg("Loaded new NZB file from last session: " + parser.getName(), MyLogger.SEV_INFO);
}
catch(Exception e)
{
logger.printStackTrace(e);
}
finally
{
if(tmpFile != null && tmpFile.canWrite())
tmpFile.delete();
if(file != null && file.canWrite())
file.delete();
}
}
// unset background status bar
taskMgr.loadSession(false);
}
/**
* Update the status of one connection in the thread view table.
*
* @param conn The connection number to set (counted from 0)
* @param text The new text to set
*/
public void updThreadView(int conn, String text)
{
synchronized(threadViewTab)
{
threadViewTab.setValueAt(text, conn, 1);
}
}
/**
* Called to save the currently open and unused parser data.
*
* @param wait If true then wait until saving to disk has finished.
*/
public void saveOpenParserData(boolean wait)
{
synchronized(contentSaverDone)
{
if(contentSaverDone)
{
contentSaverDone = false;
new ParserContentSaver(this, nzbFileQueueTabModel.copyQueue(), filesToDownloadTabModel.getDownloadFileVector()).start();
}
}
if(wait)
{
while(!contentSaverDone)
{
try
{
Thread.sleep(100);
}
catch(InterruptedException ex) {}
}
}
}
/**
* Sets the contentSaverDone flag to true.
*/
public void contentSaverDone()
{
synchronized(contentSaverDone)
{
contentSaverDone = true;
}
}
/**
* Return the current contentSaverDone value (either true or false).
*/
public boolean isContentSaverDone()
{
boolean ret;
synchronized(contentSaverDone)
{
ret = contentSaverDone;
}
return ret;
}
/**
* Load the specified nzb file (passed via command line at application call).
*
* @param filename The file to load, quit if null
*/
protected void loadFileFromCmdLine(String filename)
{
// filename passed at command line?
if(filename == null)
return;
// set background status bar
taskMgr.loadNzb(true);
try
{
File file = new File(filename);
addNzbToQueue(new NzbParser(filename, getPrefValue("GeneralSettingsDownloadDir")));
if(getBooleanPrefValue("GeneralSettingsDelNzbAfterLoading"))
file.delete();
}
catch(IOException e)
{
System.err.println("Error while reading file '" + filename + "'!");
System.exit(8);
}
catch(XMLStreamException e)
{
System.err.println("Given file '" + filename + "' is not a valid NZB file!");
System.exit(8);
}
catch(java.text.ParseException e)
{
System.err.println(e.getMessage());
System.exit(8);
}
// unset background status bar
taskMgr.loadNzb(false);
}
/**
* Shutdown the HelloNzb application.
*/
public void shutdownHelloNzb()
{
final AbstractAction quitAction = actions.get("MenuHelloNzbQuit");
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
quitAction.actionPerformed(null);
}
});
}
/**
* Shutdown the computer now (after all downloads have been finished).
*/
public void shutdownNow()
{
String shutdownCmd = "";
String shutdownDir = "";
if(System.getProperty("os.name").contains("Windows"))
{
shutdownCmd = "shutdown.exe /s /f";
shutdownDir = System.getenv("SystemRoot") + "/system32";
}
else if(System.getProperty("os.name").contains("Linux"))
{
shutdownCmd = "sudo shutdown -h -q now";
shutdownDir = "/sbin";
}
File dir = new File(shutdownDir);
try
{
Thread.sleep(1000);
saveOpenParserData(true);
// execute shutdown command
taskMgr.shutdown();
bgWorker.shutdown();
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(shutdownCmd, null, dir);
StreamGobbler errGobbler = new StreamGobbler(logger, proc.getErrorStream(), "ERR");
StreamGobbler outGobbler = new StreamGobbler(logger, proc.getInputStream(), "OUT");
// fetch command's STDOUT and STDERR
errGobbler.start();
outGobbler.start();
}
catch(Exception e)
{
logger.printStackTrace(e);
}
System.exit(0);
}
/**
* Create a share memory mapping so that a second instance of this
* program can later on pass new nzb files to this first instance here.
* This method also starts a background thread that "listens" to this
* shared memory for new data (nzb file locations) to read.
*
* This method creates an instance of the BackgroundWorker thread class
* and starts this thread.
*/
protected void startBgWorker()
{
try
{
bgWorker = new BackgroundWorker(this, MEM_MAP_BUFFER_SIZE);
bgWorker.start();
}
catch(IOException e)
{
logger.printStackTrace(e);
}
}
/**
* Set the connection speed limit label on the status bar
* according to value in preferences.
*/
public void setConnSpeedLimit()
{
String pref = prefContainer.getPrefValue("DownloadSettingsMaxConnectionSpeed");
try
{
@SuppressWarnings("unused")
long tmp = Long.parseLong(pref);
}
catch(Exception ex)
{
pref = "0";
prefContainer.setSpeedLimit("0");
}
if(pref.length() == 0 || Long.valueOf(pref) == 0)
{
limitDlSpeedButton.setText(localer.getBundleText("DlSpeedLimitButton"));
}
else
{
limitDlSpeedButton.setText(
localer.getBundleText("DlSpeedLimitButton2") + " " + pref + " KB/s");
}
}
/**
* Use this method to set the ETA and total file size label.
* Also set the tooltip of the system tray icon accordingly.
*
* @param text The text to set on the label
*/
public void setEtaAndTotalLabel(final String text)
{
// set label in status bar
if(etaAndTotalText != null)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
etaAndTotalText.setText(text);
}
} );
}
// set system tray icon
if(trayIcon != null)
{
if(text == null || text.isEmpty())
trayIcon.setToolTip("HelloNzb -- " + localer.getBundleText("SystemTrayTooltipNoFiles"));
else
trayIcon.setToolTip("HelloNzb -- " + text);
}
}
/**
* Set the memory usage label in the program's status bar.
*
* @param text The text to set on the label
*/
public void setMemoryUsageLabel(final String text)
{
if(etaAndTotalText != null)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
memoryUsage.setText(text);
}
} );
}
}
/**
* Use this method to set the current download speed label.
*
* @param bps The text to set on the label
*/
public void setCurrDlSpeedLabel(final long bps)
{
if(currDlSpeed != null)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
currDlSpeed.setText(HelloNzbToolkit.prettyPrintBps(bps));
}
} );
}
}
/**
* (Un)set the system tray icon.
*
* @param flag Whether or not to set the icon
*/
public void setTrayIcon(boolean flag)
{
if(flag && trayIcon != null)
return; // yes to set, but alreay active
else if(!flag && trayIcon == null)
return; // don't set, but already non-active
else if(flag)
initSystemTray(actions.get("MenuHelloNzbQuit")); // set it
else
{
// unset it
SystemTray tray = SystemTray.getSystemTray();
tray.remove(trayIcon);
trayIcon = null;
}
}
/**
* Check whether or not the system tray icon is set for the running program.
*
* @return true if we have set the tray icon, false otherwise
*/
public boolean hasTrayIcon()
{
return (trayIcon != null) ? true : false;
}
/**
* Returns true if there is currently a download active.
*
* @return true/false
*/
public boolean isDownloadActive()
{
return (currentFileDownloader == null ? false : true);
}
/**
* Get the JFrame object, i.e. the applications main window.
*
* @return The JFrame object
*/
public JFrame getJFrame()
{
return jframe;
}
/**
* Get the specified preferences value.
*/
public String getPrefValue(String id)
{
return prefContainer.getPrefValue(id);
}
/**
* Get the specified boolean-type preferences value.
*/
public boolean getBooleanPrefValue(String id)
{
return prefContainer.getBooleanPrefValue(id);
}
/**
* Get the applications preferences container.
*
* @return The HelloNzbPreferences object
*/
public HelloNzbPreferences getPrefContainer()
{
return prefContainer;
}
/**
* Get the menu bar object of the main application window.
*
* @return The JMenuBar object
*/
public JMenuBar getMenu()
{
return menuBar;
}
/**
* Get the tool bar object of the main application window.
*
* @return The JToolBar object
*/
public JToolBar getToolBar()
{
return toolBar;
}
/**
* Return the count of files in the download queue.
*
* @return The count of files
*/
public int getDownloadFileCount()
{
return filesToDownloadTabModel.getRowCount();
}
/**
* Set the table in the thread view to the according number of lines.
*/
public void resetThreadView()
{
try
{
int threadcount = Integer.valueOf(prefContainer.getPrefValue("ServerSettingsThreadCount"));
ThreadViewTableModel tm = (ThreadViewTableModel) threadViewTab.getModel();
tm.setRowCount(threadcount);
}
catch(NumberFormatException ex)
{
ThreadViewTableModel tm = (ThreadViewTableModel) threadViewTab.getModel();
tm.setRowCount(1);
}
}
/**
* Globally disconnect from server, if currently connected.
*
* @param block Whether or not to wait until connection has closed
*/
public void globalDisconnect(boolean block)
{
if(currentFileDownloader != null)
{
currentFileDownloader.shutdown();
currentFileDownloader = null;
}
if(backupFileDownloader != null)
{
backupFileDownloader.shutdown();
backupFileDownloader = null;
}
if(nioClient != null)
{
nioClient.shutdown(false, System.nanoTime() + 10 * SEC_MODIFIER, block);
nioClient = null;
}
if(backupNioClient != null)
{
backupNioClient.shutdown(false, System.nanoTime() + 10 * SEC_MODIFIER, block);
backupNioClient = null;
}
resetThreadView();
}
/**
* Set the start/stop and pause toggle buttons.
* This method sets the buttons' states, icons, and tooltips.
*/
public void setStartStopPauseToggleButtons(boolean start)
{
if(start)
{
startToggleButton.setIcon(new ImageIcon(HelloNzb.class.getResource("/resources/icons/start_download.png")));
startToggleButton.setToolTipText(localer.getBundleText("StartButtonTooltip"));
startToggleButton.setSelected(false);
pauseToggleButton.setSelected(false);
}
else
{
startToggleButton.setIcon(new ImageIcon(HelloNzb.class.getResource("/resources/icons/stop_download.png")));
startToggleButton.setToolTipText(localer.getBundleText("StopButtonTooltip"));
startToggleButton.setSelected(true);
pauseToggleButton.setSelected(false);
}
}
/**
* Returns the lr split pane divider.
*
* @return The current value
*/
public JSplitPane getLrSplitPane()
{
return lrSplitPane;
}
/**
* Returns the ud split pane divider.
*
* @return The current value
*/
public JSplitPane getUdSplitPane()
{
return udSplitPane;
}
/**
* Returns the localer object.
*
* @return The localer object
*/
public StringLocaler getLocaler()
{
return localer;
}
/**
* Returns the task manager object.
*
* @return The task manager object
*/
public TaskManager getTaskManager()
{
return taskMgr;
}
/**
* Returns the current NettyNioClient object (or null)
*
* @return The current NettyNioClient object (or null)
*/
public NettyNioClient getCurrNioClient()
{
return nioClient;
}
/**
* Returns the total amount of bytes loaded (counted for the currently
* downloaded NZB file and all its segments).
*
* @return The total amount of loaded bytes
*/
public long getTotalBytesLoaded()
{
return totalBytesLoaded;
}
/**
* Returns all NzbParser objects currently in the NZB file queue.
*
* @return All NzbParser objects in a vector
*/
public Vector<NzbParser> getNzbQueue()
{
Vector<NzbParser> vector = new Vector<NzbParser>();
// get all NzbParser objects from table model
try
{
int size = nzbFileQueueTabModel.getRowCount();
for(int i = 0; i < size; i++)
vector.add(nzbFileQueueTabModel.getNzbParser(i));
}
catch(Exception e) {}
return vector;
}
/**
* Returns the central logger instance.
* @return The Logger object
*/
public MyLogger getLogger()
{
return logger;
}
/**
* Returns the speed history graph panel object.
* @return The SpeedGraphPanel object
*/
public SpeedGraphPanel getSpeedGraphPanel()
{
return speedGraph;
}
/**
* Register the popup menu object of the NZB file queue.
* @param popup
*/
public void registerLeftPopup(JPopupMenu popup)
{
leftPopup = popup;
}
/**
* Register the popup menu object of the download file queue.
* @param popup
*/
public void registerRightPopup(JPopupMenu popup)
{
rightPopup = popup;
}
/**
* Dispose left popup menu.
*/
public void disposeLeftPopup()
{
if(leftPopup != null)
{
leftPopup.setVisible(false);
leftPopup.setEnabled(false);
leftPopup = null;
}
}
/**
* Dispose right popup menu.
*/
public void disposeRightPopup()
{
if(rightPopup != null)
{
rightPopup.setVisible(false);
rightPopup.setEnabled(false);
rightPopup = null;
}
}
/**
* Retrieve the given menu/toolbar action.
*/
public AbstractAction getMenuToolbarAction(String action)
{
return actions.get(action);
}
public abstract void startDownload();
}