/*
* Copyright (C) 2015 Shashank Tulsyan <shashaank at neembuu.com>
*
* 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 neembuu.uploader;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;
import neembuu.uploader.interfaces.UploadStatus;
import neembuu.uploader.interfaces.Uploader;
import neembuu.uploader.interfaces.abstractimpl.AbstractUploader;
import neembuu.uploader.theme.ThemeCheck;
import neembuu.uploader.translation.Translation;
import neembuu.uploader.utils.NULogger;
import neembuu.uploader.utils.UploadStatusUtils;
/**
* This class is used by NeembuuUploader class for displaying Popup Menu
* Don't mess with this class. (package private)
* @author vigneshwaran
*/
class PopupBuilder implements ClipboardOwner {
//Singleton instance
private static final PopupBuilder INSTANCE = new PopupBuilder();
//Member variables
private Clipboard clipboard = null;
private JPopupMenu popup = null;
private JMenuItem copyDownloadURL = null;
private JMenuItem copyDeleteURL = null;
private JMenuItem exportLinksURL = null;
private JMenuItem gotoDownloadURL = null;
private JMenuItem removeFromQueue = null;
private JMenuItem removeFinished = null;
private JMenuItem stopUpload = null;
private JMenuItem retryUpload = null;
private JTable table = null;
private int[] selectedrows = null;
private boolean multiple = false;
//Create one Popup object and reuse it everytime instead of creating new objects everytime
private PopupBuilder() {
NULogger.getLogger().info("Initializing PopupBuilder");
//Get reference to main table for easy typing
table = NeembuuUploader.getInstance().getTable();
//Get a reference to System Clipboard
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
//Initialize popup menu
popup = new JPopupMenu();
//Initialize menuitem with only icons.. Add Locale specific text later whenever getInstance is called
//Add its exclusive action listener
//Add this menu item to the popupmenu
copyDownloadURL = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/copydownloadurl.png")));
copyDownloadURL.addActionListener(new CopyDownloadURLActionListener());
popup.add(copyDownloadURL);
copyDeleteURL = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/copydeleteurl.png")));
copyDeleteURL.addActionListener(new CopyDeleteURLActionListener());
popup.add(copyDeleteURL);
exportLinksURL = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/exporturl.png")));
exportLinksURL.addActionListener(new ExportLinksActionListener());
popup.add(exportLinksURL);
gotoDownloadURL = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/gotodownloadurl.png")));
gotoDownloadURL.addActionListener(new GotoDownloadURLActionListener());
popup.add(gotoDownloadURL);
//Add a separator to differentiate menu items
popup.add(new JSeparator());
removeFromQueue = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/removefromlist.png")));
removeFromQueue.addActionListener(new RemoveFromQueueActionListener());
popup.add(removeFromQueue);
removeFinished = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/removefinished.png")));
removeFinished.addActionListener(new RemoveFinishedActionListener());
popup.add(removeFinished);
//Add a separator to differentiate menu items
popup.add(new JSeparator());
stopUpload = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/stopupload.png")));
stopUpload.addActionListener(new StopUploadActionListener());
popup.add(stopUpload);
retryUpload = new JMenuItem(new javax.swing.ImageIcon(getClass().getResource("/neembuuuploader/resources/popup/retryupload.png")));
retryUpload.addActionListener(new RetryUploadActionListener());
popup.add(retryUpload);
}
/**
*
* @return singleton instance of popupbuilder
*/
public static PopupBuilder getInstance() {
return INSTANCE;
}
/**
* Display popup menu at the specified parameters (component, x and y position)
* @param invoker
* @param x
* @param y
*/
public void show(Component invoker, int x, int y) {
ThemeCheck.apply(popup);
selectedrows = table.getSelectedRows();
if (table.getSelectedRowCount() > 1) {
multiple = true;
} else {
multiple = false;
}
copyDownloadURL.setEnabled(canCopyDownloadURLEnable());
copyDeleteURL.setEnabled(canCopyDeleteURLEnable());
exportLinksURL.setEnabled(canExportLinksEnable());
gotoDownloadURL.setEnabled(canGotoDownloadURLEnable());
removeFromQueue.setEnabled(canRemoveFromQueueEnable());
removeFinished.setEnabled(canRemoveFinishedEnable());
stopUpload.setEnabled(canStopUploadEnable());
retryUpload.setEnabled(canRetryUploadEnable());
popup.show(invoker, x, y);
NULogger.getLogger().info("Popup Menu displayed");
}
/**
* Implemented method. Hope this method may never be called.
* @param clipboard
* @param contents
*/
@Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
NULogger.getLogger().log(Level.WARNING, "{0}: Lost clipboard ownersh ip", getClass().getName());
}
/**
*
* @return whether the menuitem can be enabled or not depending on the status of selected rows
*/
private boolean canCopyDownloadURLEnable() {
//Popup shows only if there is one or more row selected so this is a safe code
//Show only for finished rows
int i = 0;
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.UPLOADFINISHED)) {
i++;
}
}
//Setting default text whether return value will be true or not
copyDownloadURL.setText(Translation.T().copyDownloadURL());
//Atleast one or more selected rows must be valid for the above condition
if (i >= 1) {
//If multiple rows selected and few or only one of them satisfies the condition, append (n) at the end even if it is 1.
if (multiple) {
copyDownloadURL.setText(Translation.T().copyDownloadURL() + " (" + i + ")");
}
return true;
}
return false;
}
private boolean canCopyDeleteURLEnable() {
//Popup shows only if there is one or more row selected so this is a safe code
int i = 0;
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.UPLOADFINISHED)
&& table.getValueAt(selectedrow, NUTableModel.DELETEURL) != UploadStatus.NA) {
i++;
}
}
copyDeleteURL.setText(Translation.T().copyDeleteURL());
if (i >= 1) {
if (multiple) {
copyDeleteURL.setText(Translation.T().copyDeleteURL() + " (" + i + ")");
}
return true;
}
return false;
}
private boolean canExportLinksEnable() {
//Popup shows only if there is one or more row selected so no need to check that here.
int i = 0;
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.UPLOADFINISHED)) {
i++;
}
}
exportLinksURL.setText(Translation.T().exportLinks());
if (i >= 1) {
if (multiple) {
exportLinksURL.setText(Translation.T().exportLinks() + " (" + i + ")");
}
return true;
}
return false;
}
private boolean canGotoDownloadURLEnable() {
//Popup shows only if there is one or more row selected so no need to check that here.
int i = 0;
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.UPLOADFINISHED)) {
i++;
}
}
gotoDownloadURL.setText(Translation.T().gotoDownloadURL());
if (i >= 1) {
if (multiple) {
gotoDownloadURL.setText(Translation.T().gotoDownloadURL() + " (" + i + ")");
}
return true;
}
return false;
}
private boolean canRemoveFromQueueEnable() {
int i = 0;
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.QUEUED,
UploadStatus.UPLOADFINISHED, UploadStatus.UPLOADFAILED,
UploadStatus.UPLOADSTOPPED, UploadStatus.UPLOADINVALID,
UploadStatus.RETRYFAILED)) {
i++;
}
}
removeFromQueue.setText(Translation.T().removeFromQueue());
if (i >= 1) {
if (multiple) {
removeFromQueue.setText(Translation.T().removeFromQueue() + " (" + i + ")");
}
return true;
}
return false;
}
private boolean canRemoveFinishedEnable() {
//Check all rows instead of selection
int finishedrows = 0;
synchronized(NUTableModel.uploadList){
for (int i = 0; i < NUTableModel.uploadList.size(); i++) {
if (UploadStatusUtils.isRowStatusOneOf(i, UploadStatus.UPLOADFINISHED)) {
finishedrows++;
}
}
}
removeFinished.setText(Translation.T().removeFinished());
if (finishedrows > 0) {
removeFinished.setText(Translation.T().removeFinished() + " (" + finishedrows + ")");
return true;
}
return false;
}
private boolean canStopUploadEnable() {
int i = 0;
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.INITIALISING,
UploadStatus.UPLOADING,
UploadStatus.REUPLOADING)) {
i++;
}
}
stopUpload.setText(Translation.T().stopUpload());
if (i >= 1) {
if (multiple) {
stopUpload.setText(Translation.T().stopUpload() + " (" + i + ")");
}
return true;
}
return false;
}
private boolean canRetryUploadEnable() {
int i = 0;
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.UPLOADFAILED,
UploadStatus.UPLOADSTOPPED,
UploadStatus.RETRYFAILED,
UploadStatus.UPLOADFINISHED)) {
i++;
}
}
retryUpload.setText(Translation.T().retryUpload());
if (i >= 1) {
if (multiple) {
retryUpload.setText(Translation.T().retryUpload() + " (" + i + ")");
}
return true;
}
return false;
}
/**
* Action listener for CopyDownloadURL menu item
*/
class CopyDownloadURLActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Collect the set of urls and set the text in Clipboard
NULogger.getLogger().info("Copy Download URL clicked");
StringBuilder listofurls = new StringBuilder();
for (int selectedrow : selectedrows) {
listofurls.append(table.getValueAt(selectedrow, NUTableModel.DOWNLOADURL).toString()).append("\n");
}
setClipboardContent(listofurls.toString());
}
}
class CopyDeleteURLActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Collect the set of urls and set the text in Clipboard
NULogger.getLogger().info("Copy Delete URL clicked");
StringBuilder listofurls = new StringBuilder();
for (int selectedrow : selectedrows) {
listofurls.append(table.getValueAt(selectedrow, NUTableModel.DELETEURL).toString()).append("\n");
}
setClipboardContent(listofurls.toString());
}
}
class ExportLinksActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//No need to check if rows are selected -- popup shown only if true
//No need to check if atleast one row is finished -- menu item enabled only if true
NULogger.getLogger().info("Export links clicked");
JFileChooser fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setDialogTitle(Translation.T().exportLinkDialog());
fc.setAcceptAllFileFilterUsed(false);
fc.setFileFilter(new FileNameExtensionFilter("HTML File", new String[]{"html"}));
if (fc.showSaveDialog(NeembuuUploader.getInstance()) != JFileChooser.APPROVE_OPTION) {
return;
}
File htmlfile = fc.getSelectedFile();
//Get the starting row to ending row
int startingrow = table.getSelectedRow();
int endingrow = startingrow + table.getSelectedRowCount() - 1;
//This is a very big stringbuilder.. It contains text for html and css.. Rows will be appended programmatically.
//Unfortunately I can do this only in English
StringBuilder sb = new StringBuilder("<html>"
+ "<head><title>Neembuu Uploader</title>"
+ "<style type='text/css'>"
+ " table { display:block; width:90%; border-top:1px solid #e5eff8; border-right:1px solid #e5eff8; margin:1em auto;border-collapse:collapse;}"
+ " td { color:#7892a8;border-bottom:1px solid #e5eff8;border-left:1px solid #e5eff8;padding:.3em 1em;text-align:center;}"
+ " tr.odd td { background:#f7fbff } tr.odd .column1 {background:#f4f9fe;} .column1 {background:#f9fcfe;}"
+ " th, h3 { background:#f4f9fe; text-align:center; font:bold 1.2em/2em 'Century Gothic','Trebuchet MS',Arial,Helvetica,sans-serif; color:#66a3d3; }"
+ "</style></head>"
+ "<body><center><h3>Neembuu Uploader Exported Linkset</h3></center>"
+ "<table><tr class='odd'><th class='column1'>File</th><th>Size</th><th>Host</th><th>Download URL</th><th>Delete URL if any</th></tr>");
int i = 1;
for (int row = startingrow; row <= endingrow; row++) {
//ignore rows that are not "Upload Finished"
if (!UploadStatusUtils.isRowStatusOneOf(row, UploadStatus.UPLOADFINISHED)) {
continue;
}
if (i % 2 == 0) {
sb.append("<tr class='odd'>");
} else {
sb.append("<tr>");
}
for (int column = 0; column < table.getModel().getColumnCount(); column++) {
if (column == NUTableModel.STATUS || column == NUTableModel.PROGRESS) {
continue;
}
if (column == 0) {
sb.append("<td class='column1'>");
} else {
sb.append("<td>");
}
if (column == NUTableModel.DOWNLOADURL
&& !(table.getModel().getValueAt(row, column).equals(UploadStatus.NA.getDefaultLocaleSpecificString())
|| table.getModel().getValueAt(row, column).equals(UploadStatus.NA.getLocaleSpecificString()))) {
sb.append("<a target='_blank' href='").append(table.getModel().getValueAt(row, column)).append("'>").append(table.getModel().getValueAt(row, column)).append("</a>");
} else {
sb.append(table.getModel().getValueAt(row, column));
}
sb.append("</td>");
}
sb.append("</tr>");
i++;
}
sb.append("</table></body></html>");
try {
NULogger.getLogger().log(Level.INFO, "{0}: Writing links to html file..", getClass().getName());
//Add .html to the filename if the file doesn't already have .html or .htm extension
if (!(htmlfile.getName().toLowerCase().endsWith(".html") || htmlfile.getName().toLowerCase().endsWith(".htm"))) {
htmlfile = new File(htmlfile.getAbsolutePath() + ".html");
}
PrintWriter writer = new PrintWriter(new FileWriter(htmlfile));
writer.write(sb.toString());
writer.close();
} catch (Exception ex) {
NULogger.getLogger().log(Level.INFO, "{0}: Error while writing html file\n{1}", new Object[]{getClass().getName(), ex});
System.err.println(ex);
}
}
}
class GotoDownloadURLActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
NULogger.getLogger().info("Goto Download URL clicked..");
for (int selectedrow : selectedrows) {
String url =
table.getValueAt(selectedrow, NUTableModel.DOWNLOADURL).toString();
if (!Desktop.isDesktopSupported()) {
return;
}
try {
NULogger.getLogger().log(Level.INFO, "Opening url in browser: {0}", url);
Desktop.getDesktop().browse(new URI(url));
} catch (Exception ex) {
NULogger.getLogger().log(Level.WARNING, "{0}: Cannot load url: {1}", new Object[]{getClass().getName(), url});
System.err.println(ex);
}
}
}
}
class RemoveFromQueueActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Run this in a separate thread for responsiveness of GUI
//Otherwise the popupmenu won't disappear until action completed. bad for slow pc's.
SwingUtilities.invokeLater(new Runnable(){
//new Thread() {
@Override
public void run() {
NULogger.getLogger().info("Remove from Queue menu item clicked");
//Must lock queue
QueueManager.getInstance().setQueueLock(true);
int selectedrow;
//Remove from the end
for (int i = selectedrows.length - 1; i >= 0; i--) {
selectedrow = selectedrows[i];
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.QUEUED,
UploadStatus.UPLOADFINISHED, UploadStatus.UPLOADFAILED,
UploadStatus.UPLOADSTOPPED, UploadStatus.UPLOADINVALID,
UploadStatus.RETRYFAILED)) {
NUTableModel.getInstance().removeUpload(selectedrow);
NULogger.getLogger().log(Level.INFO, "{0}: Removed row no. {1}", new Object[]{getClass().getName(), selectedrow});
}
}
//Unlock Queue back
QueueManager.getInstance().setQueueLock(false);
}
//}.start();
});
}
}
class RemoveFinishedActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
//new Thread() {
@Override
public void run() {
NULogger.getLogger().info("Remove Finished menu item clicked..");
//Must lock queue
QueueManager.getInstance().setQueueLock(true);
//Remove from end
for (int i = NUTableModel.uploadList.size() - 1; i >= 0; i--) {
if (UploadStatusUtils.isRowStatusOneOf(i, UploadStatus.UPLOADFINISHED)) {
NUTableModel.getInstance().removeUpload(i);
NULogger.getLogger().log(Level.INFO, "{0}: Removed row no. {1}", new Object[]{getClass().getName(), i});
}
}
//Unlock Queue back
QueueManager.getInstance().setQueueLock(false);
}
//}.start();
});
}
}
class StopUploadActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Without thread, popup will not return
SwingUtilities.invokeLater(new Runnable() {
//new Thread() {
@Override
public void run() {
NULogger.getLogger().info("Stop Upload clicked..");
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.INITIALISING, UploadStatus.UPLOADING, UploadStatus.REUPLOADING)) {
NUTableModel.uploadList.get(selectedrow).stopUpload();
NULogger.getLogger().log(Level.INFO, "Stopped upload : {0}", selectedrow);
}
}
}
//}.start();
});
}
}
class RetryUploadActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Without thread, popup will not return
new Thread() {
@Override
public void run() {
NULogger.getLogger().info("Retry Upload clicked..");
for (int selectedrow : selectedrows) {
if (UploadStatusUtils.isRowStatusOneOf(selectedrow, UploadStatus.UPLOADFAILED, UploadStatus.UPLOADSTOPPED, UploadStatus.RETRYFAILED, UploadStatus.UPLOADFINISHED)) {
try {
NULogger.getLogger().log(Level.INFO, "Retrying upload : {0}", selectedrow);
Uploader uploader = NUTableModel.uploadList.get(selectedrow);
File file = uploader.getFile();
Constructor<? extends Uploader> uploaderConstructor = uploader.getClass().getConstructor();
AbstractUploader retryingUploader = (AbstractUploader) uploaderConstructor.newInstance();
retryingUploader.setFile(file);
retryingUploader.setRetry(true);
NUTableModel.uploadList.set(selectedrow, retryingUploader);
QueueManager.getInstance().startNextUploadIfAny();
} catch (Exception ex) {
NULogger.getLogger().log(Level.INFO, "Retry failed for : {0}", selectedrow);
}
}
}
}
}.start();
}
}
/**
* Place a String on the clipboard, and make this class the
* owner of the clipboard contents.
*/
public void setClipboardContent(String aString) {
StringSelection stringSelection = new StringSelection(aString);
clipboard.setContents(stringSelection, this);
NULogger.getLogger().info("Copied to clipboard");
}
}