package esmska.gui;
import esmska.Context;
import esmska.data.Contact;
import esmska.data.Contacts;
import esmska.data.History;
import esmska.data.History.Record;
import esmska.data.Icons;
import esmska.data.Log;
import esmska.data.Queue;
import esmska.data.Queue.Events;
import esmska.data.SMS;
import esmska.persistence.ExportManager;
import esmska.utils.ConfirmingFileChooser;
import esmska.utils.L10N;
import esmska.data.event.ValuedEvent;
import esmska.data.event.ValuedListener;
import esmska.data.Links;
import java.awt.Desktop;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.JTextComponent;
import org.apache.commons.lang.StringUtils;
/** Class containing common actions used in GUIs
*
* @author ripper
*/
public class Actions {
private static final Logger logger = Logger.getLogger(Actions.class.getName());
private static final ResourceBundle l10n = L10N.l10nBundle;
private static Action aboutAction;
private static ConfigAction configAction;
private static Action quitAction;
private static Action historyAction;
private static Action importAction;
private static Action exportAction;
private static Action logAction;
/** Show about frame */
public static Action getAboutAction() {
if (aboutAction == null) {
aboutAction = new AboutAction();
}
return aboutAction;
}
/** Show config frame */
public static Action getConfigAction() {
if (configAction == null) {
configAction = new ConfigAction();
}
return configAction;
}
/** Quit the program */
public static Action getQuitAction() {
if (quitAction == null) {
quitAction = new QuitAction();
}
return quitAction;
}
/** Show the history frame */
public static Action getHistoryAction() {
if (historyAction == null) {
historyAction = new HistoryAction();
}
return historyAction;
}
/** Import contacts from other programs/formats */
public static Action getImportAction() {
if (importAction == null) {
importAction = new ImportAction();
}
return importAction;
}
/** Export contacts for other programs */
public static Action getExportAction() {
if (exportAction == null) {
exportAction = new ExportAction();
}
return exportAction;
}
/** Show the log frame */
public static Action getLogAction() {
if (logAction == null) {
logAction = new LogAction();
}
return logAction;
}
/** Pause/unpause the sms queue
* @param showName show name of the action (in button text etc)
*/
public static Action getQueuePauseAction(boolean showName) {
return new QueuePauseAction(showName);
}
/** Get action to automatically select best suitable gateway in this list
* according to number filled in specified text component.
*
* @param gatewayComboBox Gateway combobox which to adjust to suggested gateway
* @param numberComponent Text component containing phone number
* @return action to automatically select best suitable gateway in this list
* according to number filled in specified text component
*/
public static Action getSuggestGatewayAction(GatewayComboBox gatewayComboBox, JTextComponent numberComponent) {
return new SuggestGatewayAction(gatewayComboBox, numberComponent);
}
/** Browse specific URL with a web browser */
public static Action getBrowseAction(String url) {
return new BrowseAction(url);
}
/** Browse specific URL with a web browser */
private static class BrowseAction extends AbstractAction {
private final String url;
public BrowseAction(String URL) {
super();
this.url = URL;
}
@Override
public void actionPerformed(ActionEvent e) {
if (StringUtils.startsWith(url, "esmska://")) {
//internal program action link
if (Links.CONFIG_GATEWAYS.equals(url)) {
((ConfigAction)getConfigAction()).showTab(ConfigFrame.Tabs.GATEWAYS);
} else {
assert false : "Unknown internal action link: " + url;
logger.warning("Unknown internal action link: " + url);
}
return;
}
if (!Desktop.isDesktopSupported()) {
logger.warning("Running browser not supported, can't browse URL: " + url);
return;
}
//start browser
Desktop desktop = Desktop.getDesktop();
try {
logger.fine("Browsing URL: " + url);
desktop.browse(new URL(url).toURI());
} catch (Exception ex) {
logger.log(Level.WARNING, "Could not browse URL: " + url, ex);
}
}
}
/** Show about frame */
private static class AboutAction extends AbstractAction {
AboutFrame aboutFrame;
public AboutAction() {
L10N.setLocalizedText(this, l10n.getString("About_"));
putValue(SHORT_DESCRIPTION,l10n.getString("MainFrame.show_information_about_program"));
}
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Showing About frame...");
if (aboutFrame != null && aboutFrame.isVisible()) {
aboutFrame.requestFocus();
aboutFrame.toFront();
} else {
aboutFrame = new AboutFrame();
aboutFrame.setLocationRelativeTo(Context.mainFrame);
aboutFrame.setVisible(true);
}
}
}
/** Show config frame */
private static class ConfigAction extends AbstractAction {
private ConfigFrame configFrame;
public ConfigAction() {
L10N.setLocalizedText(this, l10n.getString("Preferences_"));
putValue(SMALL_ICON, Icons.get("config-16.png"));
putValue(LARGE_ICON_KEY, Icons.get("config-32.png"));
putValue(SHORT_DESCRIPTION,l10n.getString("MainFrame.configure_program_behaviour"));
}
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Showing config frame...");
if (configFrame != null) {
configFrame.setVisible(true);
configFrame.requestFocus();
configFrame.toFront();
} else {
configFrame = new ConfigFrame();
configFrame.setLocationRelativeTo(Context.mainFrame);
configFrame.setVisible(true);
}
}
public void showTab(ConfigFrame.Tabs tab) {
actionPerformed(null);
configFrame.switchToTab(tab);
}
}
/** Quit the program */
private static class QuitAction extends AbstractAction {
public QuitAction() {
L10N.setLocalizedText(this, l10n.getString("Quit_"));
putValue(SMALL_ICON, Icons.get("exit-16.png"));
putValue(LARGE_ICON_KEY, Icons.get("exit-32.png"));
putValue(SHORT_DESCRIPTION,l10n.getString("MainFrame.Quit_program"));
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
}
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Quitting application...");
Context.mainFrame.exit();
}
}
/** Show the history frame */
private static class HistoryAction extends AbstractAction {
private HistoryFrame historyFrame;
public HistoryAction() {
L10N.setLocalizedText(this, l10n.getString("Message_history_"));
putValue(SMALL_ICON, Icons.get("history-16.png"));
putValue(LARGE_ICON_KEY, Icons.get("history-32.png"));
this.putValue(SHORT_DESCRIPTION,l10n.getString("MainFrame.show_history_of_sent_messages"));
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_T,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
}
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Showing history frame...");
if (historyFrame != null) {
historyFrame.setVisible(true);
historyFrame.requestFocus();
historyFrame.toFront();
} else {
historyFrame = new HistoryFrame();
historyFrame.setLocationRelativeTo(Context.mainFrame);
historyFrame.addValuedListener(new HistoryListener());
historyFrame.setVisible(true);
}
}
}
/** Listens for events from history table */
private static class HistoryListener implements ValuedListener<HistoryFrame.Events, History.Record> {
@Override
public void eventOccured(ValuedEvent<HistoryFrame.Events, Record> e) {
switch (e.getEvent()) {
case RESEND_SMS:
History.Record record = e.getValue();
if (record == null) {
return;
}
SMS sms = new SMS(record.getNumber(), record.getText(),
record.getGateway(), record.getName(), null);
Context.mainFrame.getContactPanel().clearSelection();
Context.mainFrame.getSMSPanel().setSMS(sms);
}
}
}
/** Import contacts from other programs/formats */
private static class ImportAction extends AbstractAction {
private ImportFrame importFrame;
public ImportAction() {
L10N.setLocalizedText(this, l10n.getString("Contact_import_"));
this.putValue(SHORT_DESCRIPTION,l10n.getString("MainFrame.import_contacts_from_other_applications"));
}
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Showing import contacts dialog...");
if (importFrame != null && importFrame.isVisible()) {
importFrame.requestFocus();
importFrame.toFront();
} else {
importFrame = new ImportFrame();
importFrame.setLocationRelativeTo(Context.mainFrame);
importFrame.setVisible(true);
}
}
}
/** Export contacts for other programs */
private static class ExportAction extends AbstractAction {
private static final FileFilter csvFileFilter =
new FileNameExtensionFilter(l10n.getString("ExportManager.csv_filter"), "csv");
private static final FileFilter vCardFileFilter =
new FileNameExtensionFilter(l10n.getString("ExportManager.vcard_filter"), "vcf", "vcard");
private static ConfirmingFileChooser chooser;
public ExportAction() {
L10N.setLocalizedText(this, l10n.getString("Contact_export_"));
this.putValue(SHORT_DESCRIPTION, l10n.getString("MainFrame.export_contacts_to_file"));
}
@Override
public void actionPerformed(ActionEvent e) {
Collection<Contact> contacts = Contacts.getInstance().getAll();
logger.fine("Showing export contacts dialog...");
//show info
String message = l10n.getString("ExportManager.export_info");
JOptionPane.showMessageDialog(Context.mainFrame, new JLabel(message), l10n.getString("ExportManager.contact_export"),
JOptionPane.INFORMATION_MESSAGE,
Icons.get("contact-48.png"));
//choose file
if (chooser == null) {
chooser = new ConfirmingFileChooser();
chooser.setDialogTitle(l10n.getString("ExportManager.choose_export_file"));
//set dialog type not to erase approve button text later
chooser.setDialogType(JFileChooser.SAVE_DIALOG);
chooser.setApproveButtonText(l10n.getString("Save"));
chooser.addChoosableFileFilter(csvFileFilter);
chooser.addChoosableFileFilter(vCardFileFilter);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setFileFilter(csvFileFilter);
}
if (chooser.showSaveDialog(Context.mainFrame) != JFileChooser.APPROVE_OPTION) {
return;
}
File file = chooser.getSelectedFile();
logger.finer("File chosen for contacts export: " + file.getAbsolutePath());
//check if file can be written
if (file.exists() && !file.canWrite()) {
logger.info("File '" + file.getAbsolutePath() + "' can't be written");
JOptionPane.showMessageDialog(Context.mainFrame,
MessageFormat.format(l10n.getString("ExportManager.cant_write"), file.getAbsolutePath()),
null, JOptionPane.ERROR_MESSAGE);
return;
}
//save
logger.finer("About to export " + contacts.size() + " contacts");
try {
FileOutputStream out = new FileOutputStream(file);
if (chooser.getFileFilter() == vCardFileFilter) {
ExportManager.exportContactsToVCard(contacts, out);
} else {
ExportManager.exportContacts(contacts, out);
}
out.flush();
out.close();
} catch (Exception ex) {
logger.log(Level.WARNING, "Could not export contacts to file", ex);
Log.getInstance().addRecord(new Log.Record(
l10n.getString("ExportManager.export_failed"), null, Icons.STATUS_ERROR));
JOptionPane.showMessageDialog(Context.mainFrame, l10n.getString("ExportManager.export_failed"), null,
JOptionPane.ERROR_MESSAGE);
return;
}
Log.getInstance().addRecord(new Log.Record(
l10n.getString("ExportManager.export_ok"), null, Icons.STATUS_INFO));
JOptionPane.showMessageDialog(Context.mainFrame, l10n.getString("ExportManager.export_ok!"), null,
JOptionPane.INFORMATION_MESSAGE);
}
}
/** Select suggested gateway in the combobox */
public static class SuggestGatewayAction extends AbstractAction {
private GatewayComboBox gatewayComboBox;
private JTextComponent numberComponent;
public SuggestGatewayAction(GatewayComboBox gatewayComboBox, JTextComponent numberComponent) {
L10N.setLocalizedText(this, l10n.getString("Suggest_"));
this.putValue(SHORT_DESCRIPTION, l10n.getString("GatewayComboBox.Choose_suitable_gateway_for_provided_number"));
if (gatewayComboBox == null || numberComponent == null) {
throw new IllegalArgumentException("Arguments cant be null");
}
this.gatewayComboBox = gatewayComboBox;
this.numberComponent = numberComponent;
}
@Override
public void actionPerformed(ActionEvent e) {
String number = numberComponent.getText();
gatewayComboBox.selectNextSuggestedGateway(number);
}
}
/** Show the log frame */
private static class LogAction extends AbstractAction {
private LogFrame logFrame;
public LogAction() {
L10N.setLocalizedText(this, l10n.getString("Log_"));
putValue(LARGE_ICON_KEY, Icons.get("log-48.png"));
this.putValue(SHORT_DESCRIPTION,l10n.getString("Show_application_log"));
}
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Showing Log frame...");
if (logFrame != null) {
logFrame.setVisible(true);
logFrame.requestFocus();
logFrame.toFront();
} else {
logFrame = new LogFrame();
logFrame.setLocationRelativeTo(Context.mainFrame);
logFrame.setVisible(true);
}
}
}
/** Pause/unpause the sms queue */
private static class QueuePauseAction extends AbstractAction {
private final String nameRunning = l10n.getString("Pause");
private final String nameRunningLong = l10n.getString("Pause_queue");
private final String nameStopped = l10n.getString("Unpause");
private final String nameStoppedLong = l10n.getString("Unpause_queue");
private final String descRunning = l10n.getString("QueuePanel.Pause_sending_of_sms_in_the_queue");
private final String descStopped = l10n.getString("QueuePanel.Unpause_sending_of_sms_in_the_queue");
private final ImageIcon pauseIcon = Icons.get("pause-22.png");
private final ImageIcon pauseIconSmall = Icons.get("pause-16.png");
private final ImageIcon startIcon = Icons.get("start-22.png");
private final ImageIcon startIconSmall = Icons.get("start-16.png");
private static final Queue queue = Queue.getInstance();
private boolean longName;
public QueuePauseAction() {
this(true);
}
public QueuePauseAction(boolean longName) {
super();
this.longName = longName;
setPaused(queue.isPaused());
putValue(MNEMONIC_KEY, KeyEvent.VK_P);
queue.addValuedListener(new ValuedListener<Queue.Events, SMS>() {
@Override
public void eventOccured(ValuedEvent<Events, SMS> e) {
switch (e.getEvent()) {
case QUEUE_PAUSED:
setPaused(true);
break;
case QUEUE_RESUMED:
setPaused(false);
break;
}
}
});
// enable this only once the program is fully loaded
Context.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!StringUtils.equals(evt.getPropertyName(), "everythingLoaded")) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateStatus();
}
});
}
});
updateStatus();
}
@Override
public void actionPerformed(ActionEvent e) {
queue.setPaused(!queue.isPaused());
}
private void setPaused(boolean paused) {
if (paused) {
if (longName) {
putValue(NAME, nameStoppedLong);
} else {
putValue(NAME, nameStopped);
}
putValue(SMALL_ICON, startIconSmall);
putValue(LARGE_ICON_KEY, startIcon);
putValue(SHORT_DESCRIPTION, descStopped);
putValue(SELECTED_KEY, true);
} else {
if (longName) {
putValue(NAME, nameRunningLong);
} else {
putValue(NAME, nameRunning);
}
putValue(SMALL_ICON, pauseIconSmall);
putValue(LARGE_ICON_KEY, pauseIcon);
putValue(SHORT_DESCRIPTION, descRunning);
putValue(SELECTED_KEY, false);
}
}
private void updateStatus() {
this.setEnabled(Context.everythingLoaded());
}
}
}