From 48ce1e69166c0a5b4635cb33a01818261f1d06fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20P=C3=A1ral?= Date: Wed, 23 Apr 2014 22:14:06 +0200 Subject: [PATCH] Move signature to message start Thanks to Petr Matas . Pull request: https://github.com/kparal/esmska/pull/8 --- src/esmska/data/Config.java | 2 +- src/esmska/data/Envelope.java | 21 ++++- src/esmska/data/Gateway.java | 23 +++--- src/esmska/gui/ConfigFrame.form | 43 +++++----- src/esmska/gui/ConfigFrame.java | 115 ++++++++++++++++++++++++--- src/esmska/gui/SMSPanel.java | 78 +++++++++--------- src/esmska/resources/l10n.properties | 1 + src/esmska/update/LegacyUpdater.java | 59 +++++++++----- 8 files changed, 236 insertions(+), 106 deletions(-) diff --git a/src/esmska/data/Config.java b/src/esmska/data/Config.java index 7d54f51c..036ea54f 100644 --- a/src/esmska/data/Config.java +++ b/src/esmska/data/Config.java @@ -24,7 +24,7 @@ public class Config extends Object implements Serializable { /** mutex whether config already loaded from disk */ private static boolean loaded = false; - private static final String LATEST_VERSION = "1.6"; + private static final String LATEST_VERSION = "1.6.99"; private static final Logger logger = Logger.getLogger(Config.class.getName()); private String version = ""; diff --git a/src/esmska/data/Envelope.java b/src/esmska/data/Envelope.java index bd40f553..9157d32b 100644 --- a/src/esmska/data/Envelope.java +++ b/src/esmska/data/Envelope.java @@ -77,6 +77,21 @@ public int getMaxTextLength() { return min; } + /** How many characters at the message start are occupied by the prefix + * (i.e. sender name) + */ + public int getPrefixLength() { + int max = 0; + for (Contact c : contacts) { + Gateway gateway = gateways.get(c.getGateway()); + if (gateway == null) { + continue; + } + max = Math.max(max, gateway.getSenderName().length()); + } + return max; + } + /** get length of one sms * @return length of one sms or -1 when sms length is unspecified */ @@ -143,10 +158,10 @@ public ArrayList generate() { String msgText = text; // add user signature to the message if (gateway != null) { - String signature = gateway.getSenderNameSuffix(); + String signature = gateway.getSenderName(); // only if signature is not already added - if (!msgText.trim().toLowerCase().endsWith(signature.trim().toLowerCase())) { - msgText += signature; + if (!msgText.trim().toLowerCase().startsWith(signature.trim().toLowerCase())) { + msgText = signature + msgText; } } String messageId = SMS.generateID(); diff --git a/src/esmska/data/Gateway.java b/src/esmska/data/Gateway.java index b79db2b2..c6442ecc 100644 --- a/src/esmska/data/Gateway.java +++ b/src/esmska/data/Gateway.java @@ -161,13 +161,13 @@ public void setConfig(GatewayConfig config) { this.config = config; } - /** Get sender name signature suffix that should be appended to the message + /** Get sender name signature that should be prepended to the message * before it is sent. - * @return empty string if gateway appends the name signature automatically - * or user does not want any signature to be appended; otherwise user name - * signature prepended with a newline character + * @return empty string if gateway adds the name signature automatically + * or user does not want any signature to be added; otherwise user name + * signature */ - public String getSenderNameSuffix() { + public String getSenderName() { if (hasFeature(Feature.SENDER_NAME)) { // gateway will append sender name signature automatically return ""; @@ -180,14 +180,13 @@ public String getSenderNameSuffix() { return ""; } - // user wants to append his name signature - // prepend it with a single space to separate it from text content - String suffix = "\n" + signature.getUserName(); + // user wants to add his name signature + String result = signature.getUserName(); // remove accents if required if (Config.getInstance().isRemoveAccents()) { - suffix = MiscUtils.removeAccents(suffix); + result = MiscUtils.removeAccents(result); } - return suffix; + return result; } @Override @@ -278,8 +277,8 @@ public int getSignatureExtraLength() { // gateway will append its own string return signatureExtraLength; } else { - // we will append a newline character before the sender name - return 1; + // we will not insert anything between signature and message + return 0; } } diff --git a/src/esmska/gui/ConfigFrame.form b/src/esmska/gui/ConfigFrame.form index 0d2f4c33..f1aa8100 100644 --- a/src/esmska/gui/ConfigFrame.form +++ b/src/esmska/gui/ConfigFrame.form @@ -1,4 +1,4 @@ - +
@@ -16,10 +16,11 @@ + - + @@ -40,13 +41,13 @@ - + - + - + @@ -56,7 +57,7 @@ - + @@ -95,17 +96,16 @@ - - - - - - - - + + + + + + + @@ -544,20 +544,20 @@ - + - + - + @@ -567,14 +567,14 @@ - + - + @@ -739,6 +739,11 @@ + + + + + diff --git a/src/esmska/gui/ConfigFrame.java b/src/esmska/gui/ConfigFrame.java index 4a451456..0bd2b69a 100644 --- a/src/esmska/gui/ConfigFrame.java +++ b/src/esmska/gui/ConfigFrame.java @@ -24,7 +24,6 @@ import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.awt.event.WindowFocusListener; import java.io.IOException; import java.util.ArrayList; import java.util.logging.Level; @@ -69,6 +68,10 @@ import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; @@ -82,8 +85,11 @@ import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; +import javax.swing.JToolTip; import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; +import javax.swing.Popup; +import javax.swing.PopupFactory; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; @@ -117,11 +123,17 @@ public class ConfigFrame extends javax.swing.JFrame { private static final HashMap originalSettings = new HashMap(); private final GatewayTableModel gwTableModel = new GatewayTableModel(); private final GatewaySelectionListener gwSelectionListener = new GatewaySelectionListener(); + private Popup senderNamePopup; public enum Tabs { GENERAL, APPEARANCE, GATEWAYS, PRIVACY, CONNECTION } + /** Used for senderNamePopup */ + private enum PopupUpdateType { + SHOW, UPDATE, HIDE + } + /** Creates new form ConfigFrame */ public ConfigFrame() { initComponents(); @@ -207,6 +219,12 @@ public void onUpdate(DocumentEvent e) { updateSenderNumberWarnLabel(); } }); + senderNameTextField.getDocument().addDocumentListener(new AbstractDocumentListener() { + @Override + public void onUpdate(DocumentEvent e) { + updateSenderNamePopup(PopupUpdateType.UPDATE); + } + }); DocumentListener keyringListener = new AbstractDocumentListener() { @Override public void onUpdate(DocumentEvent e) { @@ -371,6 +389,37 @@ private void updateSenderNumberWarnLabel() { } } + /** Update the text in senderNamePopup and its visibility */ + private void updateSenderNamePopup(PopupUpdateType updateType) { + if (senderNamePopup != null) { + senderNamePopup.hide(); + } + + if (updateType == PopupUpdateType.SHOW || + (updateType == PopupUpdateType.UPDATE && senderNamePopup != null)) { + String signature = MiscUtils.escapeHtml(senderNameTextField.getText()); + JToolTip toolTip = new JToolTip(); + toolTip.setTipText(MessageFormat.format( + l10n.getString("ConfigFrame.senderNamePopup.exampleText"), + signature)); + toolTip.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (senderNamePopup != null) { + senderNamePopup.hide(); + } + } + }); + int x = senderNameLabel.getLocationOnScreen().x + senderNameTextField.getHeight() / 2; + int y = senderNameTextField.getLocationOnScreen().y + senderNameTextField.getHeight() - 2; + senderNamePopup = PopupFactory.getSharedInstance().getPopup( + senderNameTextField, toolTip, x, y); + senderNamePopup.show(); + } else { + senderNamePopup = null; + } + } + /** Save all properties of the currently selected signature. */ private void updateSignature() { if (!fullyInicialized) { @@ -466,19 +515,18 @@ public void tableChanged(TableModelEvent e) { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setTitle(l10n.getString("ConfigFrame.title")); // NOI18N - 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); } }); Mnemonics.setLocalizedText(removeAccentsCheckBox, l10n.getString("ConfigFrame.removeAccentsCheckBox.text")); + addComponentListener(new ComponentAdapter() { + public void componentShown(ComponentEvent evt) { + formComponentShown(evt); + } + }); + removeAccentsCheckBox.setToolTipText(l10n.getString("ConfigFrame.removeAccentsCheckBox.toolTipText")); // NOI18N Binding binding = Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, config, ELProperty.create("${removeAccents}"), removeAccentsCheckBox, BeanProperty.create("selected")); @@ -767,6 +815,20 @@ public void actionPerformed(ActionEvent evt) { senderNameTextField.setColumns(12); senderNameTextField.setToolTipText(l10n.getString("ConfigFrame.senderNameTextField.toolTipText")); // NOI18N Mnemonics.setLocalizedText(demandDeliveryReportCheckBox, l10n.getString("ConfigFrame.demandDeliveryReportCheckBox.text")); + senderNameTextField.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent evt) { + senderNameTextFieldFocusGained(evt); + } + public void focusLost(FocusEvent evt) { + senderNameTextFieldFocusLost(evt); + } + }); + senderNameTextField.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent evt) { + senderNameTextFieldKeyTyped(evt); + } + }); + demandDeliveryReportCheckBox.setToolTipText(l10n.getString("ConfigFrame.demandDeliveryReportCheckBox.toolTipText")); // NOI18N demandDeliveryReportCheckBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { @@ -1302,9 +1364,9 @@ private void formWindowClosing(WindowEvent evt) {//GEN-FIRST:event_formWindowClo } }//GEN-LAST:event_formWindowClosing -private void formWindowGainedFocus(WindowEvent evt) {//GEN-FIRST:event_formWindowGainedFocus +private void formComponentShown(ComponentEvent evt) {//GEN-FIRST:event_formComponentShown closeButton.requestFocusInWindow(); -}//GEN-LAST:event_formWindowGainedFocus +}//GEN-LAST:event_formComponentShown private void signatureComboBoxActionPerformed(ActionEvent evt) {//GEN-FIRST:event_signatureComboBoxActionPerformed boolean oldInit = fullyInicialized; @@ -1378,6 +1440,37 @@ private void demandDeliveryReportCheckBoxActionPerformed(ActionEvent evt) {//GEN Gateway gateway = gwTableModel.getGateway(row); gateway.getConfig().setReceipt(demandDeliveryReportCheckBox.isSelected()); }//GEN-LAST:event_demandDeliveryReportCheckBoxActionPerformed + + private void senderNameTextFieldFocusGained(FocusEvent evt) {//GEN-FIRST:event_senderNameTextFieldFocusGained + updateSenderNamePopup(PopupUpdateType.SHOW); + }//GEN-LAST:event_senderNameTextFieldFocusGained + + private void senderNameTextFieldFocusLost(FocusEvent evt) {//GEN-FIRST:event_senderNameTextFieldFocusLost + updateSenderNamePopup(PopupUpdateType.HIDE); + }//GEN-LAST:event_senderNameTextFieldFocusLost + + /** Adds a colon to the end of senderNameTextField + * if the first characted has just been typed. */ + private void senderNameTextFieldKeyTyped(KeyEvent evt) {//GEN-FIRST:event_senderNameTextFieldKeyTyped + char c = evt.getKeyChar(); + if (c < ' ' || c == KeyEvent.CHAR_UNDEFINED) { + return; + } + if (senderNameTextField.getText().length() != 0) { + return; + } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (senderNameTextField.getText().length() == 0) { + return; + } + String typed = senderNameTextField.getText(); + senderNameTextField.setText(typed + ":"); + senderNameTextField.setCaretPosition(typed.length()); + } + }); + }//GEN-LAST:event_senderNameTextFieldKeyTyped private class LaFComboRenderer extends DefaultListCellRenderer { private final ListCellRenderer lafRenderer = new JList().getCellRenderer(); @@ -1729,5 +1822,5 @@ public void valueChanged(ListSelectionEvent e) { private JCheckBox windowCenteredCheckBox; private BindingGroup bindingGroup; // End of variables declaration//GEN-END:variables - + } diff --git a/src/esmska/gui/SMSPanel.java b/src/esmska/gui/SMSPanel.java index 0f9f43b5..885b86be 100644 --- a/src/esmska/gui/SMSPanel.java +++ b/src/esmska/gui/SMSPanel.java @@ -336,55 +336,52 @@ public Action getSendAction() { private void updateProgressBars() { int currentLength = smsTextPane.getText().length(); int smsLength = envelope.getSMSLength(); + int maxTextLength = envelope.getMaxTextLength(); - //set maximums - singleProgressBar.setMaximum(Math.max(smsLength, 0)); - fullProgressBar.setMaximum(envelope.getMaxTextLength()); + //set limits + fullProgressBar.setMaximum(maxTextLength); - //if we are at the end of the whole message, the current message length - //can be lesser than usual if (smsLength > 0) { - int remainder = envelope.getMaxTextLength() % smsLength; - if (envelope.getMaxTextLength() - currentLength < remainder) { - //we have crossed the remainder border, let's update maximum on progress bar - singleProgressBar.setMaximum(remainder); - } + int prefixLength = envelope.getPrefixLength(); + int min = (currentLength + prefixLength - 1) / smsLength * smsLength - prefixLength; + int max = min + smsLength; + + if (min < 0) min = 0; + while (max <= 0) max += smsLength; + if (max > maxTextLength) max = maxTextLength; + + singleProgressBar.setMinimum(min); + singleProgressBar.setMaximum(max); + } else { + singleProgressBar.setMinimum(0); + singleProgressBar.setMaximum(maxTextLength); } //set values fullProgressBar.setValue(currentLength); - singleProgressBar.setValue(smsLength > 0 ? currentLength % smsLength : 0); - //on the border counts we want progress bar full instead of empty - if (singleProgressBar.getValue() == 0 && currentLength > 0) { - singleProgressBar.setValue(singleProgressBar.getMaximum()); - } + singleProgressBar.setValue(currentLength); //set tooltips - int current = singleProgressBar.getValue(); - int max = singleProgressBar.getMaximum(); - boolean infinite = (max == Integer.MAX_VALUE); - if (infinite) { - //don't show really big numbers when there is no limit on characters - singleProgressBar.setToolTipText(MessageFormat.format( - l10n.getString("SMSPanel.singleProgressBar"), - current, "∞", "∞")); - } else { - singleProgressBar.setToolTipText(MessageFormat.format( - l10n.getString("SMSPanel.singleProgressBar"), - current, max, (max - current))); - } - current = fullProgressBar.getValue(); - max = fullProgressBar.getMaximum(); - infinite = (max == Integer.MAX_VALUE); - if (infinite) { + updateProgressBarToolTip(fullProgressBar, "SMSPanel.fullProgressBar"); + updateProgressBarToolTip(singleProgressBar, "SMSPanel.singleProgressBar"); + } + + /** Updates the tooltip for the progress bar according to its current value and limits + * @param bar The progress bar + * @param resourceId The localization string name + */ + private void updateProgressBarToolTip(JProgressBar bar, String resourceId) + { + int used = bar.getValue() - bar.getMinimum(); + if (bar.getMaximum() == Integer.MAX_VALUE) { //don't show really big numbers when there is no limit on characters - fullProgressBar.setToolTipText(MessageFormat.format( - l10n.getString("SMSPanel.fullProgressBar"), - current, "∞", "∞")); + bar.setToolTipText(MessageFormat.format(l10n.getString(resourceId), + used, "∞", "∞")); } else { - fullProgressBar.setToolTipText(MessageFormat.format( - l10n.getString("SMSPanel.fullProgressBar"), - current, max, (max - current))); + int capacity = bar.getMaximum() - bar.getMinimum(); + int remaining = bar.getMaximum() - bar.getValue(); + bar.setToolTipText(MessageFormat.format(l10n.getString(resourceId), + used, capacity, remaining)); } } @@ -971,13 +968,14 @@ private void updateUI() { /** color parts of sms */ private void colorDocument(int from, int length) { int smsLength = envelope.getSMSLength(); + int prefixLength = envelope.getPrefixLength(); while (from < length) { int to = 0; if (smsLength <= 0) { //unspecified sms length, color it all with same color to = length - 1; } else { - to = ((from / smsLength) + 1) * smsLength - 1; + to = (((from + prefixLength) / smsLength) + 1) * smsLength - 1 - prefixLength; } to = to < length-1 ? to : length-1; doc.setCharacterAttributes(from,to-from+1,getStyle(from),false); @@ -990,7 +988,7 @@ private Style getStyle(int offset) { //unspecified sms length return regular; } - if ((offset / envelope.getSMSLength()) % 2 == 0) { + if (((offset + envelope.getPrefixLength()) / envelope.getSMSLength()) % 2 == 0) { //even sms return regular; } else { diff --git a/src/esmska/resources/l10n.properties b/src/esmska/resources/l10n.properties index 11e2ccfb..40a13d04 100644 --- a/src/esmska/resources/l10n.properties +++ b/src/esmska/resources/l10n.properties @@ -497,3 +497,4 @@ GatewayProblem.WRONG_NUMBER.help=The recipient number is either not supported by GatewayProblem.WRONG_SIGNATURE.help=The sender number or the sender name is in wrong format (maybe the number is not in an international format?). Visit the gateway configuration and fix the problem, then try to send the message again. If problems persist, visit {1} and find out why the gateway keeps rejecting your signature. GatewayErrorMessage.retryButton.text=&Send again GatewayErrorMessage.retryButton.toolTipText=Please note that this button will unpause your SMS queue.
\nThat means that more messages might be re-sent, not just this one. +ConfigFrame.senderNamePopup.exampleText=Preview:
{0}<message text> diff --git a/src/esmska/update/LegacyUpdater.java b/src/esmska/update/LegacyUpdater.java index e47235c2..4042d8d8 100644 --- a/src/esmska/update/LegacyUpdater.java +++ b/src/esmska/update/LegacyUpdater.java @@ -1,6 +1,7 @@ package esmska.update; import com.csvreader.CsvReader; +import esmska.Context; import esmska.data.Config; import esmska.data.CountryPrefix; import esmska.data.Keyring; @@ -50,26 +51,6 @@ public static void update() throws Exception { logger.log(Level.INFO, "Updating from legacy version {0} to current version {1}", new Object[]{version, Config.getLatestVersion()}); - //changes to 1.4 - if (Config.compareProgramVersions(version, "1.4") < 0) { - //add message ID to queue - logger.fine("Updating queue to add message IDs..."); - try { - Field queueFileField = PersistenceManager.class.getDeclaredField("queueFile"); - queueFileField.setAccessible(true); - File queueFile = (File) queueFileField.get(null); - - List lines = FileUtils.readLines(queueFile, "UTF-8"); - ArrayList newLines = new ArrayList(); - for (String line : lines) { - newLines.add(line + ","); - } - FileUtils.writeLines(queueFile, "UTF-8", newLines); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Updating queue file failed", ex); - } - } - //changes to 0.8.0 if (Config.compareProgramVersions(version, "0.8.0") < 0) { //set country prefix from locale @@ -148,5 +129,43 @@ public static void update() throws Exception { defaultSig.setUserNumber(senderNumber); } } + + //changes to 1.4 + if (Config.compareProgramVersions(version, "1.4") < 0) { + //add message ID to queue + logger.fine("Updating queue to add message IDs..."); + try { + Field queueFileField = PersistenceManager.class.getDeclaredField("queueFile"); + queueFileField.setAccessible(true); + File queueFile = (File) queueFileField.get(null); + + List lines = FileUtils.readLines(queueFile, "UTF-8"); + ArrayList newLines = new ArrayList(); + for (String line : lines) { + newLines.add(line + ","); + } + FileUtils.writeLines(queueFile, "UTF-8", newLines); + } catch (Exception ex) { + logger.log(Level.SEVERE, "Updating queue file failed", ex); + } + } + + //changes to 1.7 + if (Config.compareProgramVersions(version, "1.6") <= 0) { + // change signature suffix to signature prefix -> append a colon + // to the signature + Context.persistenceManager.loadGateways(); + Context.persistenceManager.loadGatewayProperties(); + ArrayList sigList = new ArrayList(); + sigList.addAll(Signatures.getInstance().getAll()); + sigList.addAll(Signatures.getInstance().getSpecial()); + for (Signature signature : sigList) { + String userName = signature.getUserName(); + if (StringUtils.isNotEmpty(userName)) { + signature.setUserName(userName + ":"); + } + } + Context.persistenceManager.saveGatewayProperties(); + } } }