package esmska.gui; import esmska.Context; import esmska.data.Queue.Events; import esmska.data.event.ValuedEvent; import java.awt.Component; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.beans.PropertyChangeEvent; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Box; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JSeparator; import javax.swing.JSplitPane; import javax.swing.JToolBar; import javax.swing.JToolBar.Separator; import javax.swing.KeyStroke; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.ToolTipManager; import javax.swing.WindowConstants; import org.apache.commons.io.IOUtils; import org.jdesktop.beansbinding.BeanProperty; import org.jdesktop.beansbinding.Binding; import org.jdesktop.beansbinding.BindingGroup; import org.jdesktop.beansbinding.Bindings; import org.jdesktop.beansbinding.AutoBinding.UpdateStrategy; import esmska.update.UpdateChecker; import esmska.data.Config; import esmska.data.Gateways; import esmska.data.History; import esmska.data.History.Record; import esmska.data.Icons; import esmska.data.Log; import esmska.data.Queue; import esmska.data.SMS; import esmska.integration.ActionBean; import esmska.integration.IntegrationAdapter; import esmska.transfer.SMSSender; import esmska.utils.L10N; import esmska.data.event.ValuedListener; import esmska.data.Links; import esmska.data.event.ActionEventSupport; import esmska.transfer.ImageCodeManager; import esmska.update.Statistics; import esmska.update.UpdateInstaller; import esmska.utils.MiscUtils; import esmska.utils.RuntimeUtils; import java.awt.Image; import java.awt.SplashScreen; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.beans.Beans; import java.beans.IntrospectionException; import java.beans.PropertyChangeListener; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.ListIterator; import java.util.ResourceBundle; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.Timer; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.openide.awt.Mnemonics; import org.pushingpixels.substance.api.SubstanceLookAndFeel; /** * MainFrame form * * @author ripper */ public class MainFrame extends javax.swing.JFrame { private static MainFrame instance; private static final Logger logger = Logger.getLogger(MainFrame.class.getName()); private static final String RES = "/esmska/resources/"; private static final ResourceBundle l10n = L10N.l10nBundle; /** custom beans binding group */ private BindingGroup bindGroup = new BindingGroup(); /** sender of sms */ private static final SMSSender smsSender = new SMSSender(); private static final Config config = Config.getInstance(); private static final History history = History.getInstance(); private static final Log log = Log.getInstance(); private static final Queue queue = Queue.getInstance(); /** shutdown handler thread */ private Thread shutdownThread = new ShutdownThread(); private UpdateChecker updateChecker = UpdateChecker.getInstance(); /** * Creates new form MainFrame */ private MainFrame() { instance = this; Context.mainFrame = instance; // if we are using Aqua L&F, set textured window property // must be called before components inicialization if (ThemeManager.isAquaCurrentLaF()) { getRootPane().putClientProperty("apple.awt.brushMetalLook", "true"); } initComponents(); //set window images ArrayList<Image> images = new ArrayList<Image>(); images.add(Icons.get("esmska-16.png").getImage()); images.add(Icons.get("esmska-32.png").getImage()); images.add(Icons.get("esmska-64.png").getImage()); images.add(Icons.get("esmska.png").getImage()); setIconImages(images); //hide on Ctrl+W String command = "hide"; getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke( KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), command); getRootPane().getActionMap().put(command, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { //hide only when notification icon present or on mac if (NotificationIcon.isInstalled() || RuntimeUtils.isMac()) { formWindowClosing(new WindowEvent(MainFrame.this, 0)); } } }); //on Mac, move program menu items to system menu if (RuntimeUtils.isMac()) { logger.fine("Running on Mac OS, hiding some menu items..."); try { ActionBean bean = new ActionBean(); bean.setQuitAction(Actions.getQuitAction()); bean.setAboutAction(Actions.getAboutAction()); bean.setConfigAction(Actions.getConfigAction()); IntegrationAdapter integration = IntegrationAdapter.getInstance(); integration.setActionBean(bean); integration.activateGUI(); programMenu.setVisible(false); aboutMenuItem.setVisible(false); //should be helpSeparator.setVisible(false); but bug #6365547 in Apple Java precludes it helpMenu.remove(helpSeparator); } catch (Throwable ex) { logger.log(Level.WARNING, "Can't integrate program menu items to " + "Mac system menu", ex); } } //set tooltip delay ToolTipManager.sharedInstance().setInitialDelay(750); ToolTipManager.sharedInstance().setDismissDelay(60000); //add first log record log.addRecord(new Log.Record(l10n.getString("Program_start"))); //load config loadConfig(); if (queue.size() > 0) { queue.setPaused(true); } //setup components contactPanel.requestFocusInWindow(); contactPanel.ensureContactSelected(); queue.addValuedListener(new QueueListener()); ImageCodeManager.setResolver(new GUIImageCodeResolver()); //use bindings Binding bind = Bindings.createAutoBinding(UpdateStrategy.READ, config, BeanProperty.create("toolbarVisible"), toolBar, BeanProperty.create("visible")); Binding bind2 = Bindings.createAutoBinding(UpdateStrategy.READ, config, BeanProperty.create("notificationIconVisible"), exitButton, BeanProperty.create("visible")); bindGroup.addBinding(bind); bindGroup.addBinding(bind2); bindGroup.bind(); //add shutdown handler, when program is closed externally (logout, SIGTERM, etc) //only if really running, not in design mode if (!Beans.isDesignTime()) { Runtime.getRuntime().addShutdownHook(shutdownThread); } // listen for changed sizes of smsPanel and queuePanel and adjust splitPane if required ActionListener verticalSplitListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (e.getID() == ActionEventSupport.ACTION_NEED_RESIZE) { JComponent comp = (JComponent) e.getSource(); int prefHeight = comp.getPreferredSize().height; int dividerLocation = prefHeight; if (verticalSplitPane.getBottomComponent() == comp) { //for bottom component we have to substract the number from full height dividerLocation = verticalSplitPane.getHeight() - prefHeight - verticalSplitPane.getDividerSize(); } verticalSplitPane.setDividerLocation(dividerLocation); } } }; smsPanel.addActionListener(verticalSplitListener); queuePanel.addActionListener(verticalSplitListener); // wait for asynchronous user data loading and then finalize everything final AtomicBoolean calledEverythingLoaded = new AtomicBoolean(); Context.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (!StringUtils.equals(evt.getPropertyName(), "everythingLoaded")) { return; } if (!Context.everythingLoaded()) { return; } // this may come from non-EDT thread SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (calledEverythingLoaded.compareAndSet(false, true)) { everythingLoaded(); } } }); Context.removePropertyChangeListener(this); } }); // the data could have been loaded before setting the listener, check it if (Context.everythingLoaded() && calledEverythingLoaded.compareAndSet(false, true)) { everythingLoaded(); } } /** Performs last operations after all user data has been loaded. */ private void everythingLoaded() { //check for valid gateways if (Gateways.getInstance().size() <= 0) { logger.warning("No usable gateways found"); JOptionPane.showMessageDialog(this, new JLabel(l10n.getString("MainFrame.no_gateways")), null, JOptionPane.ERROR_MESSAGE); } // send statistics Statistics.sendUsageInfo(); // check for updates updateChecker.addActionListener(new UpdateListener()); updateChecker.checkForUpdates(); } /** Start loading gatewates asynchronously */ private void loadGatewaysAsync() { logger.fine("Loading gateways asynchronously..."); Thread loadGwsThread = new Thread(new Runnable() { @Override public void run() { // load gateways try { Context.persistenceManager.loadGateways(); } catch (IntrospectionException ex) { //it seems there is no JavaScript support logger.log(Level.SEVERE, "Current JRE doesn't support JavaScript execution", ex); try { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { JOptionPane.showMessageDialog(null, l10n.getString("Main.no_javascript"), null, JOptionPane.ERROR_MESSAGE); } }); } catch (Exception e) { logger.log(Level.SEVERE, "Can't display error message", e); } System.exit(2); } catch (Exception ex) { logger.log(Level.SEVERE, "Could not load gateways", ex); } // load gateway properties try { Context.persistenceManager.loadGatewayProperties(); } catch (Exception ex) { logger.log(Level.SEVERE, "Could not load gateway properties file", ex); } // announce Context.setGatewaysLoaded(true); } }); loadGwsThread.setDaemon(true); loadGwsThread.start(); } /** Create an instance of MainFrame. Should be called only for the first * initialization, after that the instance is available in the Context. */ public static void instantiate() { if (instance == null) { instance = new MainFrame(); } else { throw new IllegalStateException("MainFrame is already instantiated"); } } /** Start the mainframe and let it be visible according to user preferences * (visible on the screen or hidden to notification area) */ public void startAndShow() { logger.fine("Showing mainframe..."); //if the window should be minimized into notification area if (config.isStartMinimized() && NotificationIcon.isInstalled()) { logger.fine("Starting hidden in notification icon"); //hide splashscreen, otherwise on Windows it stays visible until mainframe is shown SplashScreen splash = SplashScreen.getSplashScreen(); if (splash != null && splash.isVisible()) { splash.close(); } } else { //show the form this.setVisible(true); } // after all is set, load gateways asynchronously // we delayed it to this point, because even running it in a background // thread slows the startup of the main window loadGatewaysAsync(); } /** Display random tip from the collection of tips */ public void showTipOfTheDay() { try { List tips = IOUtils.readLines( getClass().getResourceAsStream(RES + "tips.txt"), "UTF-8"); int random = new Random().nextInt(tips.size()); statusPanel.setStatusMessage(l10n.getString("MainFrame.tip") + " " + l10n.getString((String)tips.get(random)), null, null, false); } catch (IOException ex) { logger.log(Level.SEVERE, "Can't display tip of the day", ex); } } public StatusPanel getStatusPanel() { return statusPanel; } public ContactPanel getContactPanel() { return contactPanel; } public SMSPanel getSMSPanel() { return smsPanel; } public SMSSender getSMSSender() { return smsSender; } public JToolBar getToolbar() { return toolBar; } public QueuePanel getQueuePanel() { return queuePanel; } public JSplitPane getHorizontalSplitPane() { return horizontalSplitPane; } public JSplitPane getVerticalSplitPane() { return verticalSplitPane; } /** Quit the program */ public void exit() { logger.fine("Closing program..."); //user requested program close, shutdown handler not needed Runtime.getRuntime().removeShutdownHook(shutdownThread); //save end exit boolean saveOk = saveAll(); if (!saveOk) { //some data were not saved JOptionPane.showMessageDialog(this, l10n.getString("MainFrame.cant_save_config"), null, JOptionPane.WARNING_MESSAGE); } int returnCode = saveOk ? 0 : 3; logger.log(Level.FINE, "Exiting program with return code: {0}", returnCode); System.exit(returnCode); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { horizontalSplitPane = new JSplitPane(); verticalSplitPane = new JSplitPane(); smsPanel = new SMSPanel(); queuePanel = new QueuePanel(); contactPanel = new ContactPanel(); statusPanel = new StatusPanel(); jSeparator1 = new JSeparator(); toolBar = new JToolBar(); compressButton = new JButton(); undoButton = new JButton(); redoButton = new JButton(); jSeparator2 = new Separator(); historyButton = new JButton(); jSeparator3 = new Separator(); configButton = new JButton(); exitButton = new JButton(); donateButton = new JButton(); menuBar = new JMenuBar(); programMenu = new JMenu(); configMenuItem = new JMenuItem(); exitMenuItem = new JMenuItem(); messageMenu = new JMenu(); undoMenuItem = new JMenuItem(); redoMenuItem = new JMenuItem(); jSeparator5 = new JSeparator(); compressMenuItem = new JMenuItem(); sendMenuItem = new JMenuItem(); toolsMenu = new JMenu(); historyMenuItem = new JMenuItem(); logMenuItem = new JMenuItem(); jSeparator4 = new JSeparator(); importMenuItem = new JMenuItem(); exportMenuItem = new JMenuItem(); helpMenu = new JMenu(); faqMenuItem = new JMenuItem(); getHelpMenuItem = new JMenuItem(); problemMenuItem = new JMenuItem(); translateMenuItem = new JMenuItem(); donateMenuItem = new JMenuItem(); helpSeparator = new JSeparator(); aboutMenuItem = new JMenuItem(); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); setTitle("Esmska"); // NOI18N setLocationByPlatform(true); addWindowFocusListener(new WindowFocusListener() { public void windowGainedFocus(WindowEvent evt) { formWindowGainedFocus(evt); } public void windowLostFocus(WindowEvent evt) { } }); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { formWindowClosing(evt); } }); horizontalSplitPane.setBorder(null); horizontalSplitPane.setResizeWeight(0.5); horizontalSplitPane.setContinuousLayout(true); horizontalSplitPane.setOneTouchExpandable(true); horizontalSplitPane.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE); verticalSplitPane.setBorder(null); verticalSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); verticalSplitPane.setResizeWeight(1.0); verticalSplitPane.setContinuousLayout(true); verticalSplitPane.setOneTouchExpandable(true); verticalSplitPane.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE); smsPanel.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.FALSE); verticalSplitPane.setLeftComponent(smsPanel); queuePanel.addValuedListener(new QueuePanelListener()); queuePanel.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.FALSE); verticalSplitPane.setRightComponent(queuePanel); horizontalSplitPane.setLeftComponent(verticalSplitPane); contactPanel.addActionListener(new ContactListener()); contactPanel.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.FALSE); horizontalSplitPane.setRightComponent(contactPanel); toolBar.setFloatable(false); toolBar.setRollover(true); toolBar.add(Box.createRigidArea(new Dimension(5, 1))); compressButton.setAction(smsPanel.getCompressAction()); compressButton.setFocusable(false); compressButton.setHideActionText(true); toolBar.add(compressButton); undoButton.setAction(smsPanel.getUndoAction()); undoButton.setToolTipText(l10n.getString("MainFrame.undoButton.toolTipText")); // NOI18N undoButton.setFocusable(false); undoButton.setHideActionText(true); toolBar.add(undoButton); redoButton.setAction(smsPanel.getRedoAction()); redoButton.setToolTipText(l10n.getString("MainFrame.redoButton.toolTipText")); // NOI18N redoButton.setFocusable(false); redoButton.setHideActionText(true); toolBar.add(redoButton); toolBar.add(jSeparator2); historyButton.setAction(Actions.getHistoryAction()); historyButton.setFocusable(false); historyButton.setHideActionText(true); Mnemonics.setLocalizedText(historyButton, l10n.getString("History")); historyButton.setToolTipText(historyButton.getToolTipText() + " (Ctrl+T)"); toolBar.add(historyButton); toolBar.add(jSeparator3); configButton.setAction(Actions.getConfigAction()); configButton.setToolTipText(Actions.getConfigAction().getValue(Action.NAME).toString()); configButton.setFocusable(false); configButton.setHideActionText(true); toolBar.add(configButton); exitButton.setAction(Actions.getQuitAction()); exitButton.setToolTipText(Actions.getQuitAction().getValue(Action.NAME).toString() + " (Ctrl+Q)"); exitButton.setFocusable(false); exitButton.setHideActionText(true); toolBar.add(exitButton); donateButton.setAction(Actions.getBrowseAction(Links.DONATE)); donateButton.setIcon(new ImageIcon(getClass().getResource("/esmska/resources/donate-32.png"))); // NOI18N Mnemonics.setLocalizedText(donateButton,l10n.getString("MainFrame.donateButton.text")); // NOI18N donateButton.setToolTipText(l10n.getString("AboutFrame.supportHyperlink.toolTipText")); // NOI18N donateButton.setFocusable(false); toolBar.add(Box.createHorizontalGlue()); toolBar.add(donateButton); for (Component comp : toolBar.getComponents()) { if (comp instanceof JButton) { JButton button = (JButton) comp; button.setMnemonic(0); button.putClientProperty("JButton.buttonType", "gradient"); } } Mnemonics.setLocalizedText(programMenu,l10n.getString("MainFrame.programMenu.text")); configMenuItem.setAction(Actions.getConfigAction()); programMenu.add(configMenuItem); exitMenuItem.setAction(Actions.getQuitAction()); programMenu.add(exitMenuItem); menuBar.add(programMenu); Mnemonics.setLocalizedText(messageMenu, l10n.getString("MainFrame.messageMenu.text")); // NOI18N undoMenuItem.setAction(smsPanel.getUndoAction()); messageMenu.add(undoMenuItem); redoMenuItem.setAction(smsPanel.getRedoAction()); messageMenu.add(redoMenuItem); messageMenu.add(jSeparator5); compressMenuItem.setAction(smsPanel.getCompressAction()); messageMenu.add(compressMenuItem); sendMenuItem.setAction(smsPanel.getSendAction()); messageMenu.add(sendMenuItem); menuBar.add(messageMenu); Mnemonics.setLocalizedText(toolsMenu,l10n.getString("MainFrame.toolsMenu.text")); historyMenuItem.setAction(Actions.getHistoryAction()); toolsMenu.add(historyMenuItem); logMenuItem.setAction(Actions.getLogAction()); toolsMenu.add(logMenuItem); toolsMenu.add(jSeparator4); importMenuItem.setAction(Actions.getImportAction()); toolsMenu.add(importMenuItem); exportMenuItem.setAction(Actions.getExportAction()); toolsMenu.add(exportMenuItem); menuBar.add(toolsMenu); Mnemonics.setLocalizedText(helpMenu,l10n.getString("MainFrame.helpMenu.text")); // NOI18N faqMenuItem.setAction(Actions.getBrowseAction(Links.FAQ)); Mnemonics.setLocalizedText(faqMenuItem, l10n.getString("MainFrame.faqMenuItem.text")); faqMenuItem.setToolTipText(l10n.getString("MainFrame.faqMenuItem.toolTipText")); // NOI18N helpMenu.add(faqMenuItem); getHelpMenuItem.setAction(Actions.getBrowseAction(Links.FORUM)); getHelpMenuItem.setIcon(new ImageIcon(getClass().getResource("/esmska/resources/getHelp-16.png"))); // NOI18N Mnemonics.setLocalizedText(getHelpMenuItem,l10n.getString("MainFrame.getHelpMenuItem.text")); // NOI18N getHelpMenuItem.setToolTipText(l10n.getString("MainFrame.getHelpMenuItem.toolTipText")); // NOI18N helpMenu.add(getHelpMenuItem); problemMenuItem.setAction(Actions.getBrowseAction(Links.ISSUES)); Mnemonics.setLocalizedText(problemMenuItem,l10n.getString("MainFrame.problemMenuItem.text")); // NOI18N problemMenuItem.setToolTipText(l10n.getString("MainFrame.problemMenuItem.toolTipText")); // NOI18N helpMenu.add(problemMenuItem); translateMenuItem.setAction(Actions.getBrowseAction(Links.TRANSLATE)); Mnemonics.setLocalizedText(translateMenuItem,l10n.getString("MainFrame.translateMenuItem.text")); // NOI18N translateMenuItem.setToolTipText(l10n.getString("MainFrame.translateMenuItem.toolTipText")); // NOI18N helpMenu.add(translateMenuItem); donateMenuItem.setAction(Actions.getBrowseAction(Links.DONATE)); donateMenuItem.setIcon(new ImageIcon(getClass().getResource("/esmska/resources/donate-16.png"))); // NOI18N Mnemonics.setLocalizedText(donateMenuItem,l10n.getString("MainFrame.donateMenuItem.text")); // NOI18N donateMenuItem.setToolTipText(l10n.getString("AboutFrame.supportHyperlink.toolTipText")); // NOI18N helpMenu.add(donateMenuItem); helpMenu.add(helpSeparator); aboutMenuItem.setAction(Actions.getAboutAction()); helpMenu.add(aboutMenuItem); menuBar.add(helpMenu); setJMenuBar(menuBar); GroupLayout layout = new GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(Alignment.LEADING) .addComponent(statusPanel, GroupLayout.DEFAULT_SIZE, 659, Short.MAX_VALUE) .addComponent(jSeparator1, GroupLayout.DEFAULT_SIZE, 659, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(horizontalSplitPane, GroupLayout.DEFAULT_SIZE, 647, Short.MAX_VALUE) .addContainerGap()) .addComponent(toolBar, GroupLayout.DEFAULT_SIZE, 659, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(Alignment.LEADING) .addGroup(Alignment.TRAILING, layout.createSequentialGroup() .addComponent(toolBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(horizontalSplitPane, GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(jSeparator1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(statusPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) ); pack(); }// </editor-fold>//GEN-END:initComponents private void formWindowClosing(WindowEvent evt) {//GEN-FIRST:event_formWindowClosing //if user clicked on close button (event non-null) and notification icon //installed or on mac, just hide the main window if (evt != null && (NotificationIcon.isInstalled() || RuntimeUtils.isMac())) { logger.fine("Hiding main window"); if (RuntimeUtils.isMac()) { this.setVisible(false); } else { NotificationIcon.toggleMainFrameVisibility(); } return; } //otherwise exit exit(); }//GEN-LAST:event_formWindowClosing private void formWindowGainedFocus(WindowEvent evt) {//GEN-FIRST:event_formWindowGainedFocus //work around bug http://code.google.com/p/esmska/issues/detail?id=182 //but don't do it on Mac (sigh): http://code.google.com/p/esmska/issues/detail?id=301 if (!RuntimeUtils.isMac()) { this.setVisible(true); } }//GEN-LAST:event_formWindowGainedFocus /** Save all user data * @return true if all saved ok; false otherwise */ private boolean saveAll() { logger.fine("Saving user data..."); boolean saveOk = true; //save all settings try { saveOk = saveConfig() && saveOk; saveOk = saveContacts() && saveOk; saveOk = saveQueue() && saveOk; saveOk = saveHistory() && saveOk; saveOk = saveKeyring() && saveOk; saveOk = saveGatewayProperties() && saveOk; return saveOk; } catch (Throwable t) { logger.log(Level.SEVERE, "Serious error during saving user data", t); return false; } } /** Saves history of sent sms */ private void createHistory(SMS sms) { boolean match = false; if (sms.getId() != null) { // there can be a previous SMS fragment List<Record> records = history.getRecords(); ListIterator<Record> li = records.listIterator(records.size()); while (li.hasPrevious()) { Record r = li.previous(); if (sms.getId().equals(r.getSmsId())) { // find the first one with matching ID and append text to it r.setText(r.getText() + sms.getText()); r.setDate(null); // update timestamp to current time match = true; break; } } } if (!match) { // no previous fragment, create a usual record Record record = new Record(sms.getNumber(), sms.getText(), sms.getGateway(), sms.getName(), sms.getSenderNumber(), sms.getSenderName(), null, sms.getId()); history.addRecord(record); } } /** save program configuration * @return true if saved ok; false otherwise */ private boolean saveConfig() { //save frame layout config.setMainDimension(this.getSize()); config.setHorizontalSplitPaneLocation(horizontalSplitPane.getDividerLocation()); config.setVerticalSplitPaneLocation(verticalSplitPane.getDividerLocation()); try { Context.persistenceManager.saveConfig(); return true; } catch (Exception ex) { logger.log(Level.WARNING, "Could not save config", ex); return false; } } /** load program configuration */ private void loadConfig() { logger.finer("Initializing according to config..."); //set frame layout Dimension mainDimension = config.getMainDimension(); Integer horizontalSplitPaneLocation = config.getHorizontalSplitPaneLocation(); Integer verticalSplitPaneLocation = config.getVerticalSplitPaneLocation(); if (mainDimension != null) { this.setSize(mainDimension); } if (horizontalSplitPaneLocation != null) { horizontalSplitPane.setDividerLocation(horizontalSplitPaneLocation); } if (verticalSplitPaneLocation != null) { verticalSplitPane.setDividerLocation(verticalSplitPaneLocation); } //set window centered if (config.isStartCentered()) { setLocationRelativeTo(null); } //select last contact if (history.getRecords().size() > 0) { contactPanel.setSelectedContact( history.getRecord(history.getRecords().size()-1).getName()); contactPanel.makeNiceSelection(); } //show notification icon if (config.isNotificationIconVisible()) { NotificationIcon.install(); } //show tip of the day if (config.isShowTips()) { showTipOfTheDay(); } } /** save contacts * @return true if saved ok; false otherwise */ private boolean saveContacts() { try { Context.persistenceManager.saveContacts(); return true; } catch (Exception ex) { logger.log(Level.WARNING, "Could not save contacts", ex); return false; } } /** save sms queue * @return true if saved ok; false otherwise */ private boolean saveQueue() { try { Context.persistenceManager.saveQueue(); return true; } catch (Exception ex) { logger.log(Level.WARNING, "Could not save queue", ex); return false; } } /** save sms history * @return true if saved ok; false otherwise */ private boolean saveHistory() { //erase old messages from history if demanded if (config.isReducedHistory()) { //computer last acceptable record time Calendar limitCal = Calendar.getInstance(); limitCal.add(Calendar.DAY_OF_MONTH, -config.getReducedHistoryCount()); Date limit = limitCal.getTime(); //remove old records history.removeRecordsOlderThan(limit); } try { Context.persistenceManager.saveHistory(); return true; } catch (Exception ex) { logger.log(Level.WARNING, "Could not save history", ex); return false; } } /** save keyring * @return true if saved ok; false otherwise */ private boolean saveKeyring() { try { Context.persistenceManager.saveKeyring(); return true; } catch (Exception ex) { logger.log(Level.WARNING, "Could not save keyring", ex); return false; } } /** Save gateway properties. * Skips saving if the gateways properties wasn't yet already loaded (they * are loaded asynchronously), then there is nothing to save. * @return true if saved ok or not even yet loaded; false otherwise */ private boolean saveGatewayProperties() { if (!Context.gatewaysLoaded()) { logger.log(Level.FINE, "Not saving gateway properties because they " + "were not yet even loaded."); return true; } try { Context.persistenceManager.saveGatewayProperties(); return true; } catch (Exception ex) { logger.log(Level.WARNING, "Could not save gateway properties", ex); return false; } } /** Listen for changes in the queue */ private class QueueListener implements ValuedListener<Queue.Events, SMS> { @Override public void eventOccured(ValuedEvent<Events, SMS> e) { switch (e.getEvent()) { case SENDING_SMS: sendingSMS(e.getValue()); break; case SMS_SENT: smsSent(e.getValue()); break; case SMS_SENDING_FAILED: smsFailed(e.getValue()); break; } } private void sendingSMS(SMS sms) { String gateway = sms.getGateway(); statusPanel.setTaskRunning(true); log.addRecord(new Log.Record( MessageFormat.format(l10n.getString("SMSSender.sending_message"), sms.getRecipient(), (gateway == null ? l10n.getString("SMSSender.no_gateway") : gateway)), null, Icons.STATUS_INFO)); } private void smsSent(SMS sms) { log.addRecord(new Log.Record(MessageFormat.format(l10n.getString("MainFrame.sms_sent"), sms.getRecipient()), null, Icons.STATUS_MESSAGE)); createHistory(sms); finish(sms); } private void smsFailed(SMS sms) { logger.log(Level.INFO, "Message could not be sent: {0}\nProblem: {1}", new Object[]{sms, sms.getProblem()}); log.addRecord(new Log.Record(MessageFormat.format(l10n.getString("MainFrame.sms_failed"), sms.getRecipient()), null, Icons.STATUS_WARNING)); //show the dialog logger.fine("Showing reason why SMS sending failed..."); GatewayMessageFrame gatewayMessageFrame = GatewayMessageFrame.getInstance(); gatewayMessageFrame.addErrorMsg(sms); finish(sms); } private void finish(SMS sms) { //show gateway message if present if (StringUtils.isNotEmpty(sms.getSupplMsg())) { log.addRecord(new Log.Record(sms.getGateway() + ": " + sms.getSupplMsg(), null, Icons.STATUS_MESSAGE)); } //disable task indicator if (!smsSender.isRunning()) { statusPanel.setTaskRunning(false); } } } /** Listens for events from queue panel */ private class QueuePanelListener implements ValuedListener<QueuePanel.Events, SMS> { @Override public void eventOccured(ValuedEvent<QueuePanel.Events, SMS> e) { switch (e.getEvent()) { //edit sms in queue case SMS_EDIT_REQUESTED: SMS sms = e.getValue(); if (sms == null) { return; } //if currently writing some sms then ask whether to overwrite text if (StringUtils.isNotEmpty(smsPanel.getText().trim())) { String replaceOption = l10n.getString("Replace"); String cancelOption = l10n.getString("Cancel"); String[] options = new String[]{cancelOption, replaceOption}; options = RuntimeUtils.sortDialogOptions(options); int result = JOptionPane.showOptionDialog(MainFrame.this, new JLabel(l10n.getString("QueuePanel.replaceSms")), null, JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, replaceOption); //if not chosen to replace don't do anything if (result != ArrayUtils.indexOf(options, replaceOption)) { return; } } //edit the message SMS smsToEdit = queue.extractSMS(sms.getId(), true); smsPanel.setSMS(smsToEdit); break; } } } /** Listens for changes in contact list */ private class ContactListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { switch (e.getID()) { case ContactPanel.ACTION_CONTACT_SELECTION_CHANGED: smsPanel.setContacts(contactPanel.getSelectedContacts()); break; case ContactPanel.ACTION_CONTACT_CHOSEN: smsPanel.requestFocusInWindow(); break; } } } /** Listens for events from update checker */ private class UpdateListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { int event = e.getID(); switch (event) { case UpdateChecker.ACTION_PROGRAM_UPDATE_AVAILABLE: announceProgram(); break; case UpdateChecker.ACTION_GATEWAY_UPDATE_AVAILABLE: updateGateways(); break; case UpdateChecker.ACTION_PROGRAM_AND_GATEWAY_UPDATE_AVAILABLE: announceProgram(); updateGateways(); break; } //schedule next check Timer timer = new Timer(UpdateChecker.AUTO_CHECK_INTERVAL * 1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateChecker.checkForUpdates(); } }); timer.setRepeats(false); timer.start(); } /** Announce program updates available */ private void announceProgram() { String message = MessageFormat.format(l10n.getString("MainFrame.new_program_version"), updateChecker.getLatestProgramVersion()); log.addRecord(new Log.Record(MiscUtils.stripHtml(message), null, Icons.STATUS_UPDATE_IMPORTANT)); statusPanel.setStatusMessage(message, null, Icons.STATUS_UPDATE_IMPORTANT, true); //on click open program homepage in browser statusPanel.installClickHandler(new Runnable() { @Override public void run() { Action browseAction = Actions.getBrowseAction(Links.DOWNLOAD); browseAction.actionPerformed(null); } }, l10n.getString("Update.browseDownloads")); } /** perform gateway update */ private void updateGateways() { UpdateInstaller.getInstance().installNewGateways(); } } /** Thread used when program is externally forced to shut down (logout, SIGTERM, etc) */ private class ShutdownThread extends Thread { @Override public void run() { logger.fine("Program closing down..."); saveAll(); } } // Variables declaration - do not modify//GEN-BEGIN:variables private JMenuItem aboutMenuItem; private JButton compressButton; private JMenuItem compressMenuItem; private JButton configButton; private JMenuItem configMenuItem; private ContactPanel contactPanel; private JButton donateButton; private JMenuItem donateMenuItem; private JButton exitButton; private JMenuItem exitMenuItem; private JMenuItem exportMenuItem; private JMenuItem faqMenuItem; private JMenuItem getHelpMenuItem; private JMenu helpMenu; private JSeparator helpSeparator; private JButton historyButton; private JMenuItem historyMenuItem; private JSplitPane horizontalSplitPane; private JMenuItem importMenuItem; private JSeparator jSeparator1; private Separator jSeparator2; private Separator jSeparator3; private JSeparator jSeparator4; private JSeparator jSeparator5; private JMenuItem logMenuItem; private JMenuBar menuBar; private JMenu messageMenu; private JMenuItem problemMenuItem; private JMenu programMenu; private QueuePanel queuePanel; private JButton redoButton; private JMenuItem redoMenuItem; private JMenuItem sendMenuItem; private SMSPanel smsPanel; private StatusPanel statusPanel; private JToolBar toolBar; private JMenu toolsMenu; private JMenuItem translateMenuItem; private JButton undoButton; private JMenuItem undoMenuItem; private JSplitPane verticalSplitPane; // End of variables declaration//GEN-END:variables }