diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d4c751..1cf99ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,4 +34,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -B -Dsonar.coverage.exclusions=**/WindowsShortcut.java -P sonar org.jacoco:jacoco-maven-plugin:prepare-agent test org.jacoco:jacoco-maven-plugin:report org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dorg.slf4j.simpleLogger.log.org.mockserver.log=warn -Dsonar.projectKey=fathzer_ajlib \ No newline at end of file + run: mvn -B -Dsonar.coverage.exclusions=**/WindowsShortcut.java,com/fathzer/soft/ajlib/swing/demo/** -P sonar org.jacoco:jacoco-maven-plugin:prepare-agent test org.jacoco:jacoco-maven-plugin:report org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dorg.slf4j.simpleLogger.log.org.mockserver.log=warn -Dsonar.projectKey=fathzer_ajlib \ No newline at end of file diff --git a/.gitignore b/.gitignore index 24d6437..5f172ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/target/ +*/target/ +*/dependency-reduced-pom.xml diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 29abf99..99f26c0 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,6 +1,2 @@ eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/main/resources=UTF-8 -encoding//src/test/java=UTF-8 -encoding//src/test/resources=UTF-8 encoding/=UTF-8 diff --git a/overview.html b/ajlib/overview.html similarity index 98% rename from overview.html rename to ajlib/overview.html index 8bd9bdd..2e48c2c 100644 --- a/overview.html +++ b/ajlib/overview.html @@ -1,25 +1,25 @@ - - -Overview - - - -AJlib is a simple java library with Swing widgets, utilities and other stuff.
-
-This library tries to simplify the development of Swing applications ... which is sometime a nightmare!
-It contains the classes I developed during the Yapbam project in -order to deal with the problems I encountered.
-
-The source code is available here: https://github.com/fathzer/ajlib
-For maven users, AJLib is also available in Maven central:
+ + +Overview + + + +AJlib is a simple java library with Swing widgets, utilities and other stuff.
+
+This library tries to simplify the development of Swing applications ... which is sometime a nightmare!
+It contains the classes I developed during the Yapbam project in +order to deal with the problems I encountered.
+
+The source code is available here: https://github.com/fathzer/ajlib
+For maven users, AJLib is also available in Maven central:
<dependency>
  <groupId>com.fathzer</groupId>
  <artifactId>ajlib</artifactId>
-  <version>0.3.16</version>
+  <version>0.3.16</version>
</dependency>


-It is released under Apache 2 License (information is available there). -It requires Java 7+. +It is released under Apache 2 License (information is available there). +It requires Java 7+.
\ No newline at end of file diff --git a/ajlib/pom.xml b/ajlib/pom.xml new file mode 100644 index 0000000..59242aa --- /dev/null +++ b/ajlib/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + com.fathzer + ajlib-parent-pom + 1.0.0 + + ajlib + 0.3.16 + + jar + + A-JLib + + + + com.fathzer + jlocal + 1.0.0 + + + org.codehaus.mojo + animal-sniffer-annotations + ${animal-sniffer-version} + provided + + + \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/Browser.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/Browser.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/Browser.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/Browser.java index 3663341..b8a2e91 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/Browser.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/Browser.java @@ -1,49 +1,49 @@ -package com.fathzer.soft.ajlib.swing; - -import java.awt.Component; -import java.awt.Desktop; -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; -import java.io.IOException; -import java.net.URI; - -import javax.swing.JOptionPane; - -import com.fathzer.jlocal.Formatter; -import com.fathzer.soft.ajlib.swing.framework.Application; - - -/** A class that is able to open a web browser to display an URI. - *
The main advantage of this class is to display an alert dialog if java is not able to open the browser. - */ -public abstract class Browser { - private Browser() { - //To prevent instance to be constructed - } - - /** Displays an URI in a browser. - * @param uri The uri to display - * @param parent The parent component of the dialog displayed if browser is not available. - * @param errorDialogTitle The title of the dialog displayed if browser is not available. - */ - public static void show(URI uri, Component parent, String errorDialogTitle) { - try { - Desktop.getDesktop().browse(uri); - } catch (IOException | UnsupportedOperationException e) { - error(uri, parent, errorDialogTitle); - } - } - - private static void error(URI uri, Component parent, String errorDialogTitle) { - String url = uri.toString(); - String message = Formatter.format(Application.getString("Browser.unsupported.message", parent.getLocale()), url); //$NON-NLS-1$ - StringSelection stringSelection = new StringSelection(url); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(stringSelection, null); - if (errorDialogTitle == null) { - errorDialogTitle = ""; - } - JOptionPane.showMessageDialog(Utils.getOwnerWindow(parent), message, errorDialogTitle, JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$ - } -} +package com.fathzer.soft.ajlib.swing; + +import java.awt.Component; +import java.awt.Desktop; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.io.IOException; +import java.net.URI; + +import javax.swing.JOptionPane; + +import com.fathzer.jlocal.Formatter; +import com.fathzer.soft.ajlib.swing.framework.Application; + + +/** A class that is able to open a web browser to display an URI. + *
The main advantage of this class is to display an alert dialog if java is not able to open the browser. + */ +public abstract class Browser { + private Browser() { + //To prevent instance to be constructed + } + + /** Displays an URI in a browser. + * @param uri The uri to display + * @param parent The parent component of the dialog displayed if browser is not available. + * @param errorDialogTitle The title of the dialog displayed if browser is not available. + */ + public static void show(URI uri, Component parent, String errorDialogTitle) { + try { + Desktop.getDesktop().browse(uri); + } catch (IOException | UnsupportedOperationException e) { + error(uri, parent, errorDialogTitle); + } + } + + private static void error(URI uri, Component parent, String errorDialogTitle) { + String url = uri.toString(); + String message = Formatter.format(Application.getString("Browser.unsupported.message", parent.getLocale()), url); //$NON-NLS-1$ + StringSelection stringSelection = new StringSelection(url); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, null); + if (errorDialogTitle == null) { + errorDialogTitle = ""; + } + JOptionPane.showMessageDialog(Utils.getOwnerWindow(parent), message, errorDialogTitle, JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$ + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/ButtonGroup.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/ButtonGroup.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/ButtonGroup.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/ButtonGroup.java index f4077c5..7e31502 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/ButtonGroup.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/ButtonGroup.java @@ -1,141 +1,141 @@ -package com.fathzer.soft.ajlib.swing; - -import java.awt.event.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Observable; - -import javax.swing.AbstractButton; - -/** - * This class is, like javax.swing.ButtonGroup, used to create a multiple-exclusion scope for - * a set of buttons. Creating a set of buttons with the same ButtonGroup object means that - * turning "on" one of those buttons turns off all other buttons in the group. - *

- * The main difference between this group and the swing one is that you can deselected buttons by clicking on them - * and have a group without no selected button (see {@link #setAutoDeselect(boolean)}).
- * Another difference is that this class extends the java.util.Observable class and calls its observers update method - * when the selected button changes. - *

- */ -public class ButtonGroup extends Observable { - /** - * The buttons list - */ - private List buttons; - /** - * The current selection. - */ - private AbstractButton selected; - private ItemListener listener; - private boolean autoDeselect; - - /** - * Constructor. - */ - public ButtonGroup() { - this.autoDeselect = false; - this.buttons = new ArrayList<>(); - this.selected = null; - this.listener = new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - AbstractButton b = (AbstractButton) e.getItem(); - if (e.getStateChange()==ItemEvent.SELECTED) { - setSelected(b); - } else if (selected==b) { - if (autoDeselect) { - setSelected(null); - } else { - selected.setSelected(true); - } - } - } - }; - } - - /** - * Adds a button to this group. - * @param b the button to be added - * @exception NullPointerException if the button is null - */ - public void add(AbstractButton b) { - if (b==null) { - throw new NullPointerException(); - } - buttons.add(b); - b.addItemListener(listener); - if (b.isSelected()) { - setSelected(b); - } - } - - /** - * Removes a button from this group. - * @param b the button to be removed - * @exception IllegalArgumentException if the button is unknown in this group - * @exception NullPointerException if the button is null - */ - public void remove(AbstractButton b) { - if (b == null) { - throw new NullPointerException(); - } - if (this.buttons.remove(b)) { - b.removeItemListener(this.listener); - if (this.selected==b) { - setSelected(null); - } - } else { - throw new IllegalArgumentException(); - } - } - - /** - * Clears the selection such that none of the buttons in the - * ButtonGroup are selected. - */ - public void clearSelection() { - if (selected != null) { - setSelected(null); - } - } - - /** - * Returns the selected button. - * @return the selected button - */ - public AbstractButton getSelected() { - return this.selected; - } - - /** Changes the selected button. - * @param b the button to be selected (null deselects all buttons) - * @exception IllegalArgumentException if the button is not in this group - */ - public void setSelected(AbstractButton b) { - if (b==this.selected) { - return; - } - if ((b!=null) && (!this.buttons.contains(b))) { - throw new IllegalArgumentException(); - } - AbstractButton old = this.selected; - this.selected = b; - if (b!=null) { - b.setSelected(true); - } - if (old!=null) { - old.setSelected(false); - } - this.setChanged(); - this.notifyObservers(this.selected); - } - - /** Activates/deactivates the ability to deselect a selected button by cliking onto it. - *
The default behavior is "a click on a selected button does nothing". - * @param autoDeselect true to have selected button to be deactivated when clicked - */ - public void setAutoDeselect(boolean autoDeselect) { - this.autoDeselect = autoDeselect; - } -} +package com.fathzer.soft.ajlib.swing; + +import java.awt.event.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Observable; + +import javax.swing.AbstractButton; + +/** + * This class is, like javax.swing.ButtonGroup, used to create a multiple-exclusion scope for + * a set of buttons. Creating a set of buttons with the same ButtonGroup object means that + * turning "on" one of those buttons turns off all other buttons in the group. + *

+ * The main difference between this group and the swing one is that you can deselected buttons by clicking on them + * and have a group without no selected button (see {@link #setAutoDeselect(boolean)}).
+ * Another difference is that this class extends the java.util.Observable class and calls its observers update method + * when the selected button changes. + *

+ */ +public class ButtonGroup extends Observable { + /** + * The buttons list + */ + private List buttons; + /** + * The current selection. + */ + private AbstractButton selected; + private ItemListener listener; + private boolean autoDeselect; + + /** + * Constructor. + */ + public ButtonGroup() { + this.autoDeselect = false; + this.buttons = new ArrayList<>(); + this.selected = null; + this.listener = new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + AbstractButton b = (AbstractButton) e.getItem(); + if (e.getStateChange()==ItemEvent.SELECTED) { + setSelected(b); + } else if (selected==b) { + if (autoDeselect) { + setSelected(null); + } else { + selected.setSelected(true); + } + } + } + }; + } + + /** + * Adds a button to this group. + * @param b the button to be added + * @exception NullPointerException if the button is null + */ + public void add(AbstractButton b) { + if (b==null) { + throw new NullPointerException(); + } + buttons.add(b); + b.addItemListener(listener); + if (b.isSelected()) { + setSelected(b); + } + } + + /** + * Removes a button from this group. + * @param b the button to be removed + * @exception IllegalArgumentException if the button is unknown in this group + * @exception NullPointerException if the button is null + */ + public void remove(AbstractButton b) { + if (b == null) { + throw new NullPointerException(); + } + if (this.buttons.remove(b)) { + b.removeItemListener(this.listener); + if (this.selected==b) { + setSelected(null); + } + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Clears the selection such that none of the buttons in the + * ButtonGroup are selected. + */ + public void clearSelection() { + if (selected != null) { + setSelected(null); + } + } + + /** + * Returns the selected button. + * @return the selected button + */ + public AbstractButton getSelected() { + return this.selected; + } + + /** Changes the selected button. + * @param b the button to be selected (null deselects all buttons) + * @exception IllegalArgumentException if the button is not in this group + */ + public void setSelected(AbstractButton b) { + if (b==this.selected) { + return; + } + if ((b!=null) && (!this.buttons.contains(b))) { + throw new IllegalArgumentException(); + } + AbstractButton old = this.selected; + this.selected = b; + if (b!=null) { + b.setSelected(true); + } + if (old!=null) { + old.setSelected(false); + } + this.setChanged(); + this.notifyObservers(this.selected); + } + + /** Activates/deactivates the ability to deselect a selected button by cliking onto it. + *
The default behavior is "a click on a selected button does nothing". + * @param autoDeselect true to have selected button to be deactivated when clicked + */ + public void setAutoDeselect(boolean autoDeselect) { + this.autoDeselect = autoDeselect; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/FontUtils.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/FontUtils.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/FontUtils.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/FontUtils.java index 5c1c77c..bf81a00 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/FontUtils.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/FontUtils.java @@ -1,64 +1,64 @@ -package com.fathzer.soft.ajlib.swing; - -import java.awt.Font; -import java.awt.GraphicsEnvironment; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import javax.swing.UIManager; - -/** Some utilities related to font management. - */ -public abstract class FontUtils { - private static final String DEFAULT_FONT_PROPERTY_NAME = "defaultFont"; //$NON-NLS-1$ - - private FontUtils() { - super(); - } - - /** Gets the list of available text fonts. - *
A text font is a font able to display text. if a font is available in the system but only displays symbols - * it is not available as a text font. - *
Please note that the returned list is approximated by the list of fonts able to display their own localized name. This could change in the future. - * @param locale a locale used to display text. - * @return The list of fonts that are available and able to display text in the specified locale. - */ - public static List getAvailableTextFonts(Locale locale) { - ArrayList result = new ArrayList<>(); - Font[] allfonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); - for (Font font : allfonts) { - if (font.canDisplayUpTo(font.getFontName(locale)) == -1) { - result.add(font); - } - } - return result; - } - - /** Tests whether a look and feel supports a default font - * @param lookAndFeelName The look and feel name. - * @return true if the look and feel supports default font. - */ - public static boolean isDefaultFontSupportedByLookAndFeel(String lookAndFeelName) { - return "Nimbus".equals(lookAndFeelName); - } - - /** Gets the current default font. - * @return a Font or null if the current look and feel do not supports default font. - */ - public static Font getDefaultFont() { - return isDefaultFontSupportedByLookAndFeel(UIManager.getLookAndFeel().getName()) ? - (Font)UIManager.getDefaults().getFont(DEFAULT_FONT_PROPERTY_NAME) : null; - } - - /** Sets the default font. - *
Note that if the current UI does not support default font, the method does nothing. - * @param font The new default font - * @see FontUtils#isDefaultFontSupportedByLookAndFeel(String) - */ - public static void setDefaultFont(Font font) { - if (isDefaultFontSupportedByLookAndFeel(UIManager.getLookAndFeel().getName())) { - UIManager.getLookAndFeelDefaults().put(DEFAULT_FONT_PROPERTY_NAME, font); - } - } -} +package com.fathzer.soft.ajlib.swing; + +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import javax.swing.UIManager; + +/** Some utilities related to font management. + */ +public abstract class FontUtils { + private static final String DEFAULT_FONT_PROPERTY_NAME = "defaultFont"; //$NON-NLS-1$ + + private FontUtils() { + super(); + } + + /** Gets the list of available text fonts. + *
A text font is a font able to display text. if a font is available in the system but only displays symbols + * it is not available as a text font. + *
Please note that the returned list is approximated by the list of fonts able to display their own localized name. This could change in the future. + * @param locale a locale used to display text. + * @return The list of fonts that are available and able to display text in the specified locale. + */ + public static List getAvailableTextFonts(Locale locale) { + ArrayList result = new ArrayList<>(); + Font[] allfonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + for (Font font : allfonts) { + if (font.canDisplayUpTo(font.getFontName(locale)) == -1) { + result.add(font); + } + } + return result; + } + + /** Tests whether a look and feel supports a default font + * @param lookAndFeelName The look and feel name. + * @return true if the look and feel supports default font. + */ + public static boolean isDefaultFontSupportedByLookAndFeel(String lookAndFeelName) { + return "Nimbus".equals(lookAndFeelName); + } + + /** Gets the current default font. + * @return a Font or null if the current look and feel do not supports default font. + */ + public static Font getDefaultFont() { + return isDefaultFontSupportedByLookAndFeel(UIManager.getLookAndFeel().getName()) ? + (Font)UIManager.getDefaults().getFont(DEFAULT_FONT_PROPERTY_NAME) : null; + } + + /** Sets the default font. + *
Note that if the current UI does not support default font, the method does nothing. + * @param font The new default font + * @see FontUtils#isDefaultFontSupportedByLookAndFeel(String) + */ + public static void setDefaultFont(Font font) { + if (isDefaultFontSupportedByLookAndFeel(UIManager.getLookAndFeel().getName())) { + UIManager.getLookAndFeelDefaults().put(DEFAULT_FONT_PROPERTY_NAME, font); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/TextSearcher.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/TextSearcher.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/TextSearcher.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/TextSearcher.java index a335ad2..d2f4a3a 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/TextSearcher.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/TextSearcher.java @@ -1,220 +1,220 @@ -package com.fathzer.soft.ajlib.swing; - -import java.text.Normalizer; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import javax.swing.text.BadLocationException; -import javax.swing.text.DefaultHighlighter; -import javax.swing.text.Document; -import javax.swing.text.Highlighter; -import javax.swing.text.JTextComponent; - -import com.fathzer.soft.ajlib.swing.widget.HTMLPane; - - -/** - * A class that searches for a text in a JTextComponent and highlights all - * occurrences of that text.
- * It could be used, for instance, with an HTMLPane.
- * It supports case and diacritical sensitive/insensitive search. - * - * @author Jean-Marc Astesana - * This code was inspired by an example from Kim Topley (ISBN: 0 13 - * 083292 8 - Publisher: Prentice Hall - * @see HTMLPane - */ -public class TextSearcher { - protected JTextComponent comp; - private boolean caseSensitive; - private boolean diacriticalSensitive; - private String text; - private int[] offsets; - private int searchedNormalizedLength; - - /** - * Constructor. - *
The searched text is set to null. - * @param comp The text component in which to search - * @see #TextSearcher(JTextComponent, String) - */ - public TextSearcher(JTextComponent comp) { - this(comp, null); - } - - /** - * Constructor. - *
By default, the searcher is not case sensitive nor diacritical sensitive. - * @param comp The text component in which to search - * @param searchedText The text to search - */ - public TextSearcher(JTextComponent comp, String searchedText) { - this.comp = comp; - this.caseSensitive = false; - this.diacriticalSensitive = false; - this.text = searchedText; - search(); - } - - /** - * Searches for this.text and updates the offset of all the occurrences. - */ - private void search() { - if (text == null || text.isEmpty()) { - this.offsets = new int[0]; - return; - } - - // Look for the text we are given - // 1) Extract a normalized form of the text in which to search - String content = null; - try { - Document d = comp.getDocument(); - content = normalize(d.getText(0, d.getLength())); - } catch (BadLocationException e) { - // Cannot happen - throw new RuntimeException(e); - } - - // 2) Get the normalized form of the searched text - text = normalize(text); - - // 3) Search for the searched text - List indexes = new LinkedList<>(); - int lastIndex = 0; - int size = 0; - searchedNormalizedLength = text.length(); - while ((lastIndex = content.indexOf(text, lastIndex)) != -1) { - indexes.add(lastIndex); - size++; - lastIndex = lastIndex + searchedNormalizedLength; - } - - offsets = new int[size]; - Iterator iter = indexes.iterator(); - for (int i = 0; i < offsets.length; i++) { - offsets[i] = iter.next(); - } - } - - /** - * Gets the normalized version of a text.
- * The normalized version is lowercased if the TextSearcher is not case - * sensitive and has no diacritical marks if it is diacritical insensitive. - * @param content - * @return a String - */ - private String normalize(String content) { - if (!isCaseSensitive()) { - content = content.toLowerCase(); - } - if (!isDiacriticalSensitive()) { - content = Normalizer.normalize(content, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); - } - return content; - } - - /** - * Tests whether the search is case sensitive or not. - * - * @return true if the search is case sensitive. - */ - public boolean isCaseSensitive() { - return this.caseSensitive; - } - - /** - * Sets if the search is case sensitive or not.
- * By default, the search is not case sensitive.
- * This method updates the offsets attribute. - * @param caseSensitive true to have a case sensitive search - * @see #getOffsets() - */ - public void setCaseSentitive(boolean caseSensitive) { - if (caseSensitive != this.caseSensitive) { - this.caseSensitive = caseSensitive; - search(); - } - } - - /** - * Tests whether the search is diacritical sensitive or not. - * @return true if the search is diacritical sensitive. - */ - public boolean isDiacriticalSensitive() { - return diacriticalSensitive; - } - - /** - * Sets if the search is diacritical sensitive or not.
- * By default, the search is not diacritical sensitive.
- * This method updates the offsets attribute. - * @param diacriticalSensitive true to have a diacritical sensitive search - * @see #getOffsets() - */ - public void setDiacriticalSensitive(boolean diacriticalSensitive) { - if (diacriticalSensitive != this.diacriticalSensitive) { - this.diacriticalSensitive = diacriticalSensitive; - search(); - } - } - - /** Gets the offsets of the searched text. - *
These offsets can be passed to the highlight methods - * @return a int array (its dimension is 0 if the text was not found) - * @see #highlight(int, javax.swing.text.Highlighter.HighlightPainter) - * @see #highlight(int[], javax.swing.text.Highlighter.HighlightPainter) - */ - public int[] getOffsets() { - return offsets; - } - - /** Highlights some portions of the text. - * @param offsets the start of the portions to highlight (these offsets are returned by getOffsets method). - * null to remove all highlights. - * @param painter The painter to be used (or null to use the default one). - * @throws BadLocationException if offsets are out of text bounds - * @see #getOffsets() - */ - public void highlight(int[] offsets, Highlighter.HighlightPainter painter) throws BadLocationException { - if (painter==null) { - painter = DefaultHighlighter.DefaultPainter; - } - // Remove any existing highlights for last word - Highlighter highlighter = comp.getHighlighter(); - highlighter.removeAllHighlights(); - if (offsets!=null) { - for (int i = 0; i < offsets.length; i++) { - highlighter.addHighlight(offsets[i], offsets[i] + this.searchedNormalizedLength, painter); - } - // Scroll the text pane in order to view the first occurrence. - if (offsets.length>0) { - comp.scrollRectToVisible(comp.modelToView(offsets[0])); - } - } - } - - /** Highlights a portion of the text. - * @param offset the start of the portion to highlight (one of the offsets returned by getOffsets method). - * @param painter The painter to be used (or null to use the default one). - * @throws BadLocationException if offset is out of text bounds - * @see #getOffsets() - */ - public void highlight(int offset, Highlighter.HighlightPainter painter) throws BadLocationException { - if (painter==null) { - painter = DefaultHighlighter.DefaultPainter; - } - // Remove any existing highlights for last word - Highlighter highlighter = comp.getHighlighter(); - highlighter.removeAllHighlights(); - highlighter.addHighlight(offset, offset + this.searchedNormalizedLength, painter); - // Scroll the text pane in order to view the first occurrence. - comp.scrollRectToVisible(comp.modelToView(offset)); - } - - public void setSearchedText(String text) { - this.text = text; - search(); - } +package com.fathzer.soft.ajlib.swing; + +import java.text.Normalizer; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.Document; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; + +import com.fathzer.soft.ajlib.swing.widget.HTMLPane; + + +/** + * A class that searches for a text in a JTextComponent and highlights all + * occurrences of that text.
+ * It could be used, for instance, with an HTMLPane.
+ * It supports case and diacritical sensitive/insensitive search. + * + * @author Jean-Marc Astesana + * This code was inspired by an example from Kim Topley (ISBN: 0 13 + * 083292 8 - Publisher: Prentice Hall + * @see HTMLPane + */ +public class TextSearcher { + protected JTextComponent comp; + private boolean caseSensitive; + private boolean diacriticalSensitive; + private String text; + private int[] offsets; + private int searchedNormalizedLength; + + /** + * Constructor. + *
The searched text is set to null. + * @param comp The text component in which to search + * @see #TextSearcher(JTextComponent, String) + */ + public TextSearcher(JTextComponent comp) { + this(comp, null); + } + + /** + * Constructor. + *
By default, the searcher is not case sensitive nor diacritical sensitive. + * @param comp The text component in which to search + * @param searchedText The text to search + */ + public TextSearcher(JTextComponent comp, String searchedText) { + this.comp = comp; + this.caseSensitive = false; + this.diacriticalSensitive = false; + this.text = searchedText; + search(); + } + + /** + * Searches for this.text and updates the offset of all the occurrences. + */ + private void search() { + if (text == null || text.isEmpty()) { + this.offsets = new int[0]; + return; + } + + // Look for the text we are given + // 1) Extract a normalized form of the text in which to search + String content = null; + try { + Document d = comp.getDocument(); + content = normalize(d.getText(0, d.getLength())); + } catch (BadLocationException e) { + // Cannot happen + throw new RuntimeException(e); + } + + // 2) Get the normalized form of the searched text + text = normalize(text); + + // 3) Search for the searched text + List indexes = new LinkedList<>(); + int lastIndex = 0; + int size = 0; + searchedNormalizedLength = text.length(); + while ((lastIndex = content.indexOf(text, lastIndex)) != -1) { + indexes.add(lastIndex); + size++; + lastIndex = lastIndex + searchedNormalizedLength; + } + + offsets = new int[size]; + Iterator iter = indexes.iterator(); + for (int i = 0; i < offsets.length; i++) { + offsets[i] = iter.next(); + } + } + + /** + * Gets the normalized version of a text.
+ * The normalized version is lowercased if the TextSearcher is not case + * sensitive and has no diacritical marks if it is diacritical insensitive. + * @param content + * @return a String + */ + private String normalize(String content) { + if (!isCaseSensitive()) { + content = content.toLowerCase(); + } + if (!isDiacriticalSensitive()) { + content = Normalizer.normalize(content, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); + } + return content; + } + + /** + * Tests whether the search is case sensitive or not. + * + * @return true if the search is case sensitive. + */ + public boolean isCaseSensitive() { + return this.caseSensitive; + } + + /** + * Sets if the search is case sensitive or not.
+ * By default, the search is not case sensitive.
+ * This method updates the offsets attribute. + * @param caseSensitive true to have a case sensitive search + * @see #getOffsets() + */ + public void setCaseSentitive(boolean caseSensitive) { + if (caseSensitive != this.caseSensitive) { + this.caseSensitive = caseSensitive; + search(); + } + } + + /** + * Tests whether the search is diacritical sensitive or not. + * @return true if the search is diacritical sensitive. + */ + public boolean isDiacriticalSensitive() { + return diacriticalSensitive; + } + + /** + * Sets if the search is diacritical sensitive or not.
+ * By default, the search is not diacritical sensitive.
+ * This method updates the offsets attribute. + * @param diacriticalSensitive true to have a diacritical sensitive search + * @see #getOffsets() + */ + public void setDiacriticalSensitive(boolean diacriticalSensitive) { + if (diacriticalSensitive != this.diacriticalSensitive) { + this.diacriticalSensitive = diacriticalSensitive; + search(); + } + } + + /** Gets the offsets of the searched text. + *
These offsets can be passed to the highlight methods + * @return a int array (its dimension is 0 if the text was not found) + * @see #highlight(int, javax.swing.text.Highlighter.HighlightPainter) + * @see #highlight(int[], javax.swing.text.Highlighter.HighlightPainter) + */ + public int[] getOffsets() { + return offsets; + } + + /** Highlights some portions of the text. + * @param offsets the start of the portions to highlight (these offsets are returned by getOffsets method). + * null to remove all highlights. + * @param painter The painter to be used (or null to use the default one). + * @throws BadLocationException if offsets are out of text bounds + * @see #getOffsets() + */ + public void highlight(int[] offsets, Highlighter.HighlightPainter painter) throws BadLocationException { + if (painter==null) { + painter = DefaultHighlighter.DefaultPainter; + } + // Remove any existing highlights for last word + Highlighter highlighter = comp.getHighlighter(); + highlighter.removeAllHighlights(); + if (offsets!=null) { + for (int i = 0; i < offsets.length; i++) { + highlighter.addHighlight(offsets[i], offsets[i] + this.searchedNormalizedLength, painter); + } + // Scroll the text pane in order to view the first occurrence. + if (offsets.length>0) { + comp.scrollRectToVisible(comp.modelToView(offsets[0])); + } + } + } + + /** Highlights a portion of the text. + * @param offset the start of the portion to highlight (one of the offsets returned by getOffsets method). + * @param painter The painter to be used (or null to use the default one). + * @throws BadLocationException if offset is out of text bounds + * @see #getOffsets() + */ + public void highlight(int offset, Highlighter.HighlightPainter painter) throws BadLocationException { + if (painter==null) { + painter = DefaultHighlighter.DefaultPainter; + } + // Remove any existing highlights for last word + Highlighter highlighter = comp.getHighlighter(); + highlighter.removeAllHighlights(); + highlighter.addHighlight(offset, offset + this.searchedNormalizedLength, painter); + // Scroll the text pane in order to view the first occurrence. + comp.scrollRectToVisible(comp.modelToView(offset)); + } + + public void setSearchedText(String text) { + this.text = text; + search(); + } } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/ToolsFrame.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/ToolsFrame.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/ToolsFrame.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/ToolsFrame.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/Utils.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/Utils.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/Utils.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/Utils.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/dialog/AbstractDialog.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/AbstractDialog.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/dialog/AbstractDialog.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/AbstractDialog.java index c766e55..17e7e36 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/dialog/AbstractDialog.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/AbstractDialog.java @@ -1,251 +1,251 @@ -package com.fathzer.soft.ajlib.swing.dialog; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.GraphicsEnvironment; -import java.awt.Rectangle; -import java.awt.Window; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.WindowEvent; - -import javax.swing.*; - -import com.fathzer.soft.ajlib.swing.Utils; -import com.fathzer.soft.ajlib.swing.framework.Application; - - -/** An abstract dialog with a customizable center pane, an Ok and/or a Cancel button. - *
By default, the dialog:
    - *
  • is not resizable, call this.setResizable(true) to change this behavior (don't forget to call pack - * and set the minimum size after calling setResizable).
  • - *
  • has default close operation sets to DISPOSE_ON_CLOSE.
  • - *
- * The dialog automatically wrap its center component in a JScollPane to guarantee that the dialog will - * never be bigger than the screen (in such a case ok/cancel buttons would be hidden). - * @param The class of the parameter of the dialog (information that is useful to build the center pane). - * @param The class of the result of the dialog - */ -public abstract class AbstractDialog extends JDialog { - //FIXME The horizontal scroll bar is always shown when the vertical one is shown. - private static final long serialVersionUID = 1L; - - private V result; - - private JButton cancelButton; - private JButton okButton; - /** The data passed to the dialog's constructor. - */ - protected T data; - - /** - * Constructor - * @param owner Dialog's parent frame - * @param title Dialog's title - * @param data optional data (will be transfered to createContentPane) - */ - protected AbstractDialog(Window owner, String title, T data) { - super(owner, title, ModalityType.APPLICATION_MODAL); - setDefaultCloseOperation(DISPOSE_ON_CLOSE); - this.data = data; - this.result = null; - final AbstractAction escapeAction = new AbstractAction() { - private static final long serialVersionUID = 1L; - - @Override - public void actionPerformed(ActionEvent ae) { - cancel(); - } - }; - String actionMapKey = "ESCAPE_KEY"; //$NON-NLS-1$ - getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), actionMapKey); - getRootPane().getActionMap().put(actionMapKey, escapeAction); - this.setContentPane(this.createContentPane()); - this.setResizable(false); - this.pack(); - this.setLocationRelativeTo(owner); - } - - private Container createContentPane() { - //Create the content pane. - JPanel contentPane = new JPanel(new BorderLayout(5,5)); - contentPane.setOpaque(true); - contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - - JPanel southPane = new JPanel(new BorderLayout()); - southPane.setOpaque(false); - JPanel buttonsPane = createButtonsPane(); - - southPane.add(buttonsPane, BorderLayout.EAST); - JComponent extra = createExtraComponent(); - if (extra!=null) { - southPane.add(extra, BorderLayout.WEST); - } - contentPane.add(southPane, BorderLayout.SOUTH); - - JPanel centerPane = this.createCenterPane(); - if (centerPane != null) { - // As component can be bigger than screen ... and we always want the "ok", "cancel" button to be present, - // we will wrap the center pane into a scrollPane - Component component = (centerPane instanceof Scrollable) ? centerPane : new DefaultScrollablePanel(centerPane); - JScrollPane scrollPane = new JScrollPane(component); - scrollPane.setBorder(null); - contentPane.add(scrollPane, BorderLayout.CENTER); - } - - this.updateOkButtonEnabled(); - - ActionListener listener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource().equals(okButton)) { - confirm(); - } else { - cancel(); - } - } - }; - getOkButton().addActionListener(listener); - getCancelButton().addActionListener(listener); - return contentPane; - } - - /** Gets the panel that contains the ok and cancel buttons. - *
This method creates a new JPanel with flow layout and ok and cancel buttons. Then, it defines ok has the default button. - *
It can be override to add more buttons. - * @return a JPanel - */ - protected JPanel createButtonsPane() { - JPanel buttonsPane = new JPanel(); - buttonsPane.setOpaque(false); - buttonsPane.add(getOkButton()); - buttonsPane.add(getCancelButton()); - getRootPane().setDefaultButton(okButton); - return buttonsPane; - } - - /** Gets an extra component displayed on the same line as the buttons panel (see {@link #createButtonsPane()}) on the left side. - *
By default, this method returns null. - * @return A JComponent or null if no component is provided. - */ - protected JComponent createExtraComponent() { - return null; - } - - /** Gets the ok button. - *
If you want the dialog not to have an ok button, you may set the visibility of this button to false with - * (getOkButton.setVisible(false)). - * @return The ok button. - */ - protected JButton getOkButton() { - if (okButton==null) { - okButton = new JButton(Application.getString("GenericButton.ok", getLocale())); //$NON-NLS-1$ - okButton.setOpaque(false); - } - return okButton; - } - - /** Gets the cancel button. - * If you want the dialog not to have a cancel button, you may set the visibility of this button to false - * (getCancelButton.setVisible(false)). - * @return the cancel button. - */ - protected JButton getCancelButton() { - if (cancelButton==null) { - cancelButton = new JButton(Application.getString("GenericButton.cancel", getLocale())); //$NON-NLS-1$ - cancelButton.setToolTipText(Application.getString("GenericButton.cancel.toolTip", getLocale())); //$NON-NLS-1$ - cancelButton.setOpaque(false); - } - return cancelButton; - } - - /** Gets the center pane of this dialog. - * This method is called once by the constructor of the dialog. - * The data attribute is already set to the data parameter passed to the constructor. - * @return a panel - */ - protected abstract JPanel createCenterPane(); - - /** This method is called when the user clicks the ok button. - *
This method should return the object, result of the dialog, that will be returned - * by getResult. - *
Note that it is not a good practice to override this method and set its visibility to public. - * You should prefer calling the getResult method as buildResult may instantiate a new object each - * time it is called. - * @return an object - * @see #getResult() - */ - protected abstract V buildResult(); - - /** This method is called when the user clicks the ok button. - *
It calls the buildResult method, stores the result, then closes the dialog. - * @see #getResult() - */ - protected void confirm() { - result = buildResult(); - close(); - } - - /** This method is called when the user clicks the cancel button. - *
This default implementation closes the dialog. - */ - protected void cancel() { - result = null; - close(); - } - - private void close() { - dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); - } - - /** Checks if the user input is consistent and return a short explanation of why it is not. - *
In this implementation, this method returns null. You should override this method if the input can be inconsistent. - * @return a short message explaining why the ok button is disabled or null if the ok button has to be enabled. - * This message will be displayed in the ok button toolTip. - */ - protected String getOkDisabledCause() { - return null; - } - - /** Gets the result of this dialog. - * @return an object, or null if the dialog was cancelled. - */ - public V getResult() { - return result; - } - - /** Forces the state of the users input to be evaluated and updates the state of the ok button. - * @see #getOkDisabledCause() - */ - public void updateOkButtonEnabled() { - String cause = getOkDisabledCause(); - this.getOkButton().setEnabled(cause==null); - this.getOkButton().setToolTipText(cause==null?Application.getString("GenericButton.ok.toolTip", getLocale()):cause); //$NON-NLS-1$ - } - - @Override - public Dimension getPreferredSize() { - //We never want the dialog to be bigger than the available screen space - //So, we will return the smaller of the preferred size and the available one. - Dimension preferred = super.getPreferredSize(); - Rectangle availableSpace = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds(); - preferred.height = Math.min(preferred.height, availableSpace.height); - preferred.width = Math.min(preferred.width, availableSpace.width); - return preferred; - } - - - /** Gets the window which contains a component. - * @deprecated - * @param component the component - * @return The window containing the component or null if no window contains the component. - * @see Utils#getOwnerWindow(Component) - */ - @Deprecated - public static Window getOwnerWindow(Component component) { - return Utils.getOwnerWindow(component); - } -} +package com.fathzer.soft.ajlib.swing.dialog; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; + +import javax.swing.*; + +import com.fathzer.soft.ajlib.swing.Utils; +import com.fathzer.soft.ajlib.swing.framework.Application; + + +/** An abstract dialog with a customizable center pane, an Ok and/or a Cancel button. + *
By default, the dialog:
    + *
  • is not resizable, call this.setResizable(true) to change this behavior (don't forget to call pack + * and set the minimum size after calling setResizable).
  • + *
  • has default close operation sets to DISPOSE_ON_CLOSE.
  • + *
+ * The dialog automatically wrap its center component in a JScollPane to guarantee that the dialog will + * never be bigger than the screen (in such a case ok/cancel buttons would be hidden). + * @param The class of the parameter of the dialog (information that is useful to build the center pane). + * @param The class of the result of the dialog + */ +public abstract class AbstractDialog extends JDialog { + //FIXME The horizontal scroll bar is always shown when the vertical one is shown. + private static final long serialVersionUID = 1L; + + private V result; + + private JButton cancelButton; + private JButton okButton; + /** The data passed to the dialog's constructor. + */ + protected T data; + + /** + * Constructor + * @param owner Dialog's parent frame + * @param title Dialog's title + * @param data optional data (will be transfered to createContentPane) + */ + protected AbstractDialog(Window owner, String title, T data) { + super(owner, title, ModalityType.APPLICATION_MODAL); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + this.data = data; + this.result = null; + final AbstractAction escapeAction = new AbstractAction() { + private static final long serialVersionUID = 1L; + + @Override + public void actionPerformed(ActionEvent ae) { + cancel(); + } + }; + String actionMapKey = "ESCAPE_KEY"; //$NON-NLS-1$ + getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), actionMapKey); + getRootPane().getActionMap().put(actionMapKey, escapeAction); + this.setContentPane(this.createContentPane()); + this.setResizable(false); + this.pack(); + this.setLocationRelativeTo(owner); + } + + private Container createContentPane() { + //Create the content pane. + JPanel contentPane = new JPanel(new BorderLayout(5,5)); + contentPane.setOpaque(true); + contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + JPanel southPane = new JPanel(new BorderLayout()); + southPane.setOpaque(false); + JPanel buttonsPane = createButtonsPane(); + + southPane.add(buttonsPane, BorderLayout.EAST); + JComponent extra = createExtraComponent(); + if (extra!=null) { + southPane.add(extra, BorderLayout.WEST); + } + contentPane.add(southPane, BorderLayout.SOUTH); + + JPanel centerPane = this.createCenterPane(); + if (centerPane != null) { + // As component can be bigger than screen ... and we always want the "ok", "cancel" button to be present, + // we will wrap the center pane into a scrollPane + Component component = (centerPane instanceof Scrollable) ? centerPane : new DefaultScrollablePanel(centerPane); + JScrollPane scrollPane = new JScrollPane(component); + scrollPane.setBorder(null); + contentPane.add(scrollPane, BorderLayout.CENTER); + } + + this.updateOkButtonEnabled(); + + ActionListener listener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource().equals(okButton)) { + confirm(); + } else { + cancel(); + } + } + }; + getOkButton().addActionListener(listener); + getCancelButton().addActionListener(listener); + return contentPane; + } + + /** Gets the panel that contains the ok and cancel buttons. + *
This method creates a new JPanel with flow layout and ok and cancel buttons. Then, it defines ok has the default button. + *
It can be override to add more buttons. + * @return a JPanel + */ + protected JPanel createButtonsPane() { + JPanel buttonsPane = new JPanel(); + buttonsPane.setOpaque(false); + buttonsPane.add(getOkButton()); + buttonsPane.add(getCancelButton()); + getRootPane().setDefaultButton(okButton); + return buttonsPane; + } + + /** Gets an extra component displayed on the same line as the buttons panel (see {@link #createButtonsPane()}) on the left side. + *
By default, this method returns null. + * @return A JComponent or null if no component is provided. + */ + protected JComponent createExtraComponent() { + return null; + } + + /** Gets the ok button. + *
If you want the dialog not to have an ok button, you may set the visibility of this button to false with + * (getOkButton.setVisible(false)). + * @return The ok button. + */ + protected JButton getOkButton() { + if (okButton==null) { + okButton = new JButton(Application.getString("GenericButton.ok", getLocale())); //$NON-NLS-1$ + okButton.setOpaque(false); + } + return okButton; + } + + /** Gets the cancel button. + * If you want the dialog not to have a cancel button, you may set the visibility of this button to false + * (getCancelButton.setVisible(false)). + * @return the cancel button. + */ + protected JButton getCancelButton() { + if (cancelButton==null) { + cancelButton = new JButton(Application.getString("GenericButton.cancel", getLocale())); //$NON-NLS-1$ + cancelButton.setToolTipText(Application.getString("GenericButton.cancel.toolTip", getLocale())); //$NON-NLS-1$ + cancelButton.setOpaque(false); + } + return cancelButton; + } + + /** Gets the center pane of this dialog. + * This method is called once by the constructor of the dialog. + * The data attribute is already set to the data parameter passed to the constructor. + * @return a panel + */ + protected abstract JPanel createCenterPane(); + + /** This method is called when the user clicks the ok button. + *
This method should return the object, result of the dialog, that will be returned + * by getResult. + *
Note that it is not a good practice to override this method and set its visibility to public. + * You should prefer calling the getResult method as buildResult may instantiate a new object each + * time it is called. + * @return an object + * @see #getResult() + */ + protected abstract V buildResult(); + + /** This method is called when the user clicks the ok button. + *
It calls the buildResult method, stores the result, then closes the dialog. + * @see #getResult() + */ + protected void confirm() { + result = buildResult(); + close(); + } + + /** This method is called when the user clicks the cancel button. + *
This default implementation closes the dialog. + */ + protected void cancel() { + result = null; + close(); + } + + private void close() { + dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); + } + + /** Checks if the user input is consistent and return a short explanation of why it is not. + *
In this implementation, this method returns null. You should override this method if the input can be inconsistent. + * @return a short message explaining why the ok button is disabled or null if the ok button has to be enabled. + * This message will be displayed in the ok button toolTip. + */ + protected String getOkDisabledCause() { + return null; + } + + /** Gets the result of this dialog. + * @return an object, or null if the dialog was cancelled. + */ + public V getResult() { + return result; + } + + /** Forces the state of the users input to be evaluated and updates the state of the ok button. + * @see #getOkDisabledCause() + */ + public void updateOkButtonEnabled() { + String cause = getOkDisabledCause(); + this.getOkButton().setEnabled(cause==null); + this.getOkButton().setToolTipText(cause==null?Application.getString("GenericButton.ok.toolTip", getLocale()):cause); //$NON-NLS-1$ + } + + @Override + public Dimension getPreferredSize() { + //We never want the dialog to be bigger than the available screen space + //So, we will return the smaller of the preferred size and the available one. + Dimension preferred = super.getPreferredSize(); + Rectangle availableSpace = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds(); + preferred.height = Math.min(preferred.height, availableSpace.height); + preferred.width = Math.min(preferred.width, availableSpace.width); + return preferred; + } + + + /** Gets the window which contains a component. + * @deprecated + * @param component the component + * @return The window containing the component or null if no window contains the component. + * @see Utils#getOwnerWindow(Component) + */ + @Deprecated + public static Window getOwnerWindow(Component component) { + return Utils.getOwnerWindow(component); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/dialog/DefaultScrollablePanel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/DefaultScrollablePanel.java similarity index 95% rename from src/main/java/com/fathzer/soft/ajlib/swing/dialog/DefaultScrollablePanel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/DefaultScrollablePanel.java index 11e9f92..b6f9475 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/dialog/DefaultScrollablePanel.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/DefaultScrollablePanel.java @@ -1,42 +1,42 @@ -package com.fathzer.soft.ajlib.swing.dialog; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Rectangle; - -import javax.swing.JPanel; -import javax.swing.Scrollable; - -@SuppressWarnings("serial") -class DefaultScrollablePanel extends JPanel implements Scrollable { - DefaultScrollablePanel(Component component) { - super(new BorderLayout()); - this.add(component); - } - - @Override - public Dimension getPreferredScrollableViewportSize() { - return this.getComponent(0).getPreferredSize(); - } - - @Override - public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - return 10; - } - - @Override - public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - return 40; - } - - @Override - public boolean getScrollableTracksViewportWidth() { - return true; - } - - @Override - public boolean getScrollableTracksViewportHeight() { - return true; - } -} +package com.fathzer.soft.ajlib.swing.dialog; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Rectangle; + +import javax.swing.JPanel; +import javax.swing.Scrollable; + +@SuppressWarnings("serial") +class DefaultScrollablePanel extends JPanel implements Scrollable { + DefaultScrollablePanel(Component component) { + super(new BorderLayout()); + this.add(component); + } + + @Override + public Dimension getPreferredScrollableViewportSize() { + return this.getComponent(0).getPreferredSize(); + } + + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + return 10; + } + + @Override + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + return 40; + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override + public boolean getScrollableTracksViewportHeight() { + return true; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/dialog/FileChooser.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/FileChooser.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/dialog/FileChooser.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/FileChooser.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/dialog/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/package-info.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/dialog/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/package-info.java index e063f66..c074f3c 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/dialog/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/dialog/package-info.java @@ -1,3 +1,3 @@ -/** Swing dialog utilities*/ -package com.fathzer.soft.ajlib.swing.dialog; - +/** Swing dialog utilities*/ +package com.fathzer.soft.ajlib.swing.dialog; + diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/framework/Application.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/Application.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/framework/Application.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/Application.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/framework/MainMenu.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/MainMenu.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/framework/MainMenu.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/MainMenu.java index 0a470aa..6fad250 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/framework/MainMenu.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/MainMenu.java @@ -1,38 +1,38 @@ -package com.fathzer.soft.ajlib.swing.framework; - -import java.awt.Toolkit; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.KeyStroke; - -@SuppressWarnings("serial") -class MainMenu extends JMenuBar { - public MainMenu (Application application) { - JMenu menu = new JMenu(Application.getString("MainMenu.file")); //$NON-NLS-1$ - menu.setMnemonic(Application.getString("MainMenu.file.mnemonic").charAt(0)); //$NON-NLS-1$ - this.add(menu); - JMenuItem menuItem = new JMenuItem(new QuitAction(application)); - menuItem.setAccelerator(KeyStroke.getKeyStroke(Application.getString("MainMenu.quit.shortcut").charAt(0), Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); //$NON-NLS-1$ - menuItem.setMnemonic(Application.getString("MainMenu.quit.mnemonic").charAt(0)); //$NON-NLS-1$ - menu.add(menuItem); - } - - private static class QuitAction extends AbstractAction { - private Application app; - - QuitAction(Application app) { - super(Application.getString("MainMenu.quit"), null); //$NON-NLS-1$ - putValue(SHORT_DESCRIPTION, Application.getString("MainMenu.quit.tooltip")); //$NON-NLS-1$ - this.app = app; - } - - @Override - public void actionPerformed(ActionEvent e) { - app.quit(); - } - } -} +package com.fathzer.soft.ajlib.swing.framework; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; + +@SuppressWarnings("serial") +class MainMenu extends JMenuBar { + public MainMenu (Application application) { + JMenu menu = new JMenu(Application.getString("MainMenu.file")); //$NON-NLS-1$ + menu.setMnemonic(Application.getString("MainMenu.file.mnemonic").charAt(0)); //$NON-NLS-1$ + this.add(menu); + JMenuItem menuItem = new JMenuItem(new QuitAction(application)); + menuItem.setAccelerator(KeyStroke.getKeyStroke(Application.getString("MainMenu.quit.shortcut").charAt(0), Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); //$NON-NLS-1$ + menuItem.setMnemonic(Application.getString("MainMenu.quit.mnemonic").charAt(0)); //$NON-NLS-1$ + menu.add(menuItem); + } + + private static class QuitAction extends AbstractAction { + private Application app; + + QuitAction(Application app) { + super(Application.getString("MainMenu.quit"), null); //$NON-NLS-1$ + putValue(SHORT_DESCRIPTION, Application.getString("MainMenu.quit.tooltip")); //$NON-NLS-1$ + this.app = app; + } + + @Override + public void actionPerformed(ActionEvent e) { + app.quit(); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/framework/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/package-info.java similarity index 99% rename from src/main/java/com/fathzer/soft/ajlib/swing/framework/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/package-info.java index 8da4771..5b20aea 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/framework/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/framework/package-info.java @@ -1,2 +1,2 @@ -/** A simple framework to implement Java application*/ +/** A simple framework to implement Java application*/ package com.fathzer.soft.ajlib.swing.framework; \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/package-info.java similarity index 98% rename from src/main/java/com/fathzer/soft/ajlib/swing/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/package-info.java index 33c9f1a..9138ac2 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/package-info.java @@ -1,2 +1,2 @@ -/** General purpose swing utilities*/ +/** General purpose swing utilities*/ package com.fathzer.soft.ajlib.swing; \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/AbstractTableRowMover.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/AbstractTableRowMover.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/AbstractTableRowMover.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/AbstractTableRowMover.java index 837a60d..bca137e 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/AbstractTableRowMover.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/AbstractTableRowMover.java @@ -1,119 +1,119 @@ -package com.fathzer.soft.ajlib.swing.table; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; - -import com.fathzer.soft.ajlib.utilities.ListUtils; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; - -/** An abstract widget composed of two buttons to move up or down selected rows of a JTable. - * @see JTable - */ -public abstract class AbstractTableRowMover extends JPanel { - private static final long serialVersionUID = 1L; - - private JButton downButton; - private JButton upButton; - - private JTable table; - - /** - * Creates the panel. - * @param table The table that contains the moveable rows. - */ - protected AbstractTableRowMover(JTable table) { - this.table = table; - initialize(); - - getJTable().getSelectionModel().addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { - if (e.getValueIsAdjusting()) { - return; - } - updateupDownEnabled(); - } - }); - } - private void initialize() { - GridBagLayout gridBagLayout = new GridBagLayout(); - setLayout(gridBagLayout); - GridBagConstraints gbcUpButton = new GridBagConstraints(); - gbcUpButton.insets = new Insets(0, 0, 5, 0); - gbcUpButton.weighty = 1.0; - gbcUpButton.anchor = GridBagConstraints.SOUTH; - gbcUpButton.gridx = 0; - gbcUpButton.gridy = 0; - add(getUpButton(), gbcUpButton); - GridBagConstraints gbcDownButton = new GridBagConstraints(); - gbcDownButton.anchor = GridBagConstraints.NORTH; - gbcDownButton.weighty = 1.0; - gbcDownButton.gridx = 0; - gbcDownButton.gridy = 1; - add(getDownButton(), gbcDownButton); - } - - private JButton getUpButton() { - if (upButton == null) { - upButton = new JButton(""); - upButton.setIcon(new ImageIcon(AbstractTableRowMover.class.getResource("/com/fathzer/soft/ajlib/swing/widget/up.png"))); //$NON-NLS-1$ - upButton.setEnabled(false); - upButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int[] selectedRows = getJTable().getSelectedRows(); - moveModelRows(selectedRows, true); - offsetSelection(selectedRows,-1); - } - }); - } - return upButton; - } - private JButton getDownButton() { - if (downButton == null) { - downButton = new JButton(""); - downButton.setIcon(new ImageIcon(AbstractTableRowMover.class.getResource("/com/fathzer/soft/ajlib/swing/widget/down.png"))); //$NON-NLS-1$ - downButton.setEnabled(false); - downButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int[] selectedRows = getJTable().getSelectedRows(); - moveModelRows(selectedRows, false); - offsetSelection(selectedRows,1); - } - }); - } - return downButton; - } - - private void updateupDownEnabled() { - getUpButton().setEnabled(getJTable().getSelectedRow()>0); - int [] rowsSelected=getJTable().getSelectedRows(); - getDownButton().setEnabled((rowsSelected.length!=0) && (rowsSelected[rowsSelected.length-1]!=getJTable().getRowCount()-1)); - } - private void offsetSelection(int[] selectedRows, int offset) { - for (int i = 0; i < selectedRows.length; i++) { - selectedRows[i] = selectedRows[i]+offset; - } - getJTable().setSelectedIndexes(selectedRows); - } - - protected JTable getJTable() { - return this.table; - } - - /** Moves the rows in the table model. - *
This method is called when the user clicks the up or down buttons. - *
It is mandatory that this method fire the proper model change event after updating the model. - * @param selectedRows The moved rows - * @param up true if the rows are moved up, false if they are moved down. - * @see ListUtils#move(java.util.List, int[], int) - */ - protected abstract void moveModelRows(int[] selectedRows, boolean up); -} +package com.fathzer.soft.ajlib.swing.table; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import com.fathzer.soft.ajlib.utilities.ListUtils; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +/** An abstract widget composed of two buttons to move up or down selected rows of a JTable. + * @see JTable + */ +public abstract class AbstractTableRowMover extends JPanel { + private static final long serialVersionUID = 1L; + + private JButton downButton; + private JButton upButton; + + private JTable table; + + /** + * Creates the panel. + * @param table The table that contains the moveable rows. + */ + protected AbstractTableRowMover(JTable table) { + this.table = table; + initialize(); + + getJTable().getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) { + return; + } + updateupDownEnabled(); + } + }); + } + private void initialize() { + GridBagLayout gridBagLayout = new GridBagLayout(); + setLayout(gridBagLayout); + GridBagConstraints gbcUpButton = new GridBagConstraints(); + gbcUpButton.insets = new Insets(0, 0, 5, 0); + gbcUpButton.weighty = 1.0; + gbcUpButton.anchor = GridBagConstraints.SOUTH; + gbcUpButton.gridx = 0; + gbcUpButton.gridy = 0; + add(getUpButton(), gbcUpButton); + GridBagConstraints gbcDownButton = new GridBagConstraints(); + gbcDownButton.anchor = GridBagConstraints.NORTH; + gbcDownButton.weighty = 1.0; + gbcDownButton.gridx = 0; + gbcDownButton.gridy = 1; + add(getDownButton(), gbcDownButton); + } + + private JButton getUpButton() { + if (upButton == null) { + upButton = new JButton(""); + upButton.setIcon(new ImageIcon(AbstractTableRowMover.class.getResource("/com/fathzer/soft/ajlib/swing/widget/up.png"))); //$NON-NLS-1$ + upButton.setEnabled(false); + upButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int[] selectedRows = getJTable().getSelectedRows(); + moveModelRows(selectedRows, true); + offsetSelection(selectedRows,-1); + } + }); + } + return upButton; + } + private JButton getDownButton() { + if (downButton == null) { + downButton = new JButton(""); + downButton.setIcon(new ImageIcon(AbstractTableRowMover.class.getResource("/com/fathzer/soft/ajlib/swing/widget/down.png"))); //$NON-NLS-1$ + downButton.setEnabled(false); + downButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int[] selectedRows = getJTable().getSelectedRows(); + moveModelRows(selectedRows, false); + offsetSelection(selectedRows,1); + } + }); + } + return downButton; + } + + private void updateupDownEnabled() { + getUpButton().setEnabled(getJTable().getSelectedRow()>0); + int [] rowsSelected=getJTable().getSelectedRows(); + getDownButton().setEnabled((rowsSelected.length!=0) && (rowsSelected[rowsSelected.length-1]!=getJTable().getRowCount()-1)); + } + private void offsetSelection(int[] selectedRows, int offset) { + for (int i = 0; i < selectedRows.length; i++) { + selectedRows[i] = selectedRows[i]+offset; + } + getJTable().setSelectedIndexes(selectedRows); + } + + protected JTable getJTable() { + return this.table; + } + + /** Moves the rows in the table model. + *
This method is called when the user clicks the up or down buttons. + *
It is mandatory that this method fire the proper model change event after updating the model. + * @param selectedRows The moved rows + * @param up true if the rows are moved up, false if they are moved down. + * @see ListUtils#move(java.util.List, int[], int) + */ + protected abstract void moveModelRows(int[] selectedRows, boolean up); +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/CSVExporter.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/CSVExporter.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/CSVExporter.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/CSVExporter.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/CustomCellRenderer.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/CustomCellRenderer.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/CustomCellRenderer.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/CustomCellRenderer.java index 3bdccb4..e9f39e5 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/CustomCellRenderer.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/CustomCellRenderer.java @@ -1,92 +1,92 @@ -package com.fathzer.soft.ajlib.swing.table; - -import java.awt.Color; -import java.awt.Component; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; - -import javax.swing.JTable; -import javax.swing.SwingConstants; -import javax.swing.table.DefaultTableCellRenderer; - -/** A JTable CellRenderer easy to customize. - */ -public class CustomCellRenderer extends DefaultTableCellRenderer { - private static final long serialVersionUID = 1L; - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - row = table.convertRowIndexToModel(row); - column = table.convertColumnIndexToModel(column); - this.setHorizontalAlignment(getAlignment(table, value, isSelected, hasFocus, row, column)); - this.setBackground(getBackground(table, value, isSelected, hasFocus, row, column)); - this.setForeground(getForeground(table, value, isSelected, hasFocus, row, column)); - setValue(getValue(table, value, isSelected, hasFocus, row, column)); - return this; - } - - /** Gets the alignment. - * @param table The JTable. - * @param value The original value in the cell (not the replacement value returned by getReplacementValue). - * @param isSelected true if cell is selected. - * @param hasFocus true if cell has the focus. - * @param rowModel The row index in the model. - * @param columnModel The column index in the model. - * @return The alignment, an int in the set SwingConstants: LEFT, CENTER, RIGHT, LEADING or TRAILING.
- * By default it returns CENTER if value is an instance of Date or Calendar, RIGHT if value is an instance of Float, Double - * BigDecimal, Integer, Long or BigInteger, LEFT in other cases.
You may override this method to change this behavior. - */ - protected int getAlignment(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { - int alignment = SwingConstants.LEFT; - if ((value instanceof Date) || (value instanceof Calendar)) { - alignment = SwingConstants.CENTER; - } else if ((value instanceof Double) || (value instanceof Integer) || (value instanceof Long) || (value instanceof Float) || - (value instanceof BigDecimal) || (value instanceof BigInteger)) { - alignment = SwingConstants.RIGHT; - } - return alignment; - } - - /** Gets the replacement value of a cell.
- * This method allows you to change the value on the fly. By default, it returns the original value but you can override - * this method to change this behavior. - * @param table The JTable. - * @param value The value in the cell. - * @param isSelected true if cell is selected. - * @param hasFocus true if cell has the focus. - * @param rowModel The row index in the model. - * @param columnModel The column index in the model. - * @return The value that should be displayed in the cell. - */ - protected Object getValue(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { - return value; - } - - /** Gets the background color of a cell.
- * @param table The JTable. - * @param value The value in the cell. - * @param isSelected true if cell is selected. - * @param hasFocus true if cell has the focus. - * @param rowModel The row index in the model. - * @param columnModel The column index in the model. - * @return The background color of the cell. The default implementation returns the JTable default color. - */ - protected Color getBackground(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { - return isSelected ? table.getSelectionBackground() : table.getBackground(); - } - - /** Gets the foreground color of a cell.
- * @param table The JTable. - * @param value The value in the cell. - * @param isSelected true if cell is selected. - * @param hasFocus true if cell has the focus. - * @param rowModel The row index in the model. - * @param columnModel The column index in the model. - * @return The foreground color of the cell. The default implementation returns the JTable default color. - */ - protected Color getForeground(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { - return isSelected ? table.getSelectionForeground() : table.getForeground(); - } -} +package com.fathzer.soft.ajlib.swing.table; + +import java.awt.Color; +import java.awt.Component; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Calendar; +import java.util.Date; + +import javax.swing.JTable; +import javax.swing.SwingConstants; +import javax.swing.table.DefaultTableCellRenderer; + +/** A JTable CellRenderer easy to customize. + */ +public class CustomCellRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + row = table.convertRowIndexToModel(row); + column = table.convertColumnIndexToModel(column); + this.setHorizontalAlignment(getAlignment(table, value, isSelected, hasFocus, row, column)); + this.setBackground(getBackground(table, value, isSelected, hasFocus, row, column)); + this.setForeground(getForeground(table, value, isSelected, hasFocus, row, column)); + setValue(getValue(table, value, isSelected, hasFocus, row, column)); + return this; + } + + /** Gets the alignment. + * @param table The JTable. + * @param value The original value in the cell (not the replacement value returned by getReplacementValue). + * @param isSelected true if cell is selected. + * @param hasFocus true if cell has the focus. + * @param rowModel The row index in the model. + * @param columnModel The column index in the model. + * @return The alignment, an int in the set SwingConstants: LEFT, CENTER, RIGHT, LEADING or TRAILING.
+ * By default it returns CENTER if value is an instance of Date or Calendar, RIGHT if value is an instance of Float, Double + * BigDecimal, Integer, Long or BigInteger, LEFT in other cases.
You may override this method to change this behavior. + */ + protected int getAlignment(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { + int alignment = SwingConstants.LEFT; + if ((value instanceof Date) || (value instanceof Calendar)) { + alignment = SwingConstants.CENTER; + } else if ((value instanceof Double) || (value instanceof Integer) || (value instanceof Long) || (value instanceof Float) || + (value instanceof BigDecimal) || (value instanceof BigInteger)) { + alignment = SwingConstants.RIGHT; + } + return alignment; + } + + /** Gets the replacement value of a cell.
+ * This method allows you to change the value on the fly. By default, it returns the original value but you can override + * this method to change this behavior. + * @param table The JTable. + * @param value The value in the cell. + * @param isSelected true if cell is selected. + * @param hasFocus true if cell has the focus. + * @param rowModel The row index in the model. + * @param columnModel The column index in the model. + * @return The value that should be displayed in the cell. + */ + protected Object getValue(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { + return value; + } + + /** Gets the background color of a cell.
+ * @param table The JTable. + * @param value The value in the cell. + * @param isSelected true if cell is selected. + * @param hasFocus true if cell has the focus. + * @param rowModel The row index in the model. + * @param columnModel The column index in the model. + * @return The background color of the cell. The default implementation returns the JTable default color. + */ + protected Color getBackground(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { + return isSelected ? table.getSelectionBackground() : table.getBackground(); + } + + /** Gets the foreground color of a cell.
+ * @param table The JTable. + * @param value The value in the cell. + * @param isSelected true if cell is selected. + * @param hasFocus true if cell has the focus. + * @param rowModel The row index in the model. + * @param columnModel The column index in the model. + * @return The foreground color of the cell. The default implementation returns the JTable default color. + */ + protected Color getForeground(JTable table, Object value, boolean isSelected, boolean hasFocus, int rowModel, int columnModel) { + return isSelected ? table.getSelectionForeground() : table.getForeground(); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/JTable.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTable.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/JTable.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTable.java index 6c4a1ed..0d3bd02 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/JTable.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTable.java @@ -1,50 +1,50 @@ -package com.fathzer.soft.ajlib.swing.table; - -import javax.swing.ListSelectionModel; -import javax.swing.table.TableModel; - -/** A JTable that fixes the following bugs in the JTable and adds some functionalities. - *
Bugs fixed:
    - *
  • Unlike the original swing JTable class, the row height is set accordingly to the font (see setRowHeigth() method). - *
    The method setRowHeight is called in the constructor. You should call it again when changing the font.
  • - *
- */ -@SuppressWarnings("serial") -public class JTable extends javax.swing.JTable { - public JTable() { - super(); - setRowHeight(); - } - - public JTable(TableModel tableModel) { - super(tableModel); - setRowHeight(); - } - - /** Sets the row height according to the font size. - *
The row height is set to getFont().getSize()*4/3. - */ - public void setRowHeight() { - setRowHeight(getFont().getSize()*4/3); - } - - /** Sets the selection. - * @param selectedIndexes The indexes to select.
Please note that specifying non contiguous selection on a table that - * uses SINGLE_SELECTION mode may have unpredictable result. - * @throws IllegalArgumentException if an index is out of bounds. - */ - public void setSelectedIndexes(int[] selectedIndexes) { - for (int i = 0; i < selectedIndexes.length; i++) { - if ((selectedIndexes[i]<0) || (selectedIndexes[i]>=getRowCount())) { - throw new IllegalArgumentException(); - } - } - ListSelectionModel selectionModel = getSelectionModel(); - selectionModel.setValueIsAdjusting(true); - selectionModel.clearSelection(); - for (int i = 0; i < selectedIndexes.length; i++) { - selectionModel.addSelectionInterval(selectedIndexes[i], selectedIndexes[i]); - } - selectionModel.setValueIsAdjusting(false); - } -} +package com.fathzer.soft.ajlib.swing.table; + +import javax.swing.ListSelectionModel; +import javax.swing.table.TableModel; + +/** A JTable that fixes the following bugs in the JTable and adds some functionalities. + *
Bugs fixed:
    + *
  • Unlike the original swing JTable class, the row height is set accordingly to the font (see setRowHeigth() method). + *
    The method setRowHeight is called in the constructor. You should call it again when changing the font.
  • + *
+ */ +@SuppressWarnings("serial") +public class JTable extends javax.swing.JTable { + public JTable() { + super(); + setRowHeight(); + } + + public JTable(TableModel tableModel) { + super(tableModel); + setRowHeight(); + } + + /** Sets the row height according to the font size. + *
The row height is set to getFont().getSize()*4/3. + */ + public void setRowHeight() { + setRowHeight(getFont().getSize()*4/3); + } + + /** Sets the selection. + * @param selectedIndexes The indexes to select.
Please note that specifying non contiguous selection on a table that + * uses SINGLE_SELECTION mode may have unpredictable result. + * @throws IllegalArgumentException if an index is out of bounds. + */ + public void setSelectedIndexes(int[] selectedIndexes) { + for (int i = 0; i < selectedIndexes.length; i++) { + if ((selectedIndexes[i]<0) || (selectedIndexes[i]>=getRowCount())) { + throw new IllegalArgumentException(); + } + } + ListSelectionModel selectionModel = getSelectionModel(); + selectionModel.setValueIsAdjusting(true); + selectionModel.clearSelection(); + for (int i = 0; i < selectedIndexes.length; i++) { + selectionModel.addSelectionInterval(selectedIndexes[i], selectedIndexes[i]); + } + selectionModel.setValueIsAdjusting(false); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableListener.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableListener.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/JTableListener.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableListener.java index 4f3cd94..edd8c47 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableListener.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableListener.java @@ -1,98 +1,98 @@ -package com.fathzer.soft.ajlib.swing.table; - -import java.awt.Point; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -import javax.swing.Action; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JTable; - -/** This is a standard JTable mouse listener. - *
It has action attributes (typically new/edit/delete/duplicate row) - *
When a double click occurs on the JTable, a default action is invoked (typically an edit action). - *
A pop-up menu is shown when needed (when a right click occurs under windows). - *
You should register this with a JTable in order to receive interesting events (using JTable.addMouseListener). - *
Please note that this class doesn't guarantee that actions will be enabled/disabled when rows are selected/deselected. - */ -public class JTableListener extends MouseAdapter { - private Action[] actions; - private Action defaultAction; - - /** Constructor. - * @param actions The actions that will appear in the popupMenu. Insert a null in this array to have a separator in the pop-up menu. - * If actions is null or contains no item, no pop-up menu is shown. - * @param defaultAction The default action which will be invoked when a double click occurs. This action is not necessarily one of the actions array. - * If defaultAction is null, nothing occurs on a double click. - */ - public JTableListener(Action[] actions, Action defaultAction) { - super(); - this.actions = actions==null ? null : actions.clone(); - this.defaultAction = defaultAction; - } - - @Override - public void mouseReleased(MouseEvent e) { - maybeShowPopup(e); - } - - @Override - public void mousePressed(MouseEvent e) { - JTable jTable = (JTable) e.getComponent(); - if ((e.getClickCount() == 2) && (e.getButton() == MouseEvent.BUTTON1)) { - Point p = e.getPoint(); - int row = jTable.rowAtPoint(p); - if (row >= 0) { - Action action = getDoubleClickAction(); - if (action != null) { - action.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "")); - } - } - } else { - maybeShowPopup(e); - } - } - - /** When a double click occurs, the action returned by this method is invoked. - * This implementation returns the defaultButton. - * You can override this method in order to change this action, for instance if the JTable has a mode switch that - * trigger another action when it is set. - * @return The action to invoke. - */ - protected Action getDoubleClickAction() { - return defaultAction; - } - - private void maybeShowPopup(MouseEvent e) { - JTable jTable = (JTable) e.getComponent(); - if (e.isPopupTrigger()) { - Point p = e.getPoint(); - int row = jTable.rowAtPoint(p); - if (!jTable.isRowSelected(row)) { - jTable.getSelectionModel().setSelectionInterval(row, row); - } - if ((actions!=null) && (actions.length>0)) { - JPopupMenu popup = new JPopupMenu(); - fillPopUp(popup); - popup.show(e.getComponent(), e.getX(), e.getY()); - } - } - } - - /** Fill the popup menu. - * This implementation put all the actions in the popup. You may override it in order to add - * more actions ... or less. - * @param popup the pop up to be filled. - */ - protected void fillPopUp(JPopupMenu popup) { - for (int i = 0; i < actions.length; i++) { - if (actions[i]==null) { - popup.addSeparator(); - } else { - popup.add(new JMenuItem(actions[i])); - } - } - } -} +package com.fathzer.soft.ajlib.swing.table; + +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.Action; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JTable; + +/** This is a standard JTable mouse listener. + *
It has action attributes (typically new/edit/delete/duplicate row) + *
When a double click occurs on the JTable, a default action is invoked (typically an edit action). + *
A pop-up menu is shown when needed (when a right click occurs under windows). + *
You should register this with a JTable in order to receive interesting events (using JTable.addMouseListener). + *
Please note that this class doesn't guarantee that actions will be enabled/disabled when rows are selected/deselected. + */ +public class JTableListener extends MouseAdapter { + private Action[] actions; + private Action defaultAction; + + /** Constructor. + * @param actions The actions that will appear in the popupMenu. Insert a null in this array to have a separator in the pop-up menu. + * If actions is null or contains no item, no pop-up menu is shown. + * @param defaultAction The default action which will be invoked when a double click occurs. This action is not necessarily one of the actions array. + * If defaultAction is null, nothing occurs on a double click. + */ + public JTableListener(Action[] actions, Action defaultAction) { + super(); + this.actions = actions==null ? null : actions.clone(); + this.defaultAction = defaultAction; + } + + @Override + public void mouseReleased(MouseEvent e) { + maybeShowPopup(e); + } + + @Override + public void mousePressed(MouseEvent e) { + JTable jTable = (JTable) e.getComponent(); + if ((e.getClickCount() == 2) && (e.getButton() == MouseEvent.BUTTON1)) { + Point p = e.getPoint(); + int row = jTable.rowAtPoint(p); + if (row >= 0) { + Action action = getDoubleClickAction(); + if (action != null) { + action.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "")); + } + } + } else { + maybeShowPopup(e); + } + } + + /** When a double click occurs, the action returned by this method is invoked. + * This implementation returns the defaultButton. + * You can override this method in order to change this action, for instance if the JTable has a mode switch that + * trigger another action when it is set. + * @return The action to invoke. + */ + protected Action getDoubleClickAction() { + return defaultAction; + } + + private void maybeShowPopup(MouseEvent e) { + JTable jTable = (JTable) e.getComponent(); + if (e.isPopupTrigger()) { + Point p = e.getPoint(); + int row = jTable.rowAtPoint(p); + if (!jTable.isRowSelected(row)) { + jTable.getSelectionModel().setSelectionInterval(row, row); + } + if ((actions!=null) && (actions.length>0)) { + JPopupMenu popup = new JPopupMenu(); + fillPopUp(popup); + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + } + + /** Fill the popup menu. + * This implementation put all the actions in the popup. You may override it in order to add + * more actions ... or less. + * @param popup the pop up to be filled. + */ + protected void fillPopUp(JPopupMenu popup) { + for (int i = 0; i < actions.length; i++) { + if (actions[i]==null) { + popup.addSeparator(); + } else { + popup.add(new JMenuItem(actions[i])); + } + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableSelector.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableSelector.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/JTableSelector.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableSelector.java index ad8c3ea..4c83812 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableSelector.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/JTableSelector.java @@ -1,49 +1,49 @@ -package com.fathzer.soft.ajlib.swing.table; - -import javax.swing.JTable; - -/** A class that is able to set the selection of elements in a JTable. - *
This could be seen very easy, but, unfortunately, there's some pitfalls: - * You have to set "value is adjusting" to false before start selecting elements, - * be aware of view/model index and don't forget to scroll the table to make the selection visible. - *
This class takes care of this for you. - * @param The class of the elements in the table. - */ -public abstract class JTableSelector { - /** The JTable that contains the selected elements.*/ - protected JTable table; - - /** Constructor - * @param table The JTable that contains the selected transactions. - */ - protected JTableSelector(JTable table) { - this.table = table; - } - - /** Selects some elements. - * @param elements The elements to select. If some elements do not exist in the table, they are ignored. - */ - public void setSelected(T[] elements) { - table.getSelectionModel().setValueIsAdjusting(true); - table.getSelectionModel().clearSelection(); - int firstViewRow = -1; - for (int i = 0; i < elements.length; i++) { - int row = getModelIndex(elements[i]); - if (row>=0) { - row = table.convertRowIndexToView(row); - if ((firstViewRow<0) || (firstViewRow>row)) { - firstViewRow = row; - } - table.getSelectionModel().addSelectionInterval(row, row); - } - } - table.getSelectionModel().setValueIsAdjusting(false); - table.scrollRectToVisible(table.getCellRect(firstViewRow, 0, true)); - } - - /** Gets the model index of an element. - * @param element an element - * @return the index of the element in the table model (a negative number is the element is not in the table). - */ - protected abstract int getModelIndex(T element); +package com.fathzer.soft.ajlib.swing.table; + +import javax.swing.JTable; + +/** A class that is able to set the selection of elements in a JTable. + *
This could be seen very easy, but, unfortunately, there's some pitfalls: + * You have to set "value is adjusting" to false before start selecting elements, + * be aware of view/model index and don't forget to scroll the table to make the selection visible. + *
This class takes care of this for you. + * @param The class of the elements in the table. + */ +public abstract class JTableSelector { + /** The JTable that contains the selected elements.*/ + protected JTable table; + + /** Constructor + * @param table The JTable that contains the selected transactions. + */ + protected JTableSelector(JTable table) { + this.table = table; + } + + /** Selects some elements. + * @param elements The elements to select. If some elements do not exist in the table, they are ignored. + */ + public void setSelected(T[] elements) { + table.getSelectionModel().setValueIsAdjusting(true); + table.getSelectionModel().clearSelection(); + int firstViewRow = -1; + for (int i = 0; i < elements.length; i++) { + int row = getModelIndex(elements[i]); + if (row>=0) { + row = table.convertRowIndexToView(row); + if ((firstViewRow<0) || (firstViewRow>row)) { + firstViewRow = row; + } + table.getSelectionModel().addSelectionInterval(row, row); + } + } + table.getSelectionModel().setValueIsAdjusting(false); + table.scrollRectToVisible(table.getCellRect(firstViewRow, 0, true)); + } + + /** Gets the model index of an element. + * @param element an element + * @return the index of the element in the table model (a negative number is the element is not in the table). + */ + protected abstract int getModelIndex(T element); } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/NimbusPatchBooleanTableCellRenderer.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/NimbusPatchBooleanTableCellRenderer.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/NimbusPatchBooleanTableCellRenderer.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/NimbusPatchBooleanTableCellRenderer.java index 23819c4..87a49a6 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/NimbusPatchBooleanTableCellRenderer.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/NimbusPatchBooleanTableCellRenderer.java @@ -1,48 +1,48 @@ -package com.fathzer.soft.ajlib.swing.table; - -import java.awt.Color; -import java.awt.Component; - -import javax.swing.JCheckBox; -import javax.swing.JTable; -import javax.swing.SwingConstants; -import javax.swing.table.DefaultTableCellRenderer; - -/** This class is a workaround for a very weird bug in Swing and Nimbus L&F : - *
Default boolean renderer is broken. Its background desperately remains blank instead of using the alternate Nimbus background. - * @see bug id 6723524 - */ -public class NimbusPatchBooleanTableCellRenderer extends DefaultTableCellRenderer { - private static final long serialVersionUID = 1L; - - private JCheckBox renderer; - - /** Constructor. - */ - public NimbusPatchBooleanTableCellRenderer() { - renderer = new JCheckBox(); - renderer.setHorizontalAlignment(SwingConstants.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Boolean b = (Boolean) value; - if (b != null) { - renderer.setSelected(b); - } - if (isSelected) { - renderer.setForeground(table.getSelectionForeground()); - renderer.setBackground(table.getSelectionBackground()); - } else { - // Call super in order to have background color initialized - super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - Color bg = getBackground(); - renderer.setForeground(getForeground()); - // We have to create a new color object because Nimbus returns - // a color of type DerivedColor, which behaves strange, not sure why. - renderer.setBackground(new Color(bg.getRed(), bg.getGreen(), bg.getBlue())); - renderer.setOpaque(true); - } - return renderer; - } -} +package com.fathzer.soft.ajlib.swing.table; + +import java.awt.Color; +import java.awt.Component; + +import javax.swing.JCheckBox; +import javax.swing.JTable; +import javax.swing.SwingConstants; +import javax.swing.table.DefaultTableCellRenderer; + +/** This class is a workaround for a very weird bug in Swing and Nimbus L&F : + *
Default boolean renderer is broken. Its background desperately remains blank instead of using the alternate Nimbus background. + * @see bug id 6723524 + */ +public class NimbusPatchBooleanTableCellRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = 1L; + + private JCheckBox renderer; + + /** Constructor. + */ + public NimbusPatchBooleanTableCellRenderer() { + renderer = new JCheckBox(); + renderer.setHorizontalAlignment(SwingConstants.CENTER); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Boolean b = (Boolean) value; + if (b != null) { + renderer.setSelected(b); + } + if (isSelected) { + renderer.setForeground(table.getSelectionForeground()); + renderer.setBackground(table.getSelectionBackground()); + } else { + // Call super in order to have background color initialized + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + Color bg = getBackground(); + renderer.setForeground(getForeground()); + // We have to create a new color object because Nimbus returns + // a color of type DerivedColor, which behaves strange, not sure why. + renderer.setBackground(new Color(bg.getRed(), bg.getGreen(), bg.getBlue())); + renderer.setOpaque(true); + } + return renderer; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/RowHeaderRenderer.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowHeaderRenderer.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/RowHeaderRenderer.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowHeaderRenderer.java index d7bc8d3..1581e65 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/RowHeaderRenderer.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowHeaderRenderer.java @@ -1,29 +1,29 @@ -package com.fathzer.soft.ajlib.swing.table; - -import java.awt.Component; - -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellRenderer; -import javax.swing.JTable; - -/** - * A table CellRenderer that renders row headers. - */ -public class RowHeaderRenderer implements TableCellRenderer { - private TableCellRenderer renderer; - - public RowHeaderRenderer() { - renderer = new JTable().getTableHeader().getDefaultRenderer(); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { - try { - return renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); - } catch (NullPointerException e) { - // Bug workaround: Under some unclear circumstances (Launch Application, Menu File/New, then choose Windows look and feel, then Windows Classic, - // then Nimbus, then, open a non empty file ... a NullPointerException occurred :-( - return new DefaultTableCellRenderer().getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); - } - } -} +package com.fathzer.soft.ajlib.swing.table; + +import java.awt.Component; + +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.JTable; + +/** + * A table CellRenderer that renders row headers. + */ +public class RowHeaderRenderer implements TableCellRenderer { + private TableCellRenderer renderer; + + public RowHeaderRenderer() { + renderer = new JTable().getTableHeader().getDefaultRenderer(); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { + try { + return renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); + } catch (NullPointerException e) { + // Bug workaround: Under some unclear circumstances (Launch Application, Menu File/New, then choose Windows look and feel, then Windows Classic, + // then Nimbus, then, open a non empty file ... a NullPointerException occurred :-( + return new DefaultTableCellRenderer().getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/RowModel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowModel.java similarity index 95% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/RowModel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowModel.java index 69fbeb2..25015dd 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/RowModel.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowModel.java @@ -1,37 +1,37 @@ -package com.fathzer.soft.ajlib.swing.table; - -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import javax.swing.table.AbstractTableModel; - -class RowModel extends AbstractTableModel { - private static final long serialVersionUID = 1L; - - private TitledRowsTableModel model; - - RowModel(TitledRowsTableModel model) { - this.model = model; - model.addTableModelListener(new TableModelListener() { - @Override - public void tableChanged(TableModelEvent e) { - //FIXME Be more precise ? - fireTableChanged(e); - } - }); - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - return model.getRowTitle(rowIndex, columnIndex); - } - - @Override - public int getRowCount() { - return model.getRowCount(); - } - - @Override - public int getColumnCount() { - return model.getTitlesColumnCount(); - } +package com.fathzer.soft.ajlib.swing.table; + +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.AbstractTableModel; + +class RowModel extends AbstractTableModel { + private static final long serialVersionUID = 1L; + + private TitledRowsTableModel model; + + RowModel(TitledRowsTableModel model) { + this.model = model; + model.addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) { + //FIXME Be more precise ? + fireTableChanged(e); + } + }); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return model.getRowTitle(rowIndex, columnIndex); + } + + @Override + public int getRowCount() { + return model.getRowCount(); + } + + @Override + public int getColumnCount() { + return model.getTitlesColumnCount(); + } } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/RowSorter.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowSorter.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/RowSorter.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowSorter.java index 1c37e5b..02dbdd0 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/RowSorter.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/RowSorter.java @@ -1,100 +1,100 @@ -package com.fathzer.soft.ajlib.swing.table; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.swing.SortOrder; -import javax.swing.table.TableModel; -import javax.swing.table.TableRowSorter; - -/** A subclass of javax.swing.TableRowSorter that allows the user to deselect sort keys. - *
The TableRowSorter receives user's clicks on the table header and manage the sort keys and order. - * Unfortunately, when the user click a column, the row sorter toggle the sort order (ascending/descending), - * but provides no way to deselect the sort key. - *
This class makes a cycle between ASCENDING, DESCENDING and UNSORTED when the user clicks a column header. - *
Using this class is simple as: - *
- *   JTable table;
- *   ...
- *   table.setRowSorter<TableModel>(new RowSorter(table.getModel()))
- *
- * @param The type of the table model - */ -public class RowSorter extends TableRowSorter { - private List toggleSequence; - - /** Constructor. - */ - public RowSorter() { - super(); - this.toggleSequence = Arrays.asList(SortOrder.values()); - } - - /** Constructor. - * @param model The table's model. - */ - public RowSorter(M model) { - super(model); - this.toggleSequence = Arrays.asList(SortOrder.values()); - } - - /* (non-Javadoc) - * @see javax.swing.DefaultRowSorter#toggleSortOrder(int) - */ - @Override - public void toggleSortOrder(int column) { - if (!isSortable(column)) { - return; - } - List sortKeys = getSortKeys(); - ArrayList futureKeys = new ArrayList<>(); - SortKey theKey = null; - for (SortKey sortKey : sortKeys) { - if (sortKey.getColumn()==column) { - int index = toggleSequence.indexOf(sortKey.getSortOrder()); - if ((index<0) || (index==toggleSequence.size()-1)) { - index = 0; - } else { - index++; - } - theKey = new SortKey(column, toggleSequence.get(index)); - } else { - futureKeys.add(sortKey); - } - } - if (theKey == null) { - theKey = new SortKey(column, toggleSequence.get(0)); - } - if (!theKey.getSortOrder().equals(SortOrder.UNSORTED)) { - futureKeys.add(0, theKey); - } else { - // One might be tempted to remove the key from the sort key list, it - // would not be a good idea - // If the UNSORTED is not at the end of the toggleSequence, it would - // broke the sequence - // Example: ASCENDING, UNSORTED, DESCENDING would leave DESCENDING - // unreachable - // Instead of removing the key, we will put it to the lowest - // priority. - futureKeys.add(theKey); - } - super.setSortKeys(futureKeys); - } - - /** Sets the toggle sequence. - *
By default, the sequence is ASCENDING, DESCENDING and UNSORTED - * @param sequence the toggle sequence, a non empty list of SortOrder. Null to restore the default order. - * Please note that if a SorterOrder occurs twice in the list, the behavior of this class is unpredictable. - * @throws IllegalArgumentException if the sequence is empty - */ - public void setToggleSequence(List sequence) { - if (sequence==null) { - this.toggleSequence = Arrays.asList(SortOrder.values()); - } else if (sequence.isEmpty()) { - throw new IllegalArgumentException("toggle sequence can't be empty"); - } else { - this.toggleSequence = sequence; - } - } -} +package com.fathzer.soft.ajlib.swing.table; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.swing.SortOrder; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; + +/** A subclass of javax.swing.TableRowSorter that allows the user to deselect sort keys. + *
The TableRowSorter receives user's clicks on the table header and manage the sort keys and order. + * Unfortunately, when the user click a column, the row sorter toggle the sort order (ascending/descending), + * but provides no way to deselect the sort key. + *
This class makes a cycle between ASCENDING, DESCENDING and UNSORTED when the user clicks a column header. + *
Using this class is simple as: + *
+ *   JTable table;
+ *   ...
+ *   table.setRowSorter<TableModel>(new RowSorter(table.getModel()))
+ *
+ * @param The type of the table model + */ +public class RowSorter extends TableRowSorter { + private List toggleSequence; + + /** Constructor. + */ + public RowSorter() { + super(); + this.toggleSequence = Arrays.asList(SortOrder.values()); + } + + /** Constructor. + * @param model The table's model. + */ + public RowSorter(M model) { + super(model); + this.toggleSequence = Arrays.asList(SortOrder.values()); + } + + /* (non-Javadoc) + * @see javax.swing.DefaultRowSorter#toggleSortOrder(int) + */ + @Override + public void toggleSortOrder(int column) { + if (!isSortable(column)) { + return; + } + List sortKeys = getSortKeys(); + ArrayList futureKeys = new ArrayList<>(); + SortKey theKey = null; + for (SortKey sortKey : sortKeys) { + if (sortKey.getColumn()==column) { + int index = toggleSequence.indexOf(sortKey.getSortOrder()); + if ((index<0) || (index==toggleSequence.size()-1)) { + index = 0; + } else { + index++; + } + theKey = new SortKey(column, toggleSequence.get(index)); + } else { + futureKeys.add(sortKey); + } + } + if (theKey == null) { + theKey = new SortKey(column, toggleSequence.get(0)); + } + if (!theKey.getSortOrder().equals(SortOrder.UNSORTED)) { + futureKeys.add(0, theKey); + } else { + // One might be tempted to remove the key from the sort key list, it + // would not be a good idea + // If the UNSORTED is not at the end of the toggleSequence, it would + // broke the sequence + // Example: ASCENDING, UNSORTED, DESCENDING would leave DESCENDING + // unreachable + // Instead of removing the key, we will put it to the lowest + // priority. + futureKeys.add(theKey); + } + super.setSortKeys(futureKeys); + } + + /** Sets the toggle sequence. + *
By default, the sequence is ASCENDING, DESCENDING and UNSORTED + * @param sequence the toggle sequence, a non empty list of SortOrder. Null to restore the default order. + * Please note that if a SorterOrder occurs twice in the list, the behavior of this class is unpredictable. + * @throws IllegalArgumentException if the sequence is empty + */ + public void setToggleSequence(List sequence) { + if (sequence==null) { + this.toggleSequence = Arrays.asList(SortOrder.values()); + } else if (sequence.isEmpty()) { + throw new IllegalArgumentException("toggle sequence can't be empty"); + } else { + this.toggleSequence = sequence; + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/Table.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/Table.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/Table.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/Table.java index ac2a9d2..d379992 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/Table.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/Table.java @@ -1,187 +1,187 @@ -package com.fathzer.soft.ajlib.swing.table; - -import javax.swing.JPanel; - -import java.awt.Dimension; -import javax.swing.JScrollPane; - -import javax.swing.event.ChangeEvent; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.TableColumnModelEvent; -import javax.swing.event.TableColumnModelListener; -import javax.swing.table.TableModel; - -import com.fathzer.soft.ajlib.swing.Utils; - -import java.awt.BorderLayout; - -/** - * JTable has a lot of lacks, this class adds the ability for a table to have - * row titles. - */ -public class Table extends JPanel { - private static final long serialVersionUID = 1L; - - private JScrollPane scrollPane; - private JTable table; - private JTable rowView; - - /** - * Constructor. - */ - public Table() { - initialize(); - } - - private void initialize() { - setLayout(new BorderLayout(0, 0)); - add(getScrollPane()); - } - - private JScrollPane getScrollPane() { - if (scrollPane == null) { - scrollPane = new JScrollPane(); - scrollPane.setRowHeaderView(getRowJTable()); - scrollPane.setViewportView(getJTable()); - } - return scrollPane; - } - - /** - * Gets the internal table that is used to display table rows.
- * You can override this method in order to create a customized row table. - * - * @return a JTable - */ - public final JTable getRowJTable() { - if (rowView == null) { - rowView = new JTable(); - rowView.setDefaultRenderer(Object.class, new RowHeaderRenderer()); - rowView.setFocusable(false); - rowView.setCellSelectionEnabled(false); - setRowViewSize(rowView); - } - return rowView; - } - - private void setRowViewSize(final JTable rowView) { - int width = 0; - for (int i = 0; i < rowView.getColumnCount(); i++) { - width += Utils.packColumn(rowView, i, 2); - } - Dimension d = rowView.getPreferredScrollableViewportSize(); - d.width = width; - rowView.setPreferredScrollableViewportSize(d); - } - - /** - * Gets the internal JTable. - * - * @return a JTable - * @see #buildJTable() - */ - public final JTable getJTable() { - if (table == null) { - table = buildJTable(); - if (table.getModel() instanceof TitledRowsTableModel) { - installModelInRowJTable((TitledRowsTableModel) table.getModel()); - } - } - return table; - } - - /** - * Builds the internal JTable.
- * This table doesn't not contains the row titles.
- * This method is called once and creates the internal JTable.
- * It is useful to customize the table (for example to change its - * CellRenderer).
- * So, you can override this method in order to create a customized table. - * - * @return a JTable - */ - protected JTable buildJTable() { - return new JTable(); - } - - /** - * Sets the table model.
- * Please note that you should not modify directly the model of the internal - * JTable. It would results in having the row titles not updated. - * - * @param model - * The model. If this model implements TitledRowsTableModel, the - * table will have row titles. - * @see TitledRowsTableModel - */ - public void setModel(TableModel model) { - table.setModel(model); - if (model instanceof TitledRowsTableModel) { - installModelInRowJTable((TitledRowsTableModel) model); - } - } - - private void installModelInRowJTable(TitledRowsTableModel model) { - final TableModel rowHeaderModel = new RowModel(model); - getRowJTable().setModel(rowHeaderModel); - getRowJTable().getColumnModel().addColumnModelListener( - new TableColumnModelListener() { - - @Override - public void columnSelectionChanged(ListSelectionEvent e) { - // Nothing to do - } - - @Override - public void columnRemoved(TableColumnModelEvent e) { - setRowViewSize(getRowJTable()); - } - - @Override - public void columnMoved(TableColumnModelEvent e) { - // Nothing to do - } - - @Override - public void columnMarginChanged(ChangeEvent e) { - // Nothing to do - } - - @Override - public void columnAdded(TableColumnModelEvent e) { - setRowViewSize(getRowJTable()); - } - }); - setRowViewSize(getRowJTable()); - } - - /** - * Gets the table model. - * - * @return a TableModel - */ - public TableModel getModel() { - return this.getJTable().getModel(); - } - - /** - * Gets the default row height of this table. - * - * @return an integer. - */ - public int getRowHeight() { - return this.getJTable().getRowHeight(); - } - - /** - * Sets the default row height of this table.
- * This method sets the row height of the main table and its title table. - * - * @param rowHeight - * The new row height - */ - public void setRowHeight(int rowHeight) { - this.getJTable().setRowHeight(rowHeight); - this.getRowJTable().setRowHeight(rowHeight); - } +package com.fathzer.soft.ajlib.swing.table; + +import javax.swing.JPanel; + +import java.awt.Dimension; +import javax.swing.JScrollPane; + +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.TableColumnModelEvent; +import javax.swing.event.TableColumnModelListener; +import javax.swing.table.TableModel; + +import com.fathzer.soft.ajlib.swing.Utils; + +import java.awt.BorderLayout; + +/** + * JTable has a lot of lacks, this class adds the ability for a table to have + * row titles. + */ +public class Table extends JPanel { + private static final long serialVersionUID = 1L; + + private JScrollPane scrollPane; + private JTable table; + private JTable rowView; + + /** + * Constructor. + */ + public Table() { + initialize(); + } + + private void initialize() { + setLayout(new BorderLayout(0, 0)); + add(getScrollPane()); + } + + private JScrollPane getScrollPane() { + if (scrollPane == null) { + scrollPane = new JScrollPane(); + scrollPane.setRowHeaderView(getRowJTable()); + scrollPane.setViewportView(getJTable()); + } + return scrollPane; + } + + /** + * Gets the internal table that is used to display table rows.
+ * You can override this method in order to create a customized row table. + * + * @return a JTable + */ + public final JTable getRowJTable() { + if (rowView == null) { + rowView = new JTable(); + rowView.setDefaultRenderer(Object.class, new RowHeaderRenderer()); + rowView.setFocusable(false); + rowView.setCellSelectionEnabled(false); + setRowViewSize(rowView); + } + return rowView; + } + + private void setRowViewSize(final JTable rowView) { + int width = 0; + for (int i = 0; i < rowView.getColumnCount(); i++) { + width += Utils.packColumn(rowView, i, 2); + } + Dimension d = rowView.getPreferredScrollableViewportSize(); + d.width = width; + rowView.setPreferredScrollableViewportSize(d); + } + + /** + * Gets the internal JTable. + * + * @return a JTable + * @see #buildJTable() + */ + public final JTable getJTable() { + if (table == null) { + table = buildJTable(); + if (table.getModel() instanceof TitledRowsTableModel) { + installModelInRowJTable((TitledRowsTableModel) table.getModel()); + } + } + return table; + } + + /** + * Builds the internal JTable.
+ * This table doesn't not contains the row titles.
+ * This method is called once and creates the internal JTable.
+ * It is useful to customize the table (for example to change its + * CellRenderer).
+ * So, you can override this method in order to create a customized table. + * + * @return a JTable + */ + protected JTable buildJTable() { + return new JTable(); + } + + /** + * Sets the table model.
+ * Please note that you should not modify directly the model of the internal + * JTable. It would results in having the row titles not updated. + * + * @param model + * The model. If this model implements TitledRowsTableModel, the + * table will have row titles. + * @see TitledRowsTableModel + */ + public void setModel(TableModel model) { + table.setModel(model); + if (model instanceof TitledRowsTableModel) { + installModelInRowJTable((TitledRowsTableModel) model); + } + } + + private void installModelInRowJTable(TitledRowsTableModel model) { + final TableModel rowHeaderModel = new RowModel(model); + getRowJTable().setModel(rowHeaderModel); + getRowJTable().getColumnModel().addColumnModelListener( + new TableColumnModelListener() { + + @Override + public void columnSelectionChanged(ListSelectionEvent e) { + // Nothing to do + } + + @Override + public void columnRemoved(TableColumnModelEvent e) { + setRowViewSize(getRowJTable()); + } + + @Override + public void columnMoved(TableColumnModelEvent e) { + // Nothing to do + } + + @Override + public void columnMarginChanged(ChangeEvent e) { + // Nothing to do + } + + @Override + public void columnAdded(TableColumnModelEvent e) { + setRowViewSize(getRowJTable()); + } + }); + setRowViewSize(getRowJTable()); + } + + /** + * Gets the table model. + * + * @return a TableModel + */ + public TableModel getModel() { + return this.getJTable().getModel(); + } + + /** + * Gets the default row height of this table. + * + * @return an integer. + */ + public int getRowHeight() { + return this.getJTable().getRowHeight(); + } + + /** + * Sets the default row height of this table.
+ * This method sets the row height of the main table and its title table. + * + * @param rowHeight + * The new row height + */ + public void setRowHeight(int rowHeight) { + this.getJTable().setRowHeight(rowHeight); + this.getRowJTable().setRowHeight(rowHeight); + } } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/TitledRowsTableModel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/TitledRowsTableModel.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/TitledRowsTableModel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/TitledRowsTableModel.java index a6d831e..702b360 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/TitledRowsTableModel.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/TitledRowsTableModel.java @@ -1,20 +1,20 @@ -package com.fathzer.soft.ajlib.swing.table; - -import javax.swing.table.TableModel; - -/** A TableModel that defines titles for its rows. - *
This kind of model can be used with Table that automatically display titles when its model implements this interface. - */ -public interface TitledRowsTableModel extends TableModel { - /** Gets the number of columns dedicated to the row titles. - * @return an positive integer - */ - public int getTitlesColumnCount(); - - /** Gets a row title. - * @param rowIndex The index of the row - * @param columnIndex The index of the title column - * @return the title of the row - */ - public String getRowTitle(int rowIndex, int columnIndex); +package com.fathzer.soft.ajlib.swing.table; + +import javax.swing.table.TableModel; + +/** A TableModel that defines titles for its rows. + *
This kind of model can be used with Table that automatically display titles when its model implements this interface. + */ +public interface TitledRowsTableModel extends TableModel { + /** Gets the number of columns dedicated to the row titles. + * @return an positive integer + */ + public int getTitlesColumnCount(); + + /** Gets a row title. + * @param rowIndex The index of the row + * @param columnIndex The index of the title column + * @return the title of the row + */ + public String getRowTitle(int rowIndex, int columnIndex); } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/table/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/package-info.java similarity index 98% rename from src/main/java/com/fathzer/soft/ajlib/swing/table/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/package-info.java index 636303f..5df2689 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/table/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/table/package-info.java @@ -1,2 +1,2 @@ -/** Table utilities*/ +/** Table utilities*/ package com.fathzer.soft.ajlib.swing.table; \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractFileSelector.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractFileSelector.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractFileSelector.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractFileSelector.java index a94de96..4e31163 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractFileSelector.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractFileSelector.java @@ -1,297 +1,297 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; -import javax.swing.JPanel; - -import java.awt.GridBagLayout; - -import javax.swing.JButton; - -import java.awt.GridBagConstraints; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; - -import javax.swing.ImageIcon; - -import com.fathzer.soft.ajlib.swing.Utils; -import com.fathzer.soft.ajlib.swing.dialog.FileChooser; -import com.fathzer.soft.ajlib.swing.framework.Application; -import com.fathzer.soft.ajlib.utilities.NullUtils; - -/** An abstract widget composed of four buttons to open, save, save as and create new file. - */ -public abstract class AbstractFileSelector extends JPanel { - private static final long serialVersionUID = 1L; - /** A property bind with the file selected in this panel.*/ - public static final String SELECTED_FILE_PROPERTY = "SELECTED_FILE"; - /** A property bind with the changes of the data managed by this panel.*/ - public static final String CHANGED_PROPERTY = "CHANGED"; - /** A property bind with the emptiness the data managed by this panel.*/ - public static final String EMPTY_PROPERTY = "EMPTY"; - - private JButton btnOpen; - private JButton btnNew; - private JButton btnSave; - private JButton btnSaveAs; - - private File file; - private boolean isChanged; - - /** - * Creates the panel. - */ - protected AbstractFileSelector() { - initialize(); - this.isChanged = false; - } - private void initialize() { - GridBagLayout gridBagLayout = new GridBagLayout(); - setLayout(gridBagLayout); - GridBagConstraints gbcBtnOpen = new GridBagConstraints(); - gbcBtnOpen.gridx = 0; - gbcBtnOpen.gridy = 0; - add(getBtnOpen(), gbcBtnOpen); - GridBagConstraints gbcBtnNew = new GridBagConstraints(); - gbcBtnNew.gridx = 1; - gbcBtnNew.gridy = 0; - add(getBtnNew(), gbcBtnNew); - GridBagConstraints gbcBtnSave = new GridBagConstraints(); - gbcBtnSave.gridx = 2; - gbcBtnSave.gridy = 0; - add(getBtnSave(), gbcBtnSave); - GridBagConstraints gbcBtnSaveAs = new GridBagConstraints(); - gbcBtnSaveAs.weightx = 1.0; - gbcBtnSaveAs.anchor = GridBagConstraints.WEST; - gbcBtnSaveAs.gridx = 3; - gbcBtnSaveAs.gridy = 0; - add(getBtnSaveAs(), gbcBtnSaveAs); - } - - protected JButton getBtnOpen() { - if (btnOpen == null) { - btnOpen = new JButton(Application.getString("FileSelector.open", getLocale())); //$NON-NLS-1$ - btnOpen.setToolTipText(Application.getString("FileSelector.open.tooltip", getLocale())); //$NON-NLS-1$ - btnOpen.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("Open.png"))); //$NON-NLS-1$ - btnOpen.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (!lastChanceToSave()) { - return; - } - JFileChooser chooser = new FileChooser(); - if (getSelectedFile()!=null) { - chooser.setCurrentDirectory(getSelectedFile().getParentFile()); - } - File selectedFile = chooser.showOpenDialog(Utils.getOwnerWindow(btnOpen)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; - if (selectedFile != null && read(selectedFile)) { - setFile(selectedFile); - setEmpty(false); - } - } - }); - } - return btnOpen; - } - protected JButton getBtnNew() { - if (btnNew == null) { - btnNew = new JButton(Application.getString("FileSelector.new", getLocale())); //$NON-NLS-1$ - btnNew.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("New.png"))); //$NON-NLS-1$ - btnNew.setToolTipText(Application.getString("FileSelector.new.tooltip", getLocale())); //$NON-NLS-1$ - btnNew.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (!lastChanceToSave()) { - return; - } - newFile(); - setEmpty(true); - setFile(null); - } - }); - } - return btnNew; - } - protected JButton getBtnSave() { - if (btnSave == null) { - btnSave = new JButton(Application.getString("FileSelector.save", getLocale())); //$NON-NLS-1$ - btnSave.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("Save.png"))); //$NON-NLS-1$ - btnSave.setToolTipText(Application.getString("FileSelector.save.tooltip", getLocale())); //$NON-NLS-1$ - btnSave.setEnabled(false); - btnSave.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - File selectedFile = getSelectedFile(); - if (selectedFile==null) { - JFileChooser chooser = new FileChooser(); - selectedFile = chooser.showSaveDialog(Utils.getOwnerWindow(btnSave)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; - } - if (selectedFile != null && save(selectedFile)) { - setFile(selectedFile); - } - } - }); - } - return btnSave; - } - protected JButton getBtnSaveAs() { - if (btnSaveAs == null) { - btnSaveAs = new JButton(Application.getString("FileSelector.saveAs", getLocale())); //$NON-NLS-1$ - btnSaveAs.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("SaveAs.png"))); //$NON-NLS-1$ - btnSaveAs.setToolTipText(Application.getString("FileSelector.saveAs.tooltip", getLocale())); //$NON-NLS-1$ - btnSaveAs.setEnabled(false); - btnSaveAs.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JFileChooser chooser = new FileChooser(); - if (getSelectedFile()!=null) { - chooser.setCurrentDirectory(getSelectedFile().getParentFile()); - } - File selectedFile = chooser.showSaveDialog(Utils.getOwnerWindow(btnSave)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; - if (selectedFile != null && save(selectedFile)) { - setFile(selectedFile); - } - } - }); - } - return btnSaveAs; - } - - private void updateButtons(boolean isEmpty) { - getBtnSaveAs().setEnabled(!isEmpty); - getBtnSave().setEnabled(!isEmpty && isChanged()); - } - - /** Informs this panel that the data was changed since last save. - *
This method should be called when the data state changes. - *
Please note that when data is successfully read, saved, or created using this panel buttons, changed state is automatically set to false. - * @param isChanged true if dated as been modified since last call to save method. - * @see #save(File) - */ - public void setChanged(boolean isChanged) { - if (isChanged!=this.isChanged) { - boolean oldIsChanged = this.isChanged; - this.isChanged = isChanged; - updateButtons(isEmpty()); - firePropertyChange(CHANGED_PROPERTY, oldIsChanged, isChanged); - } - } - - /** Informs this panel that the data was changed since last save. - *
This method should be called when the data state changes. - *
Please note that when data is successfully read, empty state is automatically set to false. - *
Please note that when data is successfully create, empty state is automatically set to true. - * @param isEmpty true if data is empty. - */ - public void setEmpty(boolean isEmpty) { - if (isEmpty!=this.isEmpty()) { - boolean oldIsEmpty = isEmpty(); - updateButtons(isEmpty); - firePropertyChange(EMPTY_PROPERTY, oldIsEmpty, isEmpty); - } - } - - /** This method is called before changing the selected file. - *
If current data is not saved yet, this method asks the user what to do (ignore, save, cancel). - *
You can call this method if needed, for example, when the window containing this widget is closing. - * @return true if the process can continue, false to keep everything unchanged - */ - public boolean lastChanceToSave() { - if (!getBtnSave().isEnabled()) { - // If save button is enabled, there's nothing to save - return true; - } - String[] options = new String[]{getBtnSave().getText(),Application.getString("GenericButton.ignore", getLocale()), - Application.getString("GenericButton.cancel", getLocale())}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - int n = JOptionPane.showOptionDialog(Utils.getOwnerWindow(this), - getUnsavedQuestion(), - Application.getString("FileSelector.unsavedChanges", getLocale()), //$NON-NLS-1$ - JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.WARNING_MESSAGE, - null, //do not use a custom Icon - options, //the titles of buttons - options[2]); //default button title - if (n==-1 || n==2) { - return false; - } else if (n==0) { - if (getSelectedFile()==null) { - JFileChooser chooser = new FileChooser(); - File selectedFile = chooser.showSaveDialog(Utils.getOwnerWindow(this)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; - if (selectedFile != null) { - boolean ok = save(selectedFile); - if (ok) { - setFile(selectedFile); - } - return ok; - } else { - return false; - } - } - } - return true; - } - - /** Gets the question to ask to the user when lastChanceToSave is called and data has unsaved changes. - *
Subclasses may override this method to customize the question. - * @return The question to ask. - */ - protected String getUnsavedQuestion() { - return Application.getString("FileSelector.unsavedChanges.question", getLocale()); //$NON-NLS-1$ - } - - /** Reads a file. - *
This method is called when the user clicks the "open" button. - * @param file The file to read. - * @return true if the file was successfully read, false if the file reading failed. - */ - protected abstract boolean read(File file); - - /** Creates a new empty data set. - *
This method is called when the user clicks the "new" button. - */ - protected abstract void newFile(); - - /** Saves the data to a file. - *
This method is called when the user clicks the "Save" or "Save as" buttons. - * @param file The file where to save the data. - * @return true if the save succeeds. - */ - protected abstract boolean save(File file); - - /** Gets the current edited file. - * @return The selected file or null if no file is selected. - */ - public File getSelectedFile() { - return this.file; - } - - /** Sets the selected file. - *
If current file has unmodified changes, a dialog asks the user if he wants to save the changes. - * @param file The file to read or null to clear the data and select no file - */ - public void setSelectedFile(File file) { - if (!NullUtils.areEquals(this.file, file)) { - if (!lastChanceToSave()) { - return; - } - if (file == null && lastChanceToSave()) { - newFile(); - setFile(null); - } else if (read(file)) { - setFile(file); - } - } - } - - private void setFile(File file) { - File old = this.file; - this.file = file; - firePropertyChange(SELECTED_FILE_PROPERTY, old, file); - setChanged(false); - } - - public boolean isEmpty() { - return !getBtnSaveAs().isEnabled(); - } - - public boolean isChanged() { - return isChanged; - } +package com.fathzer.soft.ajlib.swing.widget; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import java.awt.GridBagLayout; + +import javax.swing.JButton; + +import java.awt.GridBagConstraints; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.ImageIcon; + +import com.fathzer.soft.ajlib.swing.Utils; +import com.fathzer.soft.ajlib.swing.dialog.FileChooser; +import com.fathzer.soft.ajlib.swing.framework.Application; +import com.fathzer.soft.ajlib.utilities.NullUtils; + +/** An abstract widget composed of four buttons to open, save, save as and create new file. + */ +public abstract class AbstractFileSelector extends JPanel { + private static final long serialVersionUID = 1L; + /** A property bind with the file selected in this panel.*/ + public static final String SELECTED_FILE_PROPERTY = "SELECTED_FILE"; + /** A property bind with the changes of the data managed by this panel.*/ + public static final String CHANGED_PROPERTY = "CHANGED"; + /** A property bind with the emptiness the data managed by this panel.*/ + public static final String EMPTY_PROPERTY = "EMPTY"; + + private JButton btnOpen; + private JButton btnNew; + private JButton btnSave; + private JButton btnSaveAs; + + private File file; + private boolean isChanged; + + /** + * Creates the panel. + */ + protected AbstractFileSelector() { + initialize(); + this.isChanged = false; + } + private void initialize() { + GridBagLayout gridBagLayout = new GridBagLayout(); + setLayout(gridBagLayout); + GridBagConstraints gbcBtnOpen = new GridBagConstraints(); + gbcBtnOpen.gridx = 0; + gbcBtnOpen.gridy = 0; + add(getBtnOpen(), gbcBtnOpen); + GridBagConstraints gbcBtnNew = new GridBagConstraints(); + gbcBtnNew.gridx = 1; + gbcBtnNew.gridy = 0; + add(getBtnNew(), gbcBtnNew); + GridBagConstraints gbcBtnSave = new GridBagConstraints(); + gbcBtnSave.gridx = 2; + gbcBtnSave.gridy = 0; + add(getBtnSave(), gbcBtnSave); + GridBagConstraints gbcBtnSaveAs = new GridBagConstraints(); + gbcBtnSaveAs.weightx = 1.0; + gbcBtnSaveAs.anchor = GridBagConstraints.WEST; + gbcBtnSaveAs.gridx = 3; + gbcBtnSaveAs.gridy = 0; + add(getBtnSaveAs(), gbcBtnSaveAs); + } + + protected JButton getBtnOpen() { + if (btnOpen == null) { + btnOpen = new JButton(Application.getString("FileSelector.open", getLocale())); //$NON-NLS-1$ + btnOpen.setToolTipText(Application.getString("FileSelector.open.tooltip", getLocale())); //$NON-NLS-1$ + btnOpen.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("Open.png"))); //$NON-NLS-1$ + btnOpen.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (!lastChanceToSave()) { + return; + } + JFileChooser chooser = new FileChooser(); + if (getSelectedFile()!=null) { + chooser.setCurrentDirectory(getSelectedFile().getParentFile()); + } + File selectedFile = chooser.showOpenDialog(Utils.getOwnerWindow(btnOpen)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; + if (selectedFile != null && read(selectedFile)) { + setFile(selectedFile); + setEmpty(false); + } + } + }); + } + return btnOpen; + } + protected JButton getBtnNew() { + if (btnNew == null) { + btnNew = new JButton(Application.getString("FileSelector.new", getLocale())); //$NON-NLS-1$ + btnNew.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("New.png"))); //$NON-NLS-1$ + btnNew.setToolTipText(Application.getString("FileSelector.new.tooltip", getLocale())); //$NON-NLS-1$ + btnNew.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (!lastChanceToSave()) { + return; + } + newFile(); + setEmpty(true); + setFile(null); + } + }); + } + return btnNew; + } + protected JButton getBtnSave() { + if (btnSave == null) { + btnSave = new JButton(Application.getString("FileSelector.save", getLocale())); //$NON-NLS-1$ + btnSave.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("Save.png"))); //$NON-NLS-1$ + btnSave.setToolTipText(Application.getString("FileSelector.save.tooltip", getLocale())); //$NON-NLS-1$ + btnSave.setEnabled(false); + btnSave.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + File selectedFile = getSelectedFile(); + if (selectedFile==null) { + JFileChooser chooser = new FileChooser(); + selectedFile = chooser.showSaveDialog(Utils.getOwnerWindow(btnSave)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; + } + if (selectedFile != null && save(selectedFile)) { + setFile(selectedFile); + } + } + }); + } + return btnSave; + } + protected JButton getBtnSaveAs() { + if (btnSaveAs == null) { + btnSaveAs = new JButton(Application.getString("FileSelector.saveAs", getLocale())); //$NON-NLS-1$ + btnSaveAs.setIcon(new ImageIcon(AbstractFileSelector.class.getResource("SaveAs.png"))); //$NON-NLS-1$ + btnSaveAs.setToolTipText(Application.getString("FileSelector.saveAs.tooltip", getLocale())); //$NON-NLS-1$ + btnSaveAs.setEnabled(false); + btnSaveAs.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new FileChooser(); + if (getSelectedFile()!=null) { + chooser.setCurrentDirectory(getSelectedFile().getParentFile()); + } + File selectedFile = chooser.showSaveDialog(Utils.getOwnerWindow(btnSave)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; + if (selectedFile != null && save(selectedFile)) { + setFile(selectedFile); + } + } + }); + } + return btnSaveAs; + } + + private void updateButtons(boolean isEmpty) { + getBtnSaveAs().setEnabled(!isEmpty); + getBtnSave().setEnabled(!isEmpty && isChanged()); + } + + /** Informs this panel that the data was changed since last save. + *
This method should be called when the data state changes. + *
Please note that when data is successfully read, saved, or created using this panel buttons, changed state is automatically set to false. + * @param isChanged true if dated as been modified since last call to save method. + * @see #save(File) + */ + public void setChanged(boolean isChanged) { + if (isChanged!=this.isChanged) { + boolean oldIsChanged = this.isChanged; + this.isChanged = isChanged; + updateButtons(isEmpty()); + firePropertyChange(CHANGED_PROPERTY, oldIsChanged, isChanged); + } + } + + /** Informs this panel that the data was changed since last save. + *
This method should be called when the data state changes. + *
Please note that when data is successfully read, empty state is automatically set to false. + *
Please note that when data is successfully create, empty state is automatically set to true. + * @param isEmpty true if data is empty. + */ + public void setEmpty(boolean isEmpty) { + if (isEmpty!=this.isEmpty()) { + boolean oldIsEmpty = isEmpty(); + updateButtons(isEmpty); + firePropertyChange(EMPTY_PROPERTY, oldIsEmpty, isEmpty); + } + } + + /** This method is called before changing the selected file. + *
If current data is not saved yet, this method asks the user what to do (ignore, save, cancel). + *
You can call this method if needed, for example, when the window containing this widget is closing. + * @return true if the process can continue, false to keep everything unchanged + */ + public boolean lastChanceToSave() { + if (!getBtnSave().isEnabled()) { + // If save button is enabled, there's nothing to save + return true; + } + String[] options = new String[]{getBtnSave().getText(),Application.getString("GenericButton.ignore", getLocale()), + Application.getString("GenericButton.cancel", getLocale())}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + int n = JOptionPane.showOptionDialog(Utils.getOwnerWindow(this), + getUnsavedQuestion(), + Application.getString("FileSelector.unsavedChanges", getLocale()), //$NON-NLS-1$ + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE, + null, //do not use a custom Icon + options, //the titles of buttons + options[2]); //default button title + if (n==-1 || n==2) { + return false; + } else if (n==0) { + if (getSelectedFile()==null) { + JFileChooser chooser = new FileChooser(); + File selectedFile = chooser.showSaveDialog(Utils.getOwnerWindow(this)) == JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; + if (selectedFile != null) { + boolean ok = save(selectedFile); + if (ok) { + setFile(selectedFile); + } + return ok; + } else { + return false; + } + } + } + return true; + } + + /** Gets the question to ask to the user when lastChanceToSave is called and data has unsaved changes. + *
Subclasses may override this method to customize the question. + * @return The question to ask. + */ + protected String getUnsavedQuestion() { + return Application.getString("FileSelector.unsavedChanges.question", getLocale()); //$NON-NLS-1$ + } + + /** Reads a file. + *
This method is called when the user clicks the "open" button. + * @param file The file to read. + * @return true if the file was successfully read, false if the file reading failed. + */ + protected abstract boolean read(File file); + + /** Creates a new empty data set. + *
This method is called when the user clicks the "new" button. + */ + protected abstract void newFile(); + + /** Saves the data to a file. + *
This method is called when the user clicks the "Save" or "Save as" buttons. + * @param file The file where to save the data. + * @return true if the save succeeds. + */ + protected abstract boolean save(File file); + + /** Gets the current edited file. + * @return The selected file or null if no file is selected. + */ + public File getSelectedFile() { + return this.file; + } + + /** Sets the selected file. + *
If current file has unmodified changes, a dialog asks the user if he wants to save the changes. + * @param file The file to read or null to clear the data and select no file + */ + public void setSelectedFile(File file) { + if (!NullUtils.areEquals(this.file, file)) { + if (!lastChanceToSave()) { + return; + } + if (file == null && lastChanceToSave()) { + newFile(); + setFile(null); + } else if (read(file)) { + setFile(file); + } + } + } + + private void setFile(File file) { + File old = this.file; + this.file = file; + firePropertyChange(SELECTED_FILE_PROPERTY, old, file); + setChanged(false); + } + + public boolean isEmpty() { + return !getBtnSaveAs().isEnabled(); + } + + public boolean isChanged() { + return isChanged; + } } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractSelector.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractSelector.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractSelector.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractSelector.java index 0a35fd8..a3dbed4 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractSelector.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractSelector.java @@ -1,300 +1,300 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.Icon; -import javax.swing.JList; -import javax.swing.JPanel; - -import java.awt.Component; -import java.awt.Dimension; -import javax.swing.JLabel; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; - -import com.fathzer.soft.ajlib.swing.Utils; -import com.fathzer.soft.ajlib.utilities.NullUtils; -import javax.swing.SwingConstants; -import java.awt.BorderLayout; - - -/** An abstract widget composed of an optional label, a combo box and a new button. - *
It is typically used to select a value in a list of possible values. - *
This widget defines a property that reflects the selection changes. Its name is defined by the method getPropertyName. - *
It also allows the combo box display to be customized using method getDefaultRenderedValue. - * @param The class of the items selected by the selector. - * @param The class of the constructor argument. - * @see #getPropertyName() - * @see #getDefaultRenderedValue(Object) - */ -public abstract class AbstractSelector extends JPanel { - private static final long serialVersionUID = 1L; - private JLabel jLabel; - private ComboBox combo; - private JButton newButton; - - private T lastSelected; - private V parameters; - - /** - * Constructor. - * @param parameters The init parameters of the selector (that object will be used to populate the combo). - */ - protected AbstractSelector(V parameters) { - this.parameters = parameters; - initialize(); - internalPopulate(); - this.lastSelected = get(); - } - - private void internalPopulate() { - getCombo().setActionEnabled(false); - getCombo().removeAllItems(); - populateCombo(); - getCombo().setActionEnabled(true); - } - - protected Icon getNewButtonIcon() { - return null; - } - - /** Gets the parameters of the widget. - * @return The argument passed to the constructor. - * @see #AbstractSelector(Object) - */ - public V getParameters() { - return this.parameters; - } - - /** Sets the parameters of the widget. - *
The refresh method is called. - * @param parameters The new parameters - */ - public void setParameters(V parameters) { - this.parameters = parameters; - refresh(); - } - - /** Populates the combo. - *
You should override this method to define how the combo is populated. - *
Usually, you will use the getParameters method in order to retrieve the argument of the constructor. - *
Note that this method is always called with a empty combo. - * @see #getParameters() - */ - protected abstract void populateCombo(); - - /** Refreshes the widget when the parameters has changed. - *
This method is called when the widget parameters are changed by setParameters. - *
It removes all old combo items, then calls populateCombo and setSelectionAfterRefresh. - *
A property change is thrown if the selection changes (and the combo action is not disabled). - *
The actionEnabled attribute of the combo is unchanged. - * @see ComboBox#setActionEnabled(boolean) - * @see #populateCombo() - * @see #setSelectionAfterRefresh(Object) - */ - public void refresh() { - T old = get(); - boolean oldEnabled = getCombo().isActionEnabled(); - getCombo().setActionEnabled(false); - getCombo().removeAllItems(); - populateCombo(); - getCombo().setActionEnabled(oldEnabled); - setSelectionAfterRefresh(old); - } - - /** Sets the combo selection during a refresh. - *
By default, this method selects the last item selected, if it is still available. - * @param old the last selected item before refresh was performed. - */ - protected void setSelectionAfterRefresh(T old) { - if (getCombo().contains(old)) { - getCombo().setSelectedItem(old); - } - } - - private void initialize() { - String label = getLabel(); - setLayout(new BorderLayout(0, 0)); - add(getJLabel(), BorderLayout.WEST); - add(getCombo()); - - add(getNewButton(), BorderLayout.EAST); - - Dimension dimension = getCombo().getPreferredSize(); - getNewButton().setPreferredSize(new Dimension(dimension.height, dimension.height)); - - if (label!=null) { - getJLabel().setText(getLabel()); - } - if (getComboTip()!=null) { - getCombo().setToolTipText(getComboTip()); - } - if (getNewButtonTip()!=null) { - getNewButton().setToolTipText(getNewButtonTip()); - } - } - - /** Gets the label. - * @return a JLabel - */ - public JLabel getJLabel() { - if (jLabel == null) { - jLabel = new JLabel(); - } - return jLabel; - } - - /** Gets the new button. - * @return a JButton - */ - public JButton getNewButton() { - if (newButton == null) { - newButton = new JButton(); - newButton.setHorizontalAlignment(SwingConstants.LEFT); - Icon icon = getNewButtonIcon(); - newButton = new JButton(icon); - newButton.setFocusable(false); - newButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - T c = createNew(); - if (c != null) { - internalPopulate(); - getCombo().setSelectedItem(c); - Utils.getOwnerWindow(AbstractSelector.this).pack(); - } - } - }); - newButton.setVisible(isNewButtonVisible()); - } - return newButton; - } - - protected boolean isNewButtonVisible() { - return true; - } - - /** Gets the ComboBox. - * @return a CoolJComboBox. - */ - public ComboBox getCombo() { - if (combo == null) { - combo = new ComboBox<>(); - combo.setRenderer(new Renderer()); - combo.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - T old = lastSelected; - lastSelected = get(); - if (!NullUtils.areEquals(old, lastSelected)) { - firePropertyChange(getPropertyName(), old, lastSelected); - } - } - }); - } - return combo; - } - - @SuppressWarnings("serial") - private class Renderer extends DefaultListCellRenderer { - @SuppressWarnings("unchecked") - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - T v = (T)value; - Component renderer = super.getListCellRendererComponent(list, getDefaultRenderedValue(v), index, isSelected, cellHasFocus); - return getCustomizedRenderer (renderer, v, index, isSelected, cellHasFocus); - } - } - - /** Returns a customized renderer used to display a specific value. - *
Please note that this method is never called when combo's renderer is set by getCombo().setRenderer(...) method. - * @param renderer The default renderer - * @param value The value to be rendered - * @param index The index of the value in the combo - * @param isSelected True if the value is selected - * @param cellHasFocus True if the value has the focus - * @return a customized renderer, by default this method returns the renderer argument. - */ - protected Component getCustomizedRenderer (Component renderer, T value, int index, boolean isSelected, boolean cellHasFocus) { - return renderer; - } - - /** Returns the default value displayed in the combo box. - *
Default means that this value is used by the default renderer. If you define your own ListCellRenderer, this method will never been called. - * The default implementation returns the value itself (the default renderer will display the toString of that object). - * You may override this method in order to customized the combo box displayed - * @param value The value to be displayed (it can be null) - * @return The value that will be displayed. - */ - protected Object getDefaultRenderedValue (T value) { - return value; - } - - /** Creates a new element. - * This method is called when the new button is clicked. It should ask for the new instance to be created, then, adds it to the object used - * by method populateCombo, then, returns the created object. - * @return The created object, null if the object creation was cancelled. - */ - protected abstract T createNew(); - - /** Gets the widget's label. - * @return a String, null to have no label. - */ - protected String getLabel() { - return null; - } - - /** Gets the combo's initial tooltip. - * @return a String, null to have no tooltip. - */ - protected String getComboTip() { - return null; - } - - /** Gets the new button's initial tooltip. - * @return a String, null to have no tooltip. - */ - protected String getNewButtonTip() { - return null; - } - - /** Gets the name of the property that changes when the selection changes. - * @return a String - */ - protected abstract String getPropertyName(); - - /** Gets the selected value. - * @return the selected value. - */ - public T get() { - return (T)getCombo().getSelectedItem(); - } - - /** Sets the selected value. - * @param value The value to select - */ - public void set(T value) { - T oldValue = this.get(); - if (!NullUtils.areEquals(value,oldValue)) { - getCombo().setSelectedItem(value); - } - } - - /** Sets the combo tooltip. - *
This method is a shortcut to this.getCombo().setToolTipText(text) - * @param tip The tooltip text. - */ - @Override - public void setToolTipText(String tip) { - getCombo().setToolTipText(tip); - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - getJLabel().setEnabled(enabled); - getNewButton().setEnabled(enabled); - getCombo().setEnabled(enabled); - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.Icon; +import javax.swing.JList; +import javax.swing.JPanel; + +import java.awt.Component; +import java.awt.Dimension; +import javax.swing.JLabel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; + +import com.fathzer.soft.ajlib.swing.Utils; +import com.fathzer.soft.ajlib.utilities.NullUtils; +import javax.swing.SwingConstants; +import java.awt.BorderLayout; + + +/** An abstract widget composed of an optional label, a combo box and a new button. + *
It is typically used to select a value in a list of possible values. + *
This widget defines a property that reflects the selection changes. Its name is defined by the method getPropertyName. + *
It also allows the combo box display to be customized using method getDefaultRenderedValue. + * @param The class of the items selected by the selector. + * @param The class of the constructor argument. + * @see #getPropertyName() + * @see #getDefaultRenderedValue(Object) + */ +public abstract class AbstractSelector extends JPanel { + private static final long serialVersionUID = 1L; + private JLabel jLabel; + private ComboBox combo; + private JButton newButton; + + private T lastSelected; + private V parameters; + + /** + * Constructor. + * @param parameters The init parameters of the selector (that object will be used to populate the combo). + */ + protected AbstractSelector(V parameters) { + this.parameters = parameters; + initialize(); + internalPopulate(); + this.lastSelected = get(); + } + + private void internalPopulate() { + getCombo().setActionEnabled(false); + getCombo().removeAllItems(); + populateCombo(); + getCombo().setActionEnabled(true); + } + + protected Icon getNewButtonIcon() { + return null; + } + + /** Gets the parameters of the widget. + * @return The argument passed to the constructor. + * @see #AbstractSelector(Object) + */ + public V getParameters() { + return this.parameters; + } + + /** Sets the parameters of the widget. + *
The refresh method is called. + * @param parameters The new parameters + */ + public void setParameters(V parameters) { + this.parameters = parameters; + refresh(); + } + + /** Populates the combo. + *
You should override this method to define how the combo is populated. + *
Usually, you will use the getParameters method in order to retrieve the argument of the constructor. + *
Note that this method is always called with a empty combo. + * @see #getParameters() + */ + protected abstract void populateCombo(); + + /** Refreshes the widget when the parameters has changed. + *
This method is called when the widget parameters are changed by setParameters. + *
It removes all old combo items, then calls populateCombo and setSelectionAfterRefresh. + *
A property change is thrown if the selection changes (and the combo action is not disabled). + *
The actionEnabled attribute of the combo is unchanged. + * @see ComboBox#setActionEnabled(boolean) + * @see #populateCombo() + * @see #setSelectionAfterRefresh(Object) + */ + public void refresh() { + T old = get(); + boolean oldEnabled = getCombo().isActionEnabled(); + getCombo().setActionEnabled(false); + getCombo().removeAllItems(); + populateCombo(); + getCombo().setActionEnabled(oldEnabled); + setSelectionAfterRefresh(old); + } + + /** Sets the combo selection during a refresh. + *
By default, this method selects the last item selected, if it is still available. + * @param old the last selected item before refresh was performed. + */ + protected void setSelectionAfterRefresh(T old) { + if (getCombo().contains(old)) { + getCombo().setSelectedItem(old); + } + } + + private void initialize() { + String label = getLabel(); + setLayout(new BorderLayout(0, 0)); + add(getJLabel(), BorderLayout.WEST); + add(getCombo()); + + add(getNewButton(), BorderLayout.EAST); + + Dimension dimension = getCombo().getPreferredSize(); + getNewButton().setPreferredSize(new Dimension(dimension.height, dimension.height)); + + if (label!=null) { + getJLabel().setText(getLabel()); + } + if (getComboTip()!=null) { + getCombo().setToolTipText(getComboTip()); + } + if (getNewButtonTip()!=null) { + getNewButton().setToolTipText(getNewButtonTip()); + } + } + + /** Gets the label. + * @return a JLabel + */ + public JLabel getJLabel() { + if (jLabel == null) { + jLabel = new JLabel(); + } + return jLabel; + } + + /** Gets the new button. + * @return a JButton + */ + public JButton getNewButton() { + if (newButton == null) { + newButton = new JButton(); + newButton.setHorizontalAlignment(SwingConstants.LEFT); + Icon icon = getNewButtonIcon(); + newButton = new JButton(icon); + newButton.setFocusable(false); + newButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + T c = createNew(); + if (c != null) { + internalPopulate(); + getCombo().setSelectedItem(c); + Utils.getOwnerWindow(AbstractSelector.this).pack(); + } + } + }); + newButton.setVisible(isNewButtonVisible()); + } + return newButton; + } + + protected boolean isNewButtonVisible() { + return true; + } + + /** Gets the ComboBox. + * @return a CoolJComboBox. + */ + public ComboBox getCombo() { + if (combo == null) { + combo = new ComboBox<>(); + combo.setRenderer(new Renderer()); + combo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + T old = lastSelected; + lastSelected = get(); + if (!NullUtils.areEquals(old, lastSelected)) { + firePropertyChange(getPropertyName(), old, lastSelected); + } + } + }); + } + return combo; + } + + @SuppressWarnings("serial") + private class Renderer extends DefaultListCellRenderer { + @SuppressWarnings("unchecked") + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + T v = (T)value; + Component renderer = super.getListCellRendererComponent(list, getDefaultRenderedValue(v), index, isSelected, cellHasFocus); + return getCustomizedRenderer (renderer, v, index, isSelected, cellHasFocus); + } + } + + /** Returns a customized renderer used to display a specific value. + *
Please note that this method is never called when combo's renderer is set by getCombo().setRenderer(...) method. + * @param renderer The default renderer + * @param value The value to be rendered + * @param index The index of the value in the combo + * @param isSelected True if the value is selected + * @param cellHasFocus True if the value has the focus + * @return a customized renderer, by default this method returns the renderer argument. + */ + protected Component getCustomizedRenderer (Component renderer, T value, int index, boolean isSelected, boolean cellHasFocus) { + return renderer; + } + + /** Returns the default value displayed in the combo box. + *
Default means that this value is used by the default renderer. If you define your own ListCellRenderer, this method will never been called. + * The default implementation returns the value itself (the default renderer will display the toString of that object). + * You may override this method in order to customized the combo box displayed + * @param value The value to be displayed (it can be null) + * @return The value that will be displayed. + */ + protected Object getDefaultRenderedValue (T value) { + return value; + } + + /** Creates a new element. + * This method is called when the new button is clicked. It should ask for the new instance to be created, then, adds it to the object used + * by method populateCombo, then, returns the created object. + * @return The created object, null if the object creation was cancelled. + */ + protected abstract T createNew(); + + /** Gets the widget's label. + * @return a String, null to have no label. + */ + protected String getLabel() { + return null; + } + + /** Gets the combo's initial tooltip. + * @return a String, null to have no tooltip. + */ + protected String getComboTip() { + return null; + } + + /** Gets the new button's initial tooltip. + * @return a String, null to have no tooltip. + */ + protected String getNewButtonTip() { + return null; + } + + /** Gets the name of the property that changes when the selection changes. + * @return a String + */ + protected abstract String getPropertyName(); + + /** Gets the selected value. + * @return the selected value. + */ + public T get() { + return (T)getCombo().getSelectedItem(); + } + + /** Sets the selected value. + * @param value The value to select + */ + public void set(T value) { + T oldValue = this.get(); + if (!NullUtils.areEquals(value,oldValue)) { + getCombo().setSelectedItem(value); + } + } + + /** Sets the combo tooltip. + *
This method is a shortcut to this.getCombo().setToolTipText(text) + * @param tip The tooltip text. + */ + @Override + public void setToolTipText(String tip) { + getCombo().setToolTipText(tip); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + getJLabel().setEnabled(enabled); + getNewButton().setEnabled(enabled); + getCombo().setEnabled(enabled); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractTitledPanel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractTitledPanel.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractTitledPanel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractTitledPanel.java index 4bab874..53df241 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractTitledPanel.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/AbstractTitledPanel.java @@ -1,123 +1,123 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import java.awt.GridBagLayout; - -import javax.swing.Icon; -import javax.swing.JComponent; -import javax.swing.JPanel; - -import java.awt.BorderLayout; -import javax.swing.JLabel; - -import java.awt.GridBagConstraints; -import java.awt.Insets; - -/** A very basic panel, composed of a header with an icon at its north and a component in the center. - * @param The class of the data used to fill the center panel. - */ -public abstract class AbstractTitledPanel extends JPanel { - private static final long serialVersionUID = 1L; - private JPanel northPanel = null; - private JLabel iconLabel = null; - private JLabel textLabel = null; - - protected T data; - - /** - * Constructor. - * @param headerMessage The header message - * @param headerIcon The header icon (null for no icon) - * @param data The center component's data - */ - protected AbstractTitledPanel(String headerMessage, Icon headerIcon, T data) { - super(); - this.data = data; - initialize(); - this.setHeaderMessage(headerMessage); - this.setHeaderIcon(headerIcon); - } - - /** - * This method initializes this - */ - private void initialize() { - this.setLayout(new BorderLayout()); - this.add(getNorthPanel(), BorderLayout.NORTH); - this.add(getCenterComponent(), BorderLayout.CENTER); - } - - /** - * This method initializes northPanel - * - * @return javax.swing.JPanel - */ - private JPanel getNorthPanel() { - if (northPanel == null) { - GridBagConstraints gridBagConstraints1 = new GridBagConstraints(); - gridBagConstraints1.gridx = 1; - gridBagConstraints1.weightx = 1.0D; - gridBagConstraints1.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints1.insets = new Insets(5, 5, 5, 5); - gridBagConstraints1.gridy = 0; - textLabel = new JLabel(); - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); - gridBagConstraints.gridy = 0; - northPanel = new JPanel(); - northPanel.setLayout(new GridBagLayout()); - northPanel.add(getIconLabel(), gridBagConstraints); - northPanel.add(textLabel, gridBagConstraints1); - } - return northPanel; - } - - /** Sets the header message. - * @param message The message - */ - public void setHeaderMessage(String message) { - this.textLabel.setText(message); - } - - /** Gets the header message. - * @return a String - */ - public String getHeaderMessage() { - return this.textLabel.getText(); - } - - /** - * This method initializes iconLabel - * - * @return javax.swing.JLabel - */ - private JLabel getIconLabel() { - if (iconLabel == null) { - iconLabel = new JLabel(); - } - return iconLabel; - } - - /** Gets the icon. - * @return a Icon - */ - public Icon getHeaderIcon() { - return this.getIconLabel().getIcon(); - } - - /** Sets the icon. - *
By default, the icon is null (no icon). You may want to set it to some of the standard dialog icons (example UIManager.getIcon("OptionPane.informationIcon")) - * @param icon null to remove the icon - */ - public void setHeaderIcon(Icon icon) { - this.getIconLabel().setIcon(icon); - } - - /** - * Gets the center Component. - *
Please note that this method should always return the same JComponent instance. - * @return a JComponent - */ - public abstract JComponent getCenterComponent(); -} // @jve:decl-index=0:visual-constraint="10,10" +package com.fathzer.soft.ajlib.swing.widget; + +import java.awt.GridBagLayout; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import java.awt.BorderLayout; +import javax.swing.JLabel; + +import java.awt.GridBagConstraints; +import java.awt.Insets; + +/** A very basic panel, composed of a header with an icon at its north and a component in the center. + * @param The class of the data used to fill the center panel. + */ +public abstract class AbstractTitledPanel extends JPanel { + private static final long serialVersionUID = 1L; + private JPanel northPanel = null; + private JLabel iconLabel = null; + private JLabel textLabel = null; + + protected T data; + + /** + * Constructor. + * @param headerMessage The header message + * @param headerIcon The header icon (null for no icon) + * @param data The center component's data + */ + protected AbstractTitledPanel(String headerMessage, Icon headerIcon, T data) { + super(); + this.data = data; + initialize(); + this.setHeaderMessage(headerMessage); + this.setHeaderIcon(headerIcon); + } + + /** + * This method initializes this + */ + private void initialize() { + this.setLayout(new BorderLayout()); + this.add(getNorthPanel(), BorderLayout.NORTH); + this.add(getCenterComponent(), BorderLayout.CENTER); + } + + /** + * This method initializes northPanel + * + * @return javax.swing.JPanel + */ + private JPanel getNorthPanel() { + if (northPanel == null) { + GridBagConstraints gridBagConstraints1 = new GridBagConstraints(); + gridBagConstraints1.gridx = 1; + gridBagConstraints1.weightx = 1.0D; + gridBagConstraints1.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints1.insets = new Insets(5, 5, 5, 5); + gridBagConstraints1.gridy = 0; + textLabel = new JLabel(); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(5, 5, 5, 5); + gridBagConstraints.gridy = 0; + northPanel = new JPanel(); + northPanel.setLayout(new GridBagLayout()); + northPanel.add(getIconLabel(), gridBagConstraints); + northPanel.add(textLabel, gridBagConstraints1); + } + return northPanel; + } + + /** Sets the header message. + * @param message The message + */ + public void setHeaderMessage(String message) { + this.textLabel.setText(message); + } + + /** Gets the header message. + * @return a String + */ + public String getHeaderMessage() { + return this.textLabel.getText(); + } + + /** + * This method initializes iconLabel + * + * @return javax.swing.JLabel + */ + private JLabel getIconLabel() { + if (iconLabel == null) { + iconLabel = new JLabel(); + } + return iconLabel; + } + + /** Gets the icon. + * @return a Icon + */ + public Icon getHeaderIcon() { + return this.getIconLabel().getIcon(); + } + + /** Sets the icon. + *
By default, the icon is null (no icon). You may want to set it to some of the standard dialog icons (example UIManager.getIcon("OptionPane.informationIcon")) + * @param icon null to remove the icon + */ + public void setHeaderIcon(Icon icon) { + this.getIconLabel().setIcon(icon); + } + + /** + * Gets the center Component. + *
Please note that this method should always return the same JComponent instance. + * @return a JComponent + */ + public abstract JComponent getCenterComponent(); +} // @jve:decl-index=0:visual-constraint="10,10" diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/CharWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/CharWidget.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/CharWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/CharWidget.java index 587b3ba..1610138 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/CharWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/CharWidget.java @@ -1,84 +1,84 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -import com.fathzer.soft.ajlib.utilities.NullUtils; - - -/** A widget that allows the user to enter only one character. - */ -@SuppressWarnings("serial") -public class CharWidget extends TextWidget { - /** The character property name. - *
This property is the character contained in the widget. - */ - public static final String CHAR_PROPERTY = "char"; - private Character content; - private Character defaultChar; - - /** Constructor. - *
The field is empty and the default character is null. - * @see CharWidget#setDefaultChar(char) - */ - public CharWidget() { - super(1); - // We will not use a document listener to listen the field modifications because we want to change the field (truncate it to its first character) - // and document listener can't modify the source event (it throws an IllegalStateException). - addPropertyChangeListener(TextWidget.TEXT_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String text = getText(); - if (text.length()==0) { - setChar(defaultChar); - } else if (text.length()>1){ - setText(new String(new char[]{text.charAt(text.length()-1)})); - } else { - setChar(text.charAt(0)); - } - selectAll(); - } - }); - addFocusListener(new FocusListener() { - @Override - public void focusLost(FocusEvent e) { - // Nothing to do - } - - @Override - public void focusGained(FocusEvent e) { - selectAll(); - } - }); - } - - /** Sets the widget's character. - * @param character The character to put in the widget or null to make the field empty (or set it to its default value). - * @see CharWidget#setDefaultChar(char) - */ - public void setChar(Character character) { - if (!NullUtils.areEquals(character,content)) { - Character old = this.content; - this.content = character; - setText(content==null?"":new String(new char[]{this.content})); - this.firePropertyChange(CHAR_PROPERTY, old, content); - } - } - - /** Gets the widget's character. - * @return a character (null if the field is empty) - */ - public Character getChar() { - return this.content; - } - - /** Sets the default character. - *
When the user delete the character in the field. The field content is automatically set to the default character. - * @param defaultChar The new default character (null to leave the field empty). - */ - public void setDefaultChar(char defaultChar) { - this.defaultChar = defaultChar; - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import com.fathzer.soft.ajlib.utilities.NullUtils; + + +/** A widget that allows the user to enter only one character. + */ +@SuppressWarnings("serial") +public class CharWidget extends TextWidget { + /** The character property name. + *
This property is the character contained in the widget. + */ + public static final String CHAR_PROPERTY = "char"; + private Character content; + private Character defaultChar; + + /** Constructor. + *
The field is empty and the default character is null. + * @see CharWidget#setDefaultChar(char) + */ + public CharWidget() { + super(1); + // We will not use a document listener to listen the field modifications because we want to change the field (truncate it to its first character) + // and document listener can't modify the source event (it throws an IllegalStateException). + addPropertyChangeListener(TextWidget.TEXT_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String text = getText(); + if (text.length()==0) { + setChar(defaultChar); + } else if (text.length()>1){ + setText(new String(new char[]{text.charAt(text.length()-1)})); + } else { + setChar(text.charAt(0)); + } + selectAll(); + } + }); + addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + // Nothing to do + } + + @Override + public void focusGained(FocusEvent e) { + selectAll(); + } + }); + } + + /** Sets the widget's character. + * @param character The character to put in the widget or null to make the field empty (or set it to its default value). + * @see CharWidget#setDefaultChar(char) + */ + public void setChar(Character character) { + if (!NullUtils.areEquals(character,content)) { + Character old = this.content; + this.content = character; + setText(content==null?"":new String(new char[]{this.content})); + this.firePropertyChange(CHAR_PROPERTY, old, content); + } + } + + /** Gets the widget's character. + * @return a character (null if the field is empty) + */ + public Character getChar() { + return this.content; + } + + /** Sets the default character. + *
When the user delete the character in the field. The field content is automatically set to the default character. + * @param defaultChar The new default character (null to leave the field empty). + */ + public void setDefaultChar(char defaultChar) { + this.defaultChar = defaultChar; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/ComboBox.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/ComboBox.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/ComboBox.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/ComboBox.java index a415490..b6303da 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/ComboBox.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/ComboBox.java @@ -1,50 +1,50 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import javax.swing.JComboBox; - -/** This subclass of JCombo is cool because it adds some useful functionalities, often asked in the forums. - *
  • you can prevent instance from sending action event.
    - * It could be very useful when you have to refresh the menu but don't want any event being sent.
  • - *
  • The method contains tests whether the menu contains an item or not
  • - *
- */ -public class ComboBox extends JComboBox { - private static final long serialVersionUID = 1L; - - private boolean isActionEnabled = true; - - /** Enables/Disables the events. - *
Note: This method doesn't throw any event. - * @param isActionEnabled true to enable the events. - */ - public void setActionEnabled(boolean isActionEnabled) { - this.isActionEnabled = isActionEnabled; - } - - /** Tests whether the events are enabled or not. - * @return true if events are enabled. - */ - public boolean isActionEnabled() { - return isActionEnabled; - } - - /** Tests whether the menu contains an item or not - * @param item the item to test - * @return true if the item is in the menu. - */ - public boolean contains (T item) { - for (int i = 0; i < this.getItemCount(); i++) { - if (this.getItemAt(i).equals(item)) { - return true; - } - } - return false; - } - - @Override - protected void fireActionEvent() { - if (isActionEnabled) { - super.fireActionEvent(); - } - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import javax.swing.JComboBox; + +/** This subclass of JCombo is cool because it adds some useful functionalities, often asked in the forums. + *
  • you can prevent instance from sending action event.
    + * It could be very useful when you have to refresh the menu but don't want any event being sent.
  • + *
  • The method contains tests whether the menu contains an item or not
  • + *
+ */ +public class ComboBox extends JComboBox { + private static final long serialVersionUID = 1L; + + private boolean isActionEnabled = true; + + /** Enables/Disables the events. + *
Note: This method doesn't throw any event. + * @param isActionEnabled true to enable the events. + */ + public void setActionEnabled(boolean isActionEnabled) { + this.isActionEnabled = isActionEnabled; + } + + /** Tests whether the events are enabled or not. + * @return true if events are enabled. + */ + public boolean isActionEnabled() { + return isActionEnabled; + } + + /** Tests whether the menu contains an item or not + * @param item the item to test + * @return true if the item is in the menu. + */ + public boolean contains (T item) { + for (int i = 0; i < this.getItemCount(); i++) { + if (this.getItemAt(i).equals(item)) { + return true; + } + } + return false; + } + + @Override + protected void fireActionEvent() { + if (isActionEnabled) { + super.fireActionEvent(); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/CurrencyWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/CurrencyWidget.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/CurrencyWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/CurrencyWidget.java index 7bf4041..2fb16c4 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/CurrencyWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/CurrencyWidget.java @@ -1,76 +1,76 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Currency; -import java.util.Locale; - -/** A widget to enter a monetary value. - *
This widget automatically format the value it contains according to its local's currency. - *
You can restrict the valid values by setting minimum and maximum values. - *
This bean defines two properties:
    - *
  • VALUE_PROPERTY: a double, the value currently entered in the field.
  • - *
  • CONTENT_VALID_PROPERTY: a boolean, true if the text currently entered in the field is a valid value, false if it is not. - *
    The CONTENT_VALID_PROPERTY is a read only property.
  • - *
- */ -public class CurrencyWidget extends NumberWidget { - private static final long serialVersionUID = 1L; - - private DecimalFormat numberFormat; - - /** Constructor. - * The local is set to default locale. - * @see #CurrencyWidget(Locale) - */ - public CurrencyWidget() { - this(Locale.getDefault()); - } - - /** Constructor. - * Empty field is not allowed. - * minValue is equals to Double.NEGATIVE_INFINITY, maxValue to Double.POSITIVE_INFINITY. - * The value is set to null. - * @param locale The locale to apply to the widget (use to format the amount typed). - */ - public CurrencyWidget(Locale locale) { - super(locale); - } - - @Override - protected DecimalFormat buildFormat(Locale locale) { - this.numberFormat = super.buildFormat(locale); - return patchJavaBug4510618((DecimalFormat) NumberFormat.getCurrencyInstance(locale)); - } - - /** Gets the widget's currency. - * @return the windget's currency - */ - public Currency getCurrency() { - return getFormat().getCurrency(); - } - - /** Sets the currency. - *
By default, the currency is the one of the widget's locale. - * @param currency The new currency. - */ - public void setCurrency (Currency currency) { - getFormat().setCurrency(currency); - int digits = currency.getDefaultFractionDigits(); - getFormat().setMaximumFractionDigits(digits); - getFormat().setMinimumFractionDigits(digits); - refreshText(); - } - - /* (non-Javadoc) - * @see NumberWidget#parseValue(java.lang.String) - */ - @Override - protected Number parseValue(String text) { - Number result = super.parseValue(text); - if (result==null) { - result = safeParse(numberFormat, text); - } - return result; - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Currency; +import java.util.Locale; + +/** A widget to enter a monetary value. + *
This widget automatically format the value it contains according to its local's currency. + *
You can restrict the valid values by setting minimum and maximum values. + *
This bean defines two properties:
    + *
  • VALUE_PROPERTY: a double, the value currently entered in the field.
  • + *
  • CONTENT_VALID_PROPERTY: a boolean, true if the text currently entered in the field is a valid value, false if it is not. + *
    The CONTENT_VALID_PROPERTY is a read only property.
  • + *
+ */ +public class CurrencyWidget extends NumberWidget { + private static final long serialVersionUID = 1L; + + private DecimalFormat numberFormat; + + /** Constructor. + * The local is set to default locale. + * @see #CurrencyWidget(Locale) + */ + public CurrencyWidget() { + this(Locale.getDefault()); + } + + /** Constructor. + * Empty field is not allowed. + * minValue is equals to Double.NEGATIVE_INFINITY, maxValue to Double.POSITIVE_INFINITY. + * The value is set to null. + * @param locale The locale to apply to the widget (use to format the amount typed). + */ + public CurrencyWidget(Locale locale) { + super(locale); + } + + @Override + protected DecimalFormat buildFormat(Locale locale) { + this.numberFormat = super.buildFormat(locale); + return patchJavaBug4510618((DecimalFormat) NumberFormat.getCurrencyInstance(locale)); + } + + /** Gets the widget's currency. + * @return the windget's currency + */ + public Currency getCurrency() { + return getFormat().getCurrency(); + } + + /** Sets the currency. + *
By default, the currency is the one of the widget's locale. + * @param currency The new currency. + */ + public void setCurrency (Currency currency) { + getFormat().setCurrency(currency); + int digits = currency.getDefaultFractionDigits(); + getFormat().setMaximumFractionDigits(digits); + getFormat().setMinimumFractionDigits(digits); + refreshText(); + } + + /* (non-Javadoc) + * @see NumberWidget#parseValue(java.lang.String) + */ + @Override + protected Number parseValue(String text) { + Number result = super.parseValue(text); + if (result==null) { + result = safeParse(numberFormat, text); + } + return result; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/ExcelPane.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/ExcelPane.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/ExcelPane.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/ExcelPane.java index 4186504..ce83a8e 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/ExcelPane.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/ExcelPane.java @@ -1,187 +1,187 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import javax.swing.JComponent; -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; -import javax.swing.JPanel; - -import java.awt.GridBagLayout; -import java.awt.GridBagConstraints; - -import javax.swing.JButton; -import javax.swing.filechooser.FileNameExtensionFilter; - -import com.fathzer.jlocal.Formatter; -import com.fathzer.soft.ajlib.swing.dialog.FileChooser; -import com.fathzer.soft.ajlib.swing.framework.Application; -import com.fathzer.soft.ajlib.swing.table.CSVExporter; -import com.fathzer.soft.ajlib.swing.table.Table; - -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -/** A widget with a JTable and a button that is able to save it in csv format. - */ -public class ExcelPane extends JPanel { - private static final long serialVersionUID = 1L; - private static final String CSV_EXTENSION = ".csv"; - - private Table table; - private JButton saveButton; - - private CSVExporter exporter; - - /** - * Creates the panel. - */ - public ExcelPane() { - initialize(); - } - - /** Initializes the panel. - *
This method is called once by the constructor. - *
This implementation sets the layout to a new GridBagLayout and adds the table and the button to it. - *
You can override this method in order to create a custom panel (for example, if you want to change the button position). - */ - protected void initialize() { - GridBagLayout gridBagLayout = new GridBagLayout(); - setLayout(gridBagLayout); - GridBagConstraints gbcScrollPane = new GridBagConstraints(); - gbcScrollPane.gridwidth = 0; - gbcScrollPane.weighty = 1.0; - gbcScrollPane.weightx = 1.0; - gbcScrollPane.fill = GridBagConstraints.BOTH; - gbcScrollPane.gridx = 0; - gbcScrollPane.gridy = 0; - add(getTable(), gbcScrollPane); - GridBagConstraints gbcSaveButton = new GridBagConstraints(); - gbcSaveButton.insets = new Insets(5, 0, 0, 0); - gbcSaveButton.anchor = GridBagConstraints.EAST; - gbcSaveButton.gridx = 1; - gbcSaveButton.gridy = 1; - add(getSaveButton(), gbcSaveButton); - JComponent extra = getExtraComponent(); - if (extra!=null) { - GridBagConstraints gbcExtra = new GridBagConstraints(); - gbcExtra.insets = new Insets(5, 0, 0, 0); - gbcExtra.anchor = GridBagConstraints.EAST; - gbcExtra.gridx = 0; - gbcExtra.gridy = 1; - gbcExtra.weightx = 1.0; - gbcExtra.fill = GridBagConstraints.HORIZONTAL; - add(extra, gbcExtra); - } - } - - /** Gets an extra component displayed at left below the table. - *
By default, this method returns null. - * @return A JComponent or null if no component is provided. - */ - protected JComponent getExtraComponent() { - return null; - } - - /** Gets the table. - * @return a Table - */ - public final Table getTable() { - if (table == null) { - table = buildTable(); - } - return table; - } - - /** Gets the save button. - * @return a button - */ - public final JButton getSaveButton() { - if (saveButton == null) { - saveButton = new JButton(Application.getString("ExcelPane.save", getLocale())); //$NON-NLS-1$ - saveButton.addActionListener(new SaveButtonActionListener()); - } - return saveButton; - } - - private final class SaveButtonActionListener implements ActionListener { - public void actionPerformed(ActionEvent e) { - JFileChooser chooser = new FileChooser(getInitialPath()) { - private static final long serialVersionUID = 1L; - @Override - public File getSelectedFile() { - File f = super.getSelectedFile(); - if ((f!=null) && !f.getName().endsWith(CSV_EXTENSION)) { - f = new File(f.getParent(), f.getName()+CSV_EXTENSION); - } - return f; - } - }; - chooser.setFileFilter(new FileNameExtensionFilter(Application.getString("ExcelPane.csv.wording", getLocale()),CSV_EXTENSION.substring(1))); - File file = chooser.showSaveDialog(ExcelPane.this)==JFileChooser.APPROVE_OPTION?chooser.getSelectedFile():null; - if (file!=null) { - try { - save(file); - } catch(IOException ex) { - String message = Formatter.format(Application.getString("ExcelPane.error.message", getLocale()), ex.toString()); //$NON-NLS-1$ - JOptionPane.showMessageDialog(ExcelPane.this, message, Application.getString("Generic.error", getLocale()), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ - } - } - } - } - - /** Gets the initial path of the file chooser. - *
This method is called every time the save button is clicked. - *
By default, this path is null. - * @return The path or null that causes the file chooser to point to the user's default directory - */ - protected String getInitialPath() { - return null; - } - - /** Saves the pane content to a file. - *
This method is called every time the save button is clicked and a file is selected. - *
By default, the content is save to the file using the csv exporter. - * @param file The file selected by the user to save the data. - * @see #getCSVExporter() - * @throws IOException If something goes wrong while writing file. - */ - protected void save(File file) throws IOException { - try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { - getCSVExporter().export(writer, getTable().getModel(), true); - writer.flush(); - } - } - - /** Gets the CSVExporter used to export the data. - * @return a CSVExporter - */ - public final CSVExporter getCSVExporter() { - if (this.exporter == null) { - exporter = buildExporter(); - } - return this.exporter; - } - - /** Builds the table. - *
This method is called once. - *
You can override this method in order to create a customized Table. - * @return a Table - */ - protected Table buildTable() { - return new Table(); - } - - /** Builds the table. - *
This method is called once. - *
The default implementation returns a default CSVExporter; - * @return a CSVExporter - * @see CSVExporter - */ - protected CSVExporter buildExporter() { - return new CSVExporter(); - } +package com.fathzer.soft.ajlib.swing.widget; + +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; + +import javax.swing.JButton; +import javax.swing.filechooser.FileNameExtensionFilter; + +import com.fathzer.jlocal.Formatter; +import com.fathzer.soft.ajlib.swing.dialog.FileChooser; +import com.fathzer.soft.ajlib.swing.framework.Application; +import com.fathzer.soft.ajlib.swing.table.CSVExporter; +import com.fathzer.soft.ajlib.swing.table.Table; + +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +/** A widget with a JTable and a button that is able to save it in csv format. + */ +public class ExcelPane extends JPanel { + private static final long serialVersionUID = 1L; + private static final String CSV_EXTENSION = ".csv"; + + private Table table; + private JButton saveButton; + + private CSVExporter exporter; + + /** + * Creates the panel. + */ + public ExcelPane() { + initialize(); + } + + /** Initializes the panel. + *
This method is called once by the constructor. + *
This implementation sets the layout to a new GridBagLayout and adds the table and the button to it. + *
You can override this method in order to create a custom panel (for example, if you want to change the button position). + */ + protected void initialize() { + GridBagLayout gridBagLayout = new GridBagLayout(); + setLayout(gridBagLayout); + GridBagConstraints gbcScrollPane = new GridBagConstraints(); + gbcScrollPane.gridwidth = 0; + gbcScrollPane.weighty = 1.0; + gbcScrollPane.weightx = 1.0; + gbcScrollPane.fill = GridBagConstraints.BOTH; + gbcScrollPane.gridx = 0; + gbcScrollPane.gridy = 0; + add(getTable(), gbcScrollPane); + GridBagConstraints gbcSaveButton = new GridBagConstraints(); + gbcSaveButton.insets = new Insets(5, 0, 0, 0); + gbcSaveButton.anchor = GridBagConstraints.EAST; + gbcSaveButton.gridx = 1; + gbcSaveButton.gridy = 1; + add(getSaveButton(), gbcSaveButton); + JComponent extra = getExtraComponent(); + if (extra!=null) { + GridBagConstraints gbcExtra = new GridBagConstraints(); + gbcExtra.insets = new Insets(5, 0, 0, 0); + gbcExtra.anchor = GridBagConstraints.EAST; + gbcExtra.gridx = 0; + gbcExtra.gridy = 1; + gbcExtra.weightx = 1.0; + gbcExtra.fill = GridBagConstraints.HORIZONTAL; + add(extra, gbcExtra); + } + } + + /** Gets an extra component displayed at left below the table. + *
By default, this method returns null. + * @return A JComponent or null if no component is provided. + */ + protected JComponent getExtraComponent() { + return null; + } + + /** Gets the table. + * @return a Table + */ + public final Table getTable() { + if (table == null) { + table = buildTable(); + } + return table; + } + + /** Gets the save button. + * @return a button + */ + public final JButton getSaveButton() { + if (saveButton == null) { + saveButton = new JButton(Application.getString("ExcelPane.save", getLocale())); //$NON-NLS-1$ + saveButton.addActionListener(new SaveButtonActionListener()); + } + return saveButton; + } + + private final class SaveButtonActionListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new FileChooser(getInitialPath()) { + private static final long serialVersionUID = 1L; + @Override + public File getSelectedFile() { + File f = super.getSelectedFile(); + if ((f!=null) && !f.getName().endsWith(CSV_EXTENSION)) { + f = new File(f.getParent(), f.getName()+CSV_EXTENSION); + } + return f; + } + }; + chooser.setFileFilter(new FileNameExtensionFilter(Application.getString("ExcelPane.csv.wording", getLocale()),CSV_EXTENSION.substring(1))); + File file = chooser.showSaveDialog(ExcelPane.this)==JFileChooser.APPROVE_OPTION?chooser.getSelectedFile():null; + if (file!=null) { + try { + save(file); + } catch(IOException ex) { + String message = Formatter.format(Application.getString("ExcelPane.error.message", getLocale()), ex.toString()); //$NON-NLS-1$ + JOptionPane.showMessageDialog(ExcelPane.this, message, Application.getString("Generic.error", getLocale()), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ + } + } + } + } + + /** Gets the initial path of the file chooser. + *
This method is called every time the save button is clicked. + *
By default, this path is null. + * @return The path or null that causes the file chooser to point to the user's default directory + */ + protected String getInitialPath() { + return null; + } + + /** Saves the pane content to a file. + *
This method is called every time the save button is clicked and a file is selected. + *
By default, the content is save to the file using the csv exporter. + * @param file The file selected by the user to save the data. + * @see #getCSVExporter() + * @throws IOException If something goes wrong while writing file. + */ + protected void save(File file) throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + getCSVExporter().export(writer, getTable().getModel(), true); + writer.flush(); + } + } + + /** Gets the CSVExporter used to export the data. + * @return a CSVExporter + */ + public final CSVExporter getCSVExporter() { + if (this.exporter == null) { + exporter = buildExporter(); + } + return this.exporter; + } + + /** Builds the table. + *
This method is called once. + *
You can override this method in order to create a customized Table. + * @return a Table + */ + protected Table buildTable() { + return new Table(); + } + + /** Builds the table. + *
This method is called once. + *
The default implementation returns a default CSVExporter; + * @return a CSVExporter + * @see CSVExporter + */ + protected CSVExporter buildExporter() { + return new CSVExporter(); + } } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/FileSelectionPane.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/FileSelectionPane.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/FileSelectionPane.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/FileSelectionPane.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/HTMLPane.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/HTMLPane.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/HTMLPane.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/HTMLPane.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/IntegerWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/IntegerWidget.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/IntegerWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/IntegerWidget.java index 032eef9..b984eb7 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/IntegerWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/IntegerWidget.java @@ -1,185 +1,185 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.math.BigInteger; - -import com.fathzer.soft.ajlib.utilities.NullUtils; - - -/** This widget is an integer input field. - *
You can set minimum and maximum values accepted by this field. - *
The up and down arrows are shortcuts to increase/decrease the value in the field. - *
It is a java bean, so you can listen to its VALUE_PROPERTY change. - */ -public class IntegerWidget extends TextWidget { - private static final long serialVersionUID = 1L; - /** The field value property name. */ - public static final String VALUE_PROPERTY = "VALUE_PROPERTY"; - /** An utility constant for Integer.MAX_VALUE. - * @see BigInteger#ZERO - * @see BigInteger#valueOf(long) - */ - public static final BigInteger INTEGER_MAX_VALUE = BigInteger.valueOf(Integer.MAX_VALUE); - - private BigInteger value; - private BigInteger maxValue; - private BigInteger minValue; - - /** Constructor. - * The min and max values are not set - */ - public IntegerWidget() { - this(null, null); - } - - /** Constructor. - * The field is initialized empty, with a null value. - * @param minValue The field minimum value or null if the field has no minimal value. - * @param maxValue The field maximum value or null if the field has no maximal value. - */ - public IntegerWidget(BigInteger minValue, BigInteger maxValue) { - super(); - if ((minValue!=null)&&(maxValue!=null)&&(minValue.compareTo(maxValue)>0)) { - throw new IllegalArgumentException(); - } - this.minValue = minValue; - this.maxValue = maxValue; - // Adds a key listener to ignored any invalid characters and to increase/decrease value when up/down arrow key are pressed - this.addKeyListener(new KeyAdapter() { - @Override - public void keyTyped(KeyEvent e) { - // No car are allowed before a - sign - if ((getSelectionEnd()=0)) { - e.consume(); - } - char car = e.getKeyChar(); - if (car=='-') { // - char is a valid character only if the field accepts value less than zero and in the first place (if there's no other - after the current selection) - if ((IntegerWidget.this.minValue==null) || (IntegerWidget.this.minValue.compareTo(BigInteger.ZERO)<0)) { - if (IntegerWidget.this.getSelectionStart()!=0) { - e.consume(); // No - after first position - } - } else { - e.consume(); - } - } else if (!Character.isDigit(car)) { - e.consume(); - } - } - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode()==KeyEvent.VK_UP) { - if ((value!=null) && ((IntegerWidget.this.maxValue==null) || (value.compareTo(IntegerWidget.this.maxValue)<0))) { - setValue(value.add(BigInteger.ONE)); - } - } else if (e.getKeyCode()==KeyEvent.VK_DOWN) { - if ((value!=null) && ((IntegerWidget.this.minValue==null) || (value.compareTo(IntegerWidget.this.minValue)>0))) { - setValue(value.subtract(BigInteger.ONE)); - } - } - } - }); - this.addPropertyChangeListener(TEXT_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - updateValue(); - } - }); - } - - private void updateValue() { - BigInteger old = value; - String text = this.getText().trim(); - try { - value = new BigInteger(text); - if (((this.maxValue!=null)&&(value.compareTo(this.maxValue)>0)) || ((this.minValue!=null)&&(value.compareTo(this.minValue)<0))) { - // Value is out of bounds - value = null; - } - } catch (NumberFormatException e) { - value = null; - } - if (!NullUtils.areEquals(value,old)) { - this.firePropertyChange(VALUE_PROPERTY, old, value); - } - } - - @Override - public void setText(String t) { - super.setText(t); - updateValue(); - } - - /** Gets the current value. - * @return an integer or null if the value is not valid. - */ - public BigInteger getValue() { - return this.value==null?null:this.value; - } - - /** Sets the current value. - * @param value a big integer or null to set the field empty. - */ - public void setValue(BigInteger value) { - if (!NullUtils.areEquals(value,this.value)) { - this.setText(value==null?"":value.toString()); - } - } - - /** Sets the current value. - * @param value an integer or null to set the field empty. - */ - public void setValue(Integer value) { - setValue(value==null?null:BigInteger.valueOf(value)); - } - - /** Sets the min value of the widget. - * @param min the min value. - *
If the current value is lower than max, the current value is set to null. - * @throws IllegalArgumentException if min is bigger than widget max value. - * @see #setRange(BigInteger, BigInteger) - */ - public void setMinValue(BigInteger min) { - if (min!=null && maxValue!=null && min.compareTo(maxValue)>0) { - throw new IllegalArgumentException(); - } - minValue = min; - if (value!=null && min!=null && value.compareTo(min)<0) { - setValue((BigInteger)null); - } - } - - /** Sets the max value of the widget. - * @param max the max value. - *
If the current value is bigger than max, the current value is set to null. - * @throws IllegalArgumentException if max is lower than widget min value. - * @see #setRange(BigInteger, BigInteger) - */ - public void setMaxValue(BigInteger max) { - if (max!=null && minValue!=null && max.compareTo(minValue)<0) { - throw new IllegalArgumentException(); - } - maxValue = max; - if (value!=null && max!= null && value.compareTo(max)>0) { - setValue((BigInteger)null); - } - } - - /** Sets this widget min and max values. - *
If the current value is out of the new range, the current value is set to null. - * @param min The min value - * @param max The max value - */ - public void setRange(BigInteger min, BigInteger max) { - if ((min!=null)&&(max!=null)&&(min.compareTo(max)>0)) { - throw new IllegalArgumentException(); - } - minValue = min; - maxValue = max; - if (value!=null && ((min!=null && value.compareTo(min)<0) || (max!= null && value.compareTo(max)>0))) { - setValue((BigInteger)null); - } - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.math.BigInteger; + +import com.fathzer.soft.ajlib.utilities.NullUtils; + + +/** This widget is an integer input field. + *
You can set minimum and maximum values accepted by this field. + *
The up and down arrows are shortcuts to increase/decrease the value in the field. + *
It is a java bean, so you can listen to its VALUE_PROPERTY change. + */ +public class IntegerWidget extends TextWidget { + private static final long serialVersionUID = 1L; + /** The field value property name. */ + public static final String VALUE_PROPERTY = "VALUE_PROPERTY"; + /** An utility constant for Integer.MAX_VALUE. + * @see BigInteger#ZERO + * @see BigInteger#valueOf(long) + */ + public static final BigInteger INTEGER_MAX_VALUE = BigInteger.valueOf(Integer.MAX_VALUE); + + private BigInteger value; + private BigInteger maxValue; + private BigInteger minValue; + + /** Constructor. + * The min and max values are not set + */ + public IntegerWidget() { + this(null, null); + } + + /** Constructor. + * The field is initialized empty, with a null value. + * @param minValue The field minimum value or null if the field has no minimal value. + * @param maxValue The field maximum value or null if the field has no maximal value. + */ + public IntegerWidget(BigInteger minValue, BigInteger maxValue) { + super(); + if ((minValue!=null)&&(maxValue!=null)&&(minValue.compareTo(maxValue)>0)) { + throw new IllegalArgumentException(); + } + this.minValue = minValue; + this.maxValue = maxValue; + // Adds a key listener to ignored any invalid characters and to increase/decrease value when up/down arrow key are pressed + this.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + // No car are allowed before a - sign + if ((getSelectionEnd()=0)) { + e.consume(); + } + char car = e.getKeyChar(); + if (car=='-') { // - char is a valid character only if the field accepts value less than zero and in the first place (if there's no other - after the current selection) + if ((IntegerWidget.this.minValue==null) || (IntegerWidget.this.minValue.compareTo(BigInteger.ZERO)<0)) { + if (IntegerWidget.this.getSelectionStart()!=0) { + e.consume(); // No - after first position + } + } else { + e.consume(); + } + } else if (!Character.isDigit(car)) { + e.consume(); + } + } + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode()==KeyEvent.VK_UP) { + if ((value!=null) && ((IntegerWidget.this.maxValue==null) || (value.compareTo(IntegerWidget.this.maxValue)<0))) { + setValue(value.add(BigInteger.ONE)); + } + } else if (e.getKeyCode()==KeyEvent.VK_DOWN) { + if ((value!=null) && ((IntegerWidget.this.minValue==null) || (value.compareTo(IntegerWidget.this.minValue)>0))) { + setValue(value.subtract(BigInteger.ONE)); + } + } + } + }); + this.addPropertyChangeListener(TEXT_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + updateValue(); + } + }); + } + + private void updateValue() { + BigInteger old = value; + String text = this.getText().trim(); + try { + value = new BigInteger(text); + if (((this.maxValue!=null)&&(value.compareTo(this.maxValue)>0)) || ((this.minValue!=null)&&(value.compareTo(this.minValue)<0))) { + // Value is out of bounds + value = null; + } + } catch (NumberFormatException e) { + value = null; + } + if (!NullUtils.areEquals(value,old)) { + this.firePropertyChange(VALUE_PROPERTY, old, value); + } + } + + @Override + public void setText(String t) { + super.setText(t); + updateValue(); + } + + /** Gets the current value. + * @return an integer or null if the value is not valid. + */ + public BigInteger getValue() { + return this.value==null?null:this.value; + } + + /** Sets the current value. + * @param value a big integer or null to set the field empty. + */ + public void setValue(BigInteger value) { + if (!NullUtils.areEquals(value,this.value)) { + this.setText(value==null?"":value.toString()); + } + } + + /** Sets the current value. + * @param value an integer or null to set the field empty. + */ + public void setValue(Integer value) { + setValue(value==null?null:BigInteger.valueOf(value)); + } + + /** Sets the min value of the widget. + * @param min the min value. + *
If the current value is lower than max, the current value is set to null. + * @throws IllegalArgumentException if min is bigger than widget max value. + * @see #setRange(BigInteger, BigInteger) + */ + public void setMinValue(BigInteger min) { + if (min!=null && maxValue!=null && min.compareTo(maxValue)>0) { + throw new IllegalArgumentException(); + } + minValue = min; + if (value!=null && min!=null && value.compareTo(min)<0) { + setValue((BigInteger)null); + } + } + + /** Sets the max value of the widget. + * @param max the max value. + *
If the current value is bigger than max, the current value is set to null. + * @throws IllegalArgumentException if max is lower than widget min value. + * @see #setRange(BigInteger, BigInteger) + */ + public void setMaxValue(BigInteger max) { + if (max!=null && minValue!=null && max.compareTo(minValue)<0) { + throw new IllegalArgumentException(); + } + maxValue = max; + if (value!=null && max!= null && value.compareTo(max)>0) { + setValue((BigInteger)null); + } + } + + /** Sets this widget min and max values. + *
If the current value is out of the new range, the current value is set to null. + * @param min The min value + * @param max The max value + */ + public void setRange(BigInteger min, BigInteger max) { + if ((min!=null)&&(max!=null)&&(min.compareTo(max)>0)) { + throw new IllegalArgumentException(); + } + minValue = min; + maxValue = max; + if (value!=null && ((min!=null && value.compareTo(min)<0) || (max!= null && value.compareTo(max)>0))) { + setValue((BigInteger)null); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/NumberWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/NumberWidget.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/NumberWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/NumberWidget.java index 647f6db..7c71b19 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/NumberWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/NumberWidget.java @@ -1,288 +1,288 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.lang.reflect.InvocationTargetException; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.NumberFormat; -import java.text.ParsePosition; -import java.util.Locale; - -import com.fathzer.soft.ajlib.utilities.NullUtils; - - -/** A widget to enter a number. - *
This widget automatically format the value it contains according to its local. - *
You can restrict the valid values by setting minimum and maximum values. - *
This bean defines two properties:
    - *
  • VALUE_PROPERTY: a double, the value currently entered in the field.
  • - *
  • CONTENT_VALID_PROPERTY: a boolean, true if the text currently entered in the field is a valid value, false if it is not. - *
    The CONTENT_VALID_PROPERTY is a read only property.
  • - *
- */ -public class NumberWidget extends TextWidget { - private static final long serialVersionUID = 1L; - private static final char NON_BREAKING_SPACE = 0x00A0; - private static final char NARROW_NON_BREAKING_SPACE = 0x202F; - private static final char SPACE = ' '; - - /** Value property identifier. */ - public static final String VALUE_PROPERTY = "value"; - /** Content validity property identifier. */ - public static final String CONTENT_VALID_PROPERTY = "contentValid"; - - private Number value; - /** The format used to parse the number. */ - private DecimalFormat format; - private boolean isEmptyAllowed; - private Number minValue; - private Number maxValue; - private boolean valid; - - /** Constructor. - * The local is set to default locale. - * @see #NumberWidget(Locale) - */ - public NumberWidget() { - this(Locale.getDefault()); - } - - /** Constructor. - * Empty field is not allowed. - * minValue is equals to Double.NEGATIVE_INFINITY, maxValue to Double.POSITIVE_INFINITY. - * The value is set to null. - * @param locale The locale to apply to the widget (use to format the amount typed). - */ - public NumberWidget(Locale locale) { - super(); - this.isEmptyAllowed = false; - this.minValue = Double.NEGATIVE_INFINITY; - this.maxValue = Double.POSITIVE_INFINITY; - format = buildFormat(locale); - this.addPropertyChangeListener(TEXT_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - updateValue(); - } - }); - this.addFocusListener(new FocusListener() { - @Override - public void focusLost(FocusEvent e) { - if (!e.isTemporary()) { - refreshText(value); - } - } - @Override - public void focusGained(FocusEvent e) { - // Nothing to do - } - }); - } - - /** Workaround of a weird java implementation (The bug id was 4510618, but it was removed despite not being fixed). - *
In some locals (French for instance), the grouping or decimal separators are a non breaking space or a narrow non breaking space, - * (depending on java version!!!) ... not a single space. - * Users may be very surprised to see that in France, "1 000,00" is not a number. - *
To make things easy, Java did not provide any method for getting or setting the effective grouping separator of currency instance until java 15. - *
In one word: Nightmare! - *
This method changes non breaking spaces in the format symbol by simple spaces. - *
When the user type a non breaking space, it is automatically converted to a simple space before to be passed - * to the parseValue method. So, it is highly recommended that this method is applied on the format returned by buildFormat. - * @param format The format to patch - * @return the modified input format. - * @see #buildFormat(Locale) - * @see #parseValue(String) - */ - protected DecimalFormat patchJavaBug4510618 (DecimalFormat format) { - DecimalFormatSymbols decimalFormatSymbols = format.getDecimalFormatSymbols(); - if (isNonBreakingSpaceFlavor(decimalFormatSymbols.getGroupingSeparator())) { - decimalFormatSymbols.setGroupingSeparator(SPACE); - } - if (isNonBreakingSpaceFlavor(decimalFormatSymbols.getDecimalSeparator())) { - decimalFormatSymbols.setDecimalSeparator(SPACE); - } - if (isNonBreakingSpaceFlavor(decimalFormatSymbols.getMonetaryDecimalSeparator())) { - decimalFormatSymbols.setMonetaryDecimalSeparator(SPACE); - } - // Until Java 15, there was nothing to set monetary grouping separator, hopefully no need in Java 8 and 11, other pre-15 versions reached their end of life. - try { - char sep = (Character)decimalFormatSymbols.getClass().getDeclaredMethod("getMonetaryGroupingSeparator").invoke(decimalFormatSymbols); - if (isNonBreakingSpaceFlavor(sep)) { - decimalFormatSymbols.getClass().getDeclaredMethod("setMonetaryGroupingSeparator", char.class).invoke(decimalFormatSymbols, SPACE); - } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { - // Fucking java < 15 - } - format.setDecimalFormatSymbols(decimalFormatSymbols); - format.setPositiveSuffix(replaceNonBreakingSpaces(format.getPositiveSuffix())); - format.setNegativeSuffix(replaceNonBreakingSpaces(format.getNegativeSuffix())); - format.setPositivePrefix(replaceNonBreakingSpaces(format.getPositivePrefix())); - format.setNegativePrefix(replaceNonBreakingSpaces(format.getNegativePrefix())); - return format; - } - - private boolean isNonBreakingSpaceFlavor(char character) { - return character==NON_BREAKING_SPACE || character==NARROW_NON_BREAKING_SPACE; - } - - private String replaceNonBreakingSpaces(String string) { - return string.replace(NON_BREAKING_SPACE, SPACE).replace(NARROW_NON_BREAKING_SPACE, SPACE); - } - - protected DecimalFormat buildFormat(Locale locale) { - return patchJavaBug4510618((DecimalFormat) NumberFormat.getNumberInstance(locale)); - } - - protected void setFormat (DecimalFormat format) { - this.format = format; - } - - protected DecimalFormat getFormat() { - return this.format; - } - - protected void refreshText() { - refreshText(value); - } - - private void refreshText(Number value) { - this.setText(value==null?"":format.format(value)); - } - - private void updateValue() { - boolean oldValid = this.valid; - Number changed = null; - String text = this.getText().trim(); - if (text.length()==0) { - this.valid = isEmptyAllowed; - } else { - changed = parseValue(text.replace(NON_BREAKING_SPACE, SPACE).replace(NARROW_NON_BREAKING_SPACE, SPACE)); - this.valid = (changed!=null) && (changed.doubleValue()>=minValue.doubleValue()) && (changed.doubleValue()<=maxValue.doubleValue()); - } - internalSetValue(changed==null?null:changed.doubleValue()); - if (this.valid!=oldValid) { - firePropertyChange(CONTENT_VALID_PROPERTY, oldValid, this.valid); - } - } - - /** Parses the text contained in the field. - *
This method is called each time the field is modified. - * @param text The trimmed text contained in the field - * @return The value of the text or null if the text can't be parsed as a number. - */ - protected Number parseValue(String text) { - return safeParse(format, text); - } - - /** Parses a text with a format and ensures the text has no extra characters after valid data. - *
We have to be cautious with parsing. If the text begins with a valid number but is followed by garbage, - * format.parse(String) will succeed !!! This method uses format.parse(String, ParsePosition) in order to detect - * garbage placed after a valid number. - * @param format The format to use for parsing - * @param text The text to be parsed - * @return A number, or null if the text is not valid according to the format - */ - public static Number safeParse(NumberFormat format, String text) { - ParsePosition pos = new ParsePosition(0); - Number candidate = format.parse(text, pos); - if (pos.getIndex()==text.length()) { - return candidate; - } else { - return null; - } - } - - /** Determines whether empty text (or blank) are considered as valid or not. - * @return true if blank field is valid. - */ - public boolean isEmptyAllowed() { - return isEmptyAllowed; - } - - /** Sets the validity of blank text. - * @param isEmptyAllowed true if blank text are allowed, false if not. - * The value associated with a blank field is always null. - */ - public void setEmptyAllowed(boolean isEmptyAllowed) { - this.isEmptyAllowed = isEmptyAllowed; - if (this.getText().trim().length()==0) { - updateValue(); - } - } - - /** Gets the minimum value allowed in the field. - * @return a double (Double.NEGATIVE_INFINITY if there is no limit) - */ - public Double getMinValue() { - return minValue.doubleValue(); - } - - /** Sets the minimum value allowed in the field. - * @param minValue a double (Double.NEGATIVE_INFINITY if there is no limit) - */ - public void setMinValue(Double minValue) { - this.minValue = minValue; - } - - /** Gets the maximum value allowed in the field. - * @return a double (Double.POSITIVE_INFINITY if there is no limit) - */ - public Double getMaxValue() { - return maxValue.doubleValue(); - } - - /** Sets the maximum value allowed in the field. - * @param maxValue a double (Double.POSITIVE_INFINITY if there is no limit) - */ - public void setMaxValue(Double maxValue) { - this.maxValue = maxValue; - } - - /** Gets the current value. - * @return a number or null. - */ - public Double getValue() { - updateValue(); - return this.value==null?null: this.value.doubleValue(); - } - - /** Sets the current value. - * @param value a value, or null. - */ - public void setValue(Double value) { - refreshText(value); - } - - @Override - public void setText(String t) { - super.setText(t); - updateValue(); - } - - /** Set the value without changing the content of the TextField. - * This method does nothing if the value is equals to the current widget value. - * @param value - * @return true if the value was changed - */ - private boolean internalSetValue(Double value) { - // Does nothing if amount is equals to current widget amount - // Be aware of null values - if (NullUtils.areEquals(value, this.value)) { - return false; - } - Number old = this.value; - this.value = value; - firePropertyChange(VALUE_PROPERTY, old, value); - return true; - } - - /** Gets the content validity. - * @return true if the content is valid, false if it is not. - */ - public boolean isContentValid() { - return this.valid; - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.reflect.InvocationTargetException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Locale; + +import com.fathzer.soft.ajlib.utilities.NullUtils; + + +/** A widget to enter a number. + *
This widget automatically format the value it contains according to its local. + *
You can restrict the valid values by setting minimum and maximum values. + *
This bean defines two properties:
    + *
  • VALUE_PROPERTY: a double, the value currently entered in the field.
  • + *
  • CONTENT_VALID_PROPERTY: a boolean, true if the text currently entered in the field is a valid value, false if it is not. + *
    The CONTENT_VALID_PROPERTY is a read only property.
  • + *
+ */ +public class NumberWidget extends TextWidget { + private static final long serialVersionUID = 1L; + private static final char NON_BREAKING_SPACE = 0x00A0; + private static final char NARROW_NON_BREAKING_SPACE = 0x202F; + private static final char SPACE = ' '; + + /** Value property identifier. */ + public static final String VALUE_PROPERTY = "value"; + /** Content validity property identifier. */ + public static final String CONTENT_VALID_PROPERTY = "contentValid"; + + private Number value; + /** The format used to parse the number. */ + private DecimalFormat format; + private boolean isEmptyAllowed; + private Number minValue; + private Number maxValue; + private boolean valid; + + /** Constructor. + * The local is set to default locale. + * @see #NumberWidget(Locale) + */ + public NumberWidget() { + this(Locale.getDefault()); + } + + /** Constructor. + * Empty field is not allowed. + * minValue is equals to Double.NEGATIVE_INFINITY, maxValue to Double.POSITIVE_INFINITY. + * The value is set to null. + * @param locale The locale to apply to the widget (use to format the amount typed). + */ + public NumberWidget(Locale locale) { + super(); + this.isEmptyAllowed = false; + this.minValue = Double.NEGATIVE_INFINITY; + this.maxValue = Double.POSITIVE_INFINITY; + format = buildFormat(locale); + this.addPropertyChangeListener(TEXT_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + updateValue(); + } + }); + this.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (!e.isTemporary()) { + refreshText(value); + } + } + @Override + public void focusGained(FocusEvent e) { + // Nothing to do + } + }); + } + + /** Workaround of a weird java implementation (The bug id was 4510618, but it was removed despite not being fixed). + *
In some locals (French for instance), the grouping or decimal separators are a non breaking space or a narrow non breaking space, + * (depending on java version!!!) ... not a single space. + * Users may be very surprised to see that in France, "1 000,00" is not a number. + *
To make things easy, Java did not provide any method for getting or setting the effective grouping separator of currency instance until java 15. + *
In one word: Nightmare! + *
This method changes non breaking spaces in the format symbol by simple spaces. + *
When the user type a non breaking space, it is automatically converted to a simple space before to be passed + * to the parseValue method. So, it is highly recommended that this method is applied on the format returned by buildFormat. + * @param format The format to patch + * @return the modified input format. + * @see #buildFormat(Locale) + * @see #parseValue(String) + */ + protected DecimalFormat patchJavaBug4510618 (DecimalFormat format) { + DecimalFormatSymbols decimalFormatSymbols = format.getDecimalFormatSymbols(); + if (isNonBreakingSpaceFlavor(decimalFormatSymbols.getGroupingSeparator())) { + decimalFormatSymbols.setGroupingSeparator(SPACE); + } + if (isNonBreakingSpaceFlavor(decimalFormatSymbols.getDecimalSeparator())) { + decimalFormatSymbols.setDecimalSeparator(SPACE); + } + if (isNonBreakingSpaceFlavor(decimalFormatSymbols.getMonetaryDecimalSeparator())) { + decimalFormatSymbols.setMonetaryDecimalSeparator(SPACE); + } + // Until Java 15, there was nothing to set monetary grouping separator, hopefully no need in Java 8 and 11, other pre-15 versions reached their end of life. + try { + char sep = (Character)decimalFormatSymbols.getClass().getDeclaredMethod("getMonetaryGroupingSeparator").invoke(decimalFormatSymbols); + if (isNonBreakingSpaceFlavor(sep)) { + decimalFormatSymbols.getClass().getDeclaredMethod("setMonetaryGroupingSeparator", char.class).invoke(decimalFormatSymbols, SPACE); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + // Fucking java < 15 + } + format.setDecimalFormatSymbols(decimalFormatSymbols); + format.setPositiveSuffix(replaceNonBreakingSpaces(format.getPositiveSuffix())); + format.setNegativeSuffix(replaceNonBreakingSpaces(format.getNegativeSuffix())); + format.setPositivePrefix(replaceNonBreakingSpaces(format.getPositivePrefix())); + format.setNegativePrefix(replaceNonBreakingSpaces(format.getNegativePrefix())); + return format; + } + + private boolean isNonBreakingSpaceFlavor(char character) { + return character==NON_BREAKING_SPACE || character==NARROW_NON_BREAKING_SPACE; + } + + private String replaceNonBreakingSpaces(String string) { + return string.replace(NON_BREAKING_SPACE, SPACE).replace(NARROW_NON_BREAKING_SPACE, SPACE); + } + + protected DecimalFormat buildFormat(Locale locale) { + return patchJavaBug4510618((DecimalFormat) NumberFormat.getNumberInstance(locale)); + } + + protected void setFormat (DecimalFormat format) { + this.format = format; + } + + protected DecimalFormat getFormat() { + return this.format; + } + + protected void refreshText() { + refreshText(value); + } + + private void refreshText(Number value) { + this.setText(value==null?"":format.format(value)); + } + + private void updateValue() { + boolean oldValid = this.valid; + Number changed = null; + String text = this.getText().trim(); + if (text.length()==0) { + this.valid = isEmptyAllowed; + } else { + changed = parseValue(text.replace(NON_BREAKING_SPACE, SPACE).replace(NARROW_NON_BREAKING_SPACE, SPACE)); + this.valid = (changed!=null) && (changed.doubleValue()>=minValue.doubleValue()) && (changed.doubleValue()<=maxValue.doubleValue()); + } + internalSetValue(changed==null?null:changed.doubleValue()); + if (this.valid!=oldValid) { + firePropertyChange(CONTENT_VALID_PROPERTY, oldValid, this.valid); + } + } + + /** Parses the text contained in the field. + *
This method is called each time the field is modified. + * @param text The trimmed text contained in the field + * @return The value of the text or null if the text can't be parsed as a number. + */ + protected Number parseValue(String text) { + return safeParse(format, text); + } + + /** Parses a text with a format and ensures the text has no extra characters after valid data. + *
We have to be cautious with parsing. If the text begins with a valid number but is followed by garbage, + * format.parse(String) will succeed !!! This method uses format.parse(String, ParsePosition) in order to detect + * garbage placed after a valid number. + * @param format The format to use for parsing + * @param text The text to be parsed + * @return A number, or null if the text is not valid according to the format + */ + public static Number safeParse(NumberFormat format, String text) { + ParsePosition pos = new ParsePosition(0); + Number candidate = format.parse(text, pos); + if (pos.getIndex()==text.length()) { + return candidate; + } else { + return null; + } + } + + /** Determines whether empty text (or blank) are considered as valid or not. + * @return true if blank field is valid. + */ + public boolean isEmptyAllowed() { + return isEmptyAllowed; + } + + /** Sets the validity of blank text. + * @param isEmptyAllowed true if blank text are allowed, false if not. + * The value associated with a blank field is always null. + */ + public void setEmptyAllowed(boolean isEmptyAllowed) { + this.isEmptyAllowed = isEmptyAllowed; + if (this.getText().trim().length()==0) { + updateValue(); + } + } + + /** Gets the minimum value allowed in the field. + * @return a double (Double.NEGATIVE_INFINITY if there is no limit) + */ + public Double getMinValue() { + return minValue.doubleValue(); + } + + /** Sets the minimum value allowed in the field. + * @param minValue a double (Double.NEGATIVE_INFINITY if there is no limit) + */ + public void setMinValue(Double minValue) { + this.minValue = minValue; + } + + /** Gets the maximum value allowed in the field. + * @return a double (Double.POSITIVE_INFINITY if there is no limit) + */ + public Double getMaxValue() { + return maxValue.doubleValue(); + } + + /** Sets the maximum value allowed in the field. + * @param maxValue a double (Double.POSITIVE_INFINITY if there is no limit) + */ + public void setMaxValue(Double maxValue) { + this.maxValue = maxValue; + } + + /** Gets the current value. + * @return a number or null. + */ + public Double getValue() { + updateValue(); + return this.value==null?null: this.value.doubleValue(); + } + + /** Sets the current value. + * @param value a value, or null. + */ + public void setValue(Double value) { + refreshText(value); + } + + @Override + public void setText(String t) { + super.setText(t); + updateValue(); + } + + /** Set the value without changing the content of the TextField. + * This method does nothing if the value is equals to the current widget value. + * @param value + * @return true if the value was changed + */ + private boolean internalSetValue(Double value) { + // Does nothing if amount is equals to current widget amount + // Be aware of null values + if (NullUtils.areEquals(value, this.value)) { + return false; + } + Number old = this.value; + this.value = value; + firePropertyChange(VALUE_PROPERTY, old, value); + return true; + } + + /** Gets the content validity. + * @return true if the content is valid, false if it is not. + */ + public boolean isContentValid() { + return this.valid; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/PageSelector.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/PageSelector.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/PageSelector.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/PageSelector.java index 968a769..3c2cf61 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/PageSelector.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/PageSelector.java @@ -1,241 +1,241 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.math.BigInteger; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; - -import com.fathzer.soft.ajlib.swing.Utils; - -/** A widget that allows to select a page in a specified range. - */ -public class PageSelector extends JPanel { - private static final String RES_PATH = "/com/fathzer/soft/ajlib/swing/widget/"; - public static final String PAGE_SELECTED_PROPERTY_NAME = "PageSelected"; - private static final long serialVersionUID = 1L; - - private IntegerWidget pageNumber; - private JButton nextPage; - private JButton lastPage; - private JButton previousPage; - private JButton firstPage; - private int pageCount; - private int currentPage; - private JLabel sizeLabel; - - /** - * Constructor. - */ - public PageSelector() { - this.pageCount = 0; - this.currentPage = -1; - setLayout(new GridBagLayout()); - setOpaque(false); - - GridBagConstraints gbcFirstPage = new GridBagConstraints(); - gbcFirstPage.insets = new Insets(0, 0, 0, 5); - gbcFirstPage.weighty = 1.0; - gbcFirstPage.fill = GridBagConstraints.VERTICAL; - gbcFirstPage.gridx = 1; - gbcFirstPage.gridy = 0; - add(getFirstPage(), gbcFirstPage); - - GridBagConstraints gbcPageNumber = new GridBagConstraints(); - gbcPageNumber.gridx = 3; - gbcPageNumber.gridy = 0; - gbcPageNumber.fill = GridBagConstraints.VERTICAL; - add(getPageNumber(), gbcPageNumber); - - sizeLabel = new JLabel("/"+pageCount); - GridBagConstraints gbcLabel = new GridBagConstraints(); - gbcLabel.insets = new Insets(0, 0, 0, 5); - gbcLabel.gridx = 4; - gbcLabel.gridy = 0; - add(sizeLabel, gbcLabel); - - GridBagConstraints gbcNextPage = new GridBagConstraints(); - gbcNextPage.fill = GridBagConstraints.VERTICAL; - gbcNextPage.insets = new Insets(0, 0, 0, 5); - gbcNextPage.gridx = 5; - gbcNextPage.gridy = 0; - add(getNextPage(), gbcNextPage); - - GridBagConstraints gbcLastPage = new GridBagConstraints(); - gbcLastPage.fill = GridBagConstraints.VERTICAL; - gbcLastPage.gridx = 6; - gbcLastPage.gridy = 0; - add(getLastPage(), gbcLastPage); - - GridBagConstraints gbcPreviousPage = new GridBagConstraints(); - gbcPreviousPage.fill = GridBagConstraints.VERTICAL; - gbcPreviousPage.insets = new Insets(0, 0, 0, 5); - gbcPreviousPage.gridx = 2; - gbcPreviousPage.gridy = 0; - add(getPreviousPage(), gbcPreviousPage); - - restoreButtonStates(); - } - - /** Gets "page number" field. - * @return an IntegerWidget - */ - public IntegerWidget getPageNumber() { - if (pageNumber==null) { - pageNumber = new IntegerWidget(pageCount==0?BigInteger.ZERO:BigInteger.ONE, BigInteger.valueOf(pageCount)); - pageNumber.setColumns(2); - pageNumber.addPropertyChangeListener(IntegerWidget.VALUE_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getNewValue()!=null) { - setPage(((BigInteger)evt.getNewValue()).intValue()-1); - } else { - setPage(-1); - } - } - }); - } - return pageNumber; - } - - /** Gets "Go to next page" button. - * @return a JButton - */ - public JButton getNextPage() { - if (nextPage==null) { - nextPage = new JButton(); - nextPage.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setPage(getCurrentPage()+1); - } - }); - nextPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"next.png"), 16*getFont().getSize()/12)); - setSelectionButtonSize(nextPage); - } - return nextPage; - } - - /** Gets "Go to last page" button. - * @return a JButton - */ - public JButton getLastPage() { - if (lastPage==null) { - lastPage = new JButton(); - lastPage.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setPage(pageCount-1); - } - }); - lastPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"last.png"), 16*getFont().getSize()/12)); - setSelectionButtonSize(lastPage); - } - return lastPage; - } - - /** Gets "Go to previous page" button. - * @return a JButton - */ - public JButton getPreviousPage() { - if (previousPage==null) { - previousPage = new JButton(); - previousPage.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setPage(getCurrentPage()-1); - } - }); - previousPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"previous.png"), 16*getFont().getSize()/12)); - setSelectionButtonSize(previousPage); - } - return previousPage; - } - - /** Gets "Go to first page" button. - * @return a JButton - */ - public JButton getFirstPage() { - if (firstPage==null) { - firstPage = new JButton(); - firstPage.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setPage(0); - } - }); - firstPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"first.png"), 16*getFont().getSize()/12)); - setSelectionButtonSize(firstPage); - } - return firstPage; - } - - /** Sets the current page index. - * @param index The index of the page (should be in the range 0 - page count-1) - * or -1 to specify that no page is selected. - * @throws IllegalArgumentException If index is not in the range [-1,getPageCount()-1]. - */ - public void setPage(int index) { - if (index>=getPageCount() || index<-1) { - throw new IllegalArgumentException(); - } - int old = getCurrentPage(); - if (index!=old) { - currentPage = index; - pageNumber.setValue(index<0?null:index+1); - restoreButtonStates(); - firePropertyChange(PAGE_SELECTED_PROPERTY_NAME, old, currentPage); - } - } - - private void restoreButtonStates() { - firstPage.setEnabled(getCurrentPage()>0); - previousPage.setEnabled(getCurrentPage()>0); - nextPage.setEnabled(getCurrentPage()=pageCount) { - setPage(pageCount-1); - } else { - restoreButtonStates(); - } - } - - /** Gets the current page number. - * @return the current page number (0 based) or -1 if no page is currently selected. - */ - public int getCurrentPage() { - return currentPage; - } - - /** Gets the page count. - * @return a positive or null integer. - */ - public int getPageCount() { - return this.pageCount; - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.math.BigInteger; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import com.fathzer.soft.ajlib.swing.Utils; + +/** A widget that allows to select a page in a specified range. + */ +public class PageSelector extends JPanel { + private static final String RES_PATH = "/com/fathzer/soft/ajlib/swing/widget/"; + public static final String PAGE_SELECTED_PROPERTY_NAME = "PageSelected"; + private static final long serialVersionUID = 1L; + + private IntegerWidget pageNumber; + private JButton nextPage; + private JButton lastPage; + private JButton previousPage; + private JButton firstPage; + private int pageCount; + private int currentPage; + private JLabel sizeLabel; + + /** + * Constructor. + */ + public PageSelector() { + this.pageCount = 0; + this.currentPage = -1; + setLayout(new GridBagLayout()); + setOpaque(false); + + GridBagConstraints gbcFirstPage = new GridBagConstraints(); + gbcFirstPage.insets = new Insets(0, 0, 0, 5); + gbcFirstPage.weighty = 1.0; + gbcFirstPage.fill = GridBagConstraints.VERTICAL; + gbcFirstPage.gridx = 1; + gbcFirstPage.gridy = 0; + add(getFirstPage(), gbcFirstPage); + + GridBagConstraints gbcPageNumber = new GridBagConstraints(); + gbcPageNumber.gridx = 3; + gbcPageNumber.gridy = 0; + gbcPageNumber.fill = GridBagConstraints.VERTICAL; + add(getPageNumber(), gbcPageNumber); + + sizeLabel = new JLabel("/"+pageCount); + GridBagConstraints gbcLabel = new GridBagConstraints(); + gbcLabel.insets = new Insets(0, 0, 0, 5); + gbcLabel.gridx = 4; + gbcLabel.gridy = 0; + add(sizeLabel, gbcLabel); + + GridBagConstraints gbcNextPage = new GridBagConstraints(); + gbcNextPage.fill = GridBagConstraints.VERTICAL; + gbcNextPage.insets = new Insets(0, 0, 0, 5); + gbcNextPage.gridx = 5; + gbcNextPage.gridy = 0; + add(getNextPage(), gbcNextPage); + + GridBagConstraints gbcLastPage = new GridBagConstraints(); + gbcLastPage.fill = GridBagConstraints.VERTICAL; + gbcLastPage.gridx = 6; + gbcLastPage.gridy = 0; + add(getLastPage(), gbcLastPage); + + GridBagConstraints gbcPreviousPage = new GridBagConstraints(); + gbcPreviousPage.fill = GridBagConstraints.VERTICAL; + gbcPreviousPage.insets = new Insets(0, 0, 0, 5); + gbcPreviousPage.gridx = 2; + gbcPreviousPage.gridy = 0; + add(getPreviousPage(), gbcPreviousPage); + + restoreButtonStates(); + } + + /** Gets "page number" field. + * @return an IntegerWidget + */ + public IntegerWidget getPageNumber() { + if (pageNumber==null) { + pageNumber = new IntegerWidget(pageCount==0?BigInteger.ZERO:BigInteger.ONE, BigInteger.valueOf(pageCount)); + pageNumber.setColumns(2); + pageNumber.addPropertyChangeListener(IntegerWidget.VALUE_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getNewValue()!=null) { + setPage(((BigInteger)evt.getNewValue()).intValue()-1); + } else { + setPage(-1); + } + } + }); + } + return pageNumber; + } + + /** Gets "Go to next page" button. + * @return a JButton + */ + public JButton getNextPage() { + if (nextPage==null) { + nextPage = new JButton(); + nextPage.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setPage(getCurrentPage()+1); + } + }); + nextPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"next.png"), 16*getFont().getSize()/12)); + setSelectionButtonSize(nextPage); + } + return nextPage; + } + + /** Gets "Go to last page" button. + * @return a JButton + */ + public JButton getLastPage() { + if (lastPage==null) { + lastPage = new JButton(); + lastPage.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setPage(pageCount-1); + } + }); + lastPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"last.png"), 16*getFont().getSize()/12)); + setSelectionButtonSize(lastPage); + } + return lastPage; + } + + /** Gets "Go to previous page" button. + * @return a JButton + */ + public JButton getPreviousPage() { + if (previousPage==null) { + previousPage = new JButton(); + previousPage.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setPage(getCurrentPage()-1); + } + }); + previousPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"previous.png"), 16*getFont().getSize()/12)); + setSelectionButtonSize(previousPage); + } + return previousPage; + } + + /** Gets "Go to first page" button. + * @return a JButton + */ + public JButton getFirstPage() { + if (firstPage==null) { + firstPage = new JButton(); + firstPage.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setPage(0); + } + }); + firstPage.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"first.png"), 16*getFont().getSize()/12)); + setSelectionButtonSize(firstPage); + } + return firstPage; + } + + /** Sets the current page index. + * @param index The index of the page (should be in the range 0 - page count-1) + * or -1 to specify that no page is selected. + * @throws IllegalArgumentException If index is not in the range [-1,getPageCount()-1]. + */ + public void setPage(int index) { + if (index>=getPageCount() || index<-1) { + throw new IllegalArgumentException(); + } + int old = getCurrentPage(); + if (index!=old) { + currentPage = index; + pageNumber.setValue(index<0?null:index+1); + restoreButtonStates(); + firePropertyChange(PAGE_SELECTED_PROPERTY_NAME, old, currentPage); + } + } + + private void restoreButtonStates() { + firstPage.setEnabled(getCurrentPage()>0); + previousPage.setEnabled(getCurrentPage()>0); + nextPage.setEnabled(getCurrentPage()=pageCount) { + setPage(pageCount-1); + } else { + restoreButtonStates(); + } + } + + /** Gets the current page number. + * @return the current page number (0 based) or -1 if no page is currently selected. + */ + public int getCurrentPage() { + return currentPage; + } + + /** Gets the page count. + * @return a positive or null integer. + */ + public int getPageCount() { + return this.pageCount; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/PasswordWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/PasswordWidget.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/PasswordWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/PasswordWidget.java index 6f24439..b5c9af9 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/PasswordWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/PasswordWidget.java @@ -1,60 +1,60 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import javax.swing.JPasswordField; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.PlainDocument; - -/** A JPasswordField with a property that maps its text. - *
I've found no way to track efficiently the modifications of the text of a JTextField ... so I developed this widget. - *
DocumentListeners are intended to do it, unfortunately, when a text is replace in a field, the listener receive two events:
    - *
  1. One when the replaced text is removed.
  2. - *
  3. One when the replacing text is inserted
  4. - *
- * The first event is ... simply absolutely misleading, it corresponds to a value that the text never had. - *
Another problem with DocumentListener is that you can't modify the text into it (it throws IllegalStateException). - *

Another way was to use KeyListeners ... but some key events are throw a long time (probably the key auto-repeat interval) - * after the key was released. And others events (for example a click on an OK button) may occurs before the listener is informed of the change. - *

This widget guarantees that no "ghost" property change is thrown ! - */ -public class PasswordWidget extends JPasswordField { - private static final long serialVersionUID = 1L; - - public static final String TEXT_PROPERTY = "text"; - - public PasswordWidget() { - this(0); - } - - public PasswordWidget(int nbColumns) { - super("", nbColumns); - this.setDocument(new MyDocument()); - } - - @SuppressWarnings("serial") - private class MyDocument extends PlainDocument { - private boolean ignoreEvents = false; - - @Override - public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { - String oldValue = new String(PasswordWidget.this.getPassword()); - this.ignoreEvents = true; - super.replace(offset, length, text, attrs); - this.ignoreEvents = false; - String newValue = new String(PasswordWidget.this.getPassword()); - if (!oldValue.equals(newValue)) { - PasswordWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); - } - } - - @Override - public void remove(int offs, int len) throws BadLocationException { - String oldValue = new String(PasswordWidget.this.getPassword()); - super.remove(offs, len); - String newValue = new String(PasswordWidget.this.getPassword()); - if (!ignoreEvents && !oldValue.equals(newValue)) { - PasswordWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); - } - } - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import javax.swing.JPasswordField; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; + +/** A JPasswordField with a property that maps its text. + *
I've found no way to track efficiently the modifications of the text of a JTextField ... so I developed this widget. + *
DocumentListeners are intended to do it, unfortunately, when a text is replace in a field, the listener receive two events:
    + *
  1. One when the replaced text is removed.
  2. + *
  3. One when the replacing text is inserted
  4. + *
+ * The first event is ... simply absolutely misleading, it corresponds to a value that the text never had. + *
Another problem with DocumentListener is that you can't modify the text into it (it throws IllegalStateException). + *

Another way was to use KeyListeners ... but some key events are throw a long time (probably the key auto-repeat interval) + * after the key was released. And others events (for example a click on an OK button) may occurs before the listener is informed of the change. + *

This widget guarantees that no "ghost" property change is thrown ! + */ +public class PasswordWidget extends JPasswordField { + private static final long serialVersionUID = 1L; + + public static final String TEXT_PROPERTY = "text"; + + public PasswordWidget() { + this(0); + } + + public PasswordWidget(int nbColumns) { + super("", nbColumns); + this.setDocument(new MyDocument()); + } + + @SuppressWarnings("serial") + private class MyDocument extends PlainDocument { + private boolean ignoreEvents = false; + + @Override + public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { + String oldValue = new String(PasswordWidget.this.getPassword()); + this.ignoreEvents = true; + super.replace(offset, length, text, attrs); + this.ignoreEvents = false; + String newValue = new String(PasswordWidget.this.getPassword()); + if (!oldValue.equals(newValue)) { + PasswordWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); + } + } + + @Override + public void remove(int offs, int len) throws BadLocationException { + String oldValue = new String(PasswordWidget.this.getPassword()); + super.remove(offs, len); + String newValue = new String(PasswordWidget.this.getPassword()); + if (!ignoreEvents && !oldValue.equals(newValue)) { + PasswordWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); + } + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/RotatingLabel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/RotatingLabel.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/RotatingLabel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/RotatingLabel.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/SearchPanel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/SearchPanel.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/SearchPanel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/SearchPanel.java index b8b13c7..1c0a1fd 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/SearchPanel.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/SearchPanel.java @@ -1,247 +1,247 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import javax.swing.JPanel; - -import javax.swing.JLabel; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.GridBagLayout; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -import javax.swing.JCheckBox; -import javax.swing.text.BadLocationException; -import javax.swing.text.JTextComponent; - -import java.awt.event.ItemListener; -import java.awt.event.ItemEvent; -import javax.swing.JTextField; -import javax.swing.JButton; -import javax.swing.ImageIcon; - -import com.fathzer.soft.ajlib.swing.TextSearcher; -import com.fathzer.soft.ajlib.swing.framework.Application; - -import java.awt.event.ActionListener; -import java.awt.event.ActionEvent; - -/** A panel with every need to search text in a text component. - *
It provides a text field to enter the searched text, buttons to navigate through occurrences and setting buttons. - *
When text is entered in search the text field, the first occurrence is automatically highlight in the text component. - */ -@SuppressWarnings("serial") -public class SearchPanel extends JPanel { - private JLabel lblNewLabel; - private TextWidget searchedTextField; - private JPanel settingsPanel; - private JCheckBox chckbxCase; - private JCheckBox chckbxDiacritical; - private JTextField resultField; - private JButton upButton; - private JButton downButton; - private JPanel resultPanel; - - private JTextComponent textComponent; - private TextSearcher searcher; - private int currentOffset; - - /** - * Constructor. - * @param textComponent The component in which to search. - */ - public SearchPanel(JTextComponent textComponent) { - this.textComponent = textComponent; - this.searcher = new TextSearcher(this.textComponent); - initialize(); - } - - private void initialize() { - GridBagLayout gblSearchPanel = new GridBagLayout(); - setLayout(gblSearchPanel); - GridBagConstraints gbcLblNewLabel = new GridBagConstraints(); - gbcLblNewLabel.insets = new Insets(0, 0, 0, 5); - gbcLblNewLabel.anchor = GridBagConstraints.EAST; - gbcLblNewLabel.gridx = 0; - gbcLblNewLabel.gridy = 0; - add(getLblNewLabel(), gbcLblNewLabel); - GridBagConstraints gbcSearchedTextField = new GridBagConstraints(); - gbcSearchedTextField.fill = GridBagConstraints.HORIZONTAL; - gbcSearchedTextField.weightx = 1.0; - gbcSearchedTextField.gridx = 1; - gbcSearchedTextField.gridy = 0; - add(getSearchedTextField(), gbcSearchedTextField); - GridBagConstraints gbcResultPanel = new GridBagConstraints(); - gbcResultPanel.fill = GridBagConstraints.BOTH; - gbcResultPanel.gridx = 2; - gbcResultPanel.gridy = 0; - add(getResultPanel(), gbcResultPanel); - GridBagConstraints gbcSettingsPanel = new GridBagConstraints(); - gbcSettingsPanel.fill = GridBagConstraints.BOTH; - gbcSettingsPanel.gridx = 3; - gbcSettingsPanel.gridy = 0; - add(getSettingsPanel(), gbcSettingsPanel); - } - - private JLabel getLblNewLabel() { - if (lblNewLabel == null) { - lblNewLabel = new JLabel(Application.getString("SearchPanel.find", getLocale())); //$NON-NLS-1$ - } - return lblNewLabel; - } - - private TextWidget getSearchedTextField() { - if (searchedTextField == null) { - searchedTextField = new TextWidget(); - searchedTextField.addPropertyChangeListener(TextWidget.TEXT_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - doSearch(); - } - }); - } - return searchedTextField; - } - - private JCheckBox getCaseInsensitiveCheckBox() { - if (chckbxCase == null) { - chckbxCase = new JCheckBox(Application.getString("SearchPanel.ingnoreCase", getLocale())); //$NON-NLS-1$ - chckbxCase.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - searcher.setCaseSentitive(!getCaseInsensitiveCheckBox().isSelected()); - doSearch(); - } - }); - chckbxCase.setSelected(true); - } - return chckbxCase; - } - - private JPanel getSettingsPanel() { - if (settingsPanel == null) { - settingsPanel = new JPanel(); - GridBagLayout gblSettingsPanel = new GridBagLayout(); - settingsPanel.setLayout(gblSettingsPanel); - GridBagConstraints gbcChckbxCase = new GridBagConstraints(); - gbcChckbxCase.anchor = GridBagConstraints.WEST; - gbcChckbxCase.insets = new Insets(0, 0, 5, 5); - gbcChckbxCase.gridx = 0; - gbcChckbxCase.gridy = 0; - settingsPanel.add(getCaseInsensitiveCheckBox(), gbcChckbxCase); - GridBagConstraints gbcChckbxDiacritical = new GridBagConstraints(); - gbcChckbxDiacritical.insets = new Insets(0, 0, 0, 5); - gbcChckbxDiacritical.anchor = GridBagConstraints.WEST; - gbcChckbxDiacritical.gridx = 0; - gbcChckbxDiacritical.gridy = 1; - settingsPanel.add(getDiacriticalInsensitiveCheckbox(), gbcChckbxDiacritical); - } - return settingsPanel; - } - private JCheckBox getDiacriticalInsensitiveCheckbox() { - if (chckbxDiacritical == null) { - chckbxDiacritical = new JCheckBox(Application.getString("SearchPanel.ignoreMarks", getLocale())); //$NON-NLS-1$ - chckbxDiacritical.setSelected(true); - chckbxDiacritical.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - searcher.setDiacriticalSensitive(!getDiacriticalInsensitiveCheckbox().isSelected()); - doSearch(); - } - }); - } - return chckbxDiacritical; - } - private void doSearch() { - String text = getSearchedTextField().getText(); - searcher.setSearchedText(text); - int[] offsets = searcher.getOffsets(); - searchedTextField.setBackground((text.length()>0) && (offsets.length==0)?Color.red:Color.white); - getResultPanel().setVisible(offsets.length>0); - if (offsets.length>0) { - setSelected(0); - } else { - textComponent.getHighlighter().removeAllHighlights(); - } - SearchPanel.this.validate(); - } - - private void setSelected(int offsetNum) { - try { - int[] offsets = searcher.getOffsets(); - searcher.highlight(offsets[offsetNum], null); - currentOffset = offsetNum; - getDownButton().setEnabled(currentOffset!=offsets.length-1); - getUpButton().setEnabled(currentOffset!=0); - getResultField().setText((currentOffset+1)+"/"+offsets.length); //$NON-NLS-1$ - } catch (BadLocationException e) { - throw new RuntimeException(e); - } - } - - private JTextField getResultField() { - if (resultField == null) { - resultField = new JTextField(); - resultField.setEditable(false); - resultField.setFocusable(false); - resultField.setColumns(5); - } - return resultField; - } - private JButton getUpButton() { - if (upButton == null) { - upButton = new JButton(new ImageIcon(getClass().getResource("/com/fathzer/soft/ajlib/swing/widget/up.png"))); //$NON-NLS-1$ - upButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setSelected(currentOffset-1); - } - }); - upButton.setContentAreaFilled(false); - upButton.setPreferredSize(new Dimension(16,12)); - } - return upButton; - } - private JButton getDownButton() { - if (downButton == null) { - downButton = new JButton(new ImageIcon(getClass().getResource("/com/fathzer/soft/ajlib/swing/widget/down.png"))); //$NON-NLS-1$ - downButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - setSelected(currentOffset+1); - } - }); - downButton.setContentAreaFilled(false); - downButton.setPreferredSize(new Dimension(16,12)); - } - return downButton; - } - private JPanel getResultPanel() { - if (resultPanel == null) { - resultPanel = new JPanel(); - GridBagLayout gblResultPanel = new GridBagLayout(); - resultPanel.setLayout(gblResultPanel); - GridBagConstraints gbcResultField = new GridBagConstraints(); - gbcResultField.fill = GridBagConstraints.HORIZONTAL; - gbcResultField.gridheight = 2; - gbcResultField.gridx = 0; - gbcResultField.gridy = 0; - resultPanel.add(getResultField(), gbcResultField); - GridBagConstraints gbcUpButton = new GridBagConstraints(); - gbcUpButton.gridx = 1; - gbcUpButton.gridy = 0; - resultPanel.add(getUpButton(), gbcUpButton); - GridBagConstraints gbcDownButton = new GridBagConstraints(); - gbcDownButton.gridx = 1; - gbcDownButton.gridy = 1; - resultPanel.add(getDownButton(), gbcDownButton); - } - return resultPanel; - } - - /** Show/hide the search settings. - * @param visible true to show the settings, false to hide them. - */ - public void setSettingsVisible(boolean visible) { - this.getDiacriticalInsensitiveCheckbox().setVisible(visible); - this.getCaseInsensitiveCheckBox().setVisible(visible); - } -} +package com.fathzer.soft.ajlib.swing.widget; + +import javax.swing.JPanel; + +import javax.swing.JLabel; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.JCheckBox; +import javax.swing.text.BadLocationException; +import javax.swing.text.JTextComponent; + +import java.awt.event.ItemListener; +import java.awt.event.ItemEvent; +import javax.swing.JTextField; +import javax.swing.JButton; +import javax.swing.ImageIcon; + +import com.fathzer.soft.ajlib.swing.TextSearcher; +import com.fathzer.soft.ajlib.swing.framework.Application; + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +/** A panel with every need to search text in a text component. + *
It provides a text field to enter the searched text, buttons to navigate through occurrences and setting buttons. + *
When text is entered in search the text field, the first occurrence is automatically highlight in the text component. + */ +@SuppressWarnings("serial") +public class SearchPanel extends JPanel { + private JLabel lblNewLabel; + private TextWidget searchedTextField; + private JPanel settingsPanel; + private JCheckBox chckbxCase; + private JCheckBox chckbxDiacritical; + private JTextField resultField; + private JButton upButton; + private JButton downButton; + private JPanel resultPanel; + + private JTextComponent textComponent; + private TextSearcher searcher; + private int currentOffset; + + /** + * Constructor. + * @param textComponent The component in which to search. + */ + public SearchPanel(JTextComponent textComponent) { + this.textComponent = textComponent; + this.searcher = new TextSearcher(this.textComponent); + initialize(); + } + + private void initialize() { + GridBagLayout gblSearchPanel = new GridBagLayout(); + setLayout(gblSearchPanel); + GridBagConstraints gbcLblNewLabel = new GridBagConstraints(); + gbcLblNewLabel.insets = new Insets(0, 0, 0, 5); + gbcLblNewLabel.anchor = GridBagConstraints.EAST; + gbcLblNewLabel.gridx = 0; + gbcLblNewLabel.gridy = 0; + add(getLblNewLabel(), gbcLblNewLabel); + GridBagConstraints gbcSearchedTextField = new GridBagConstraints(); + gbcSearchedTextField.fill = GridBagConstraints.HORIZONTAL; + gbcSearchedTextField.weightx = 1.0; + gbcSearchedTextField.gridx = 1; + gbcSearchedTextField.gridy = 0; + add(getSearchedTextField(), gbcSearchedTextField); + GridBagConstraints gbcResultPanel = new GridBagConstraints(); + gbcResultPanel.fill = GridBagConstraints.BOTH; + gbcResultPanel.gridx = 2; + gbcResultPanel.gridy = 0; + add(getResultPanel(), gbcResultPanel); + GridBagConstraints gbcSettingsPanel = new GridBagConstraints(); + gbcSettingsPanel.fill = GridBagConstraints.BOTH; + gbcSettingsPanel.gridx = 3; + gbcSettingsPanel.gridy = 0; + add(getSettingsPanel(), gbcSettingsPanel); + } + + private JLabel getLblNewLabel() { + if (lblNewLabel == null) { + lblNewLabel = new JLabel(Application.getString("SearchPanel.find", getLocale())); //$NON-NLS-1$ + } + return lblNewLabel; + } + + private TextWidget getSearchedTextField() { + if (searchedTextField == null) { + searchedTextField = new TextWidget(); + searchedTextField.addPropertyChangeListener(TextWidget.TEXT_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + doSearch(); + } + }); + } + return searchedTextField; + } + + private JCheckBox getCaseInsensitiveCheckBox() { + if (chckbxCase == null) { + chckbxCase = new JCheckBox(Application.getString("SearchPanel.ingnoreCase", getLocale())); //$NON-NLS-1$ + chckbxCase.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + searcher.setCaseSentitive(!getCaseInsensitiveCheckBox().isSelected()); + doSearch(); + } + }); + chckbxCase.setSelected(true); + } + return chckbxCase; + } + + private JPanel getSettingsPanel() { + if (settingsPanel == null) { + settingsPanel = new JPanel(); + GridBagLayout gblSettingsPanel = new GridBagLayout(); + settingsPanel.setLayout(gblSettingsPanel); + GridBagConstraints gbcChckbxCase = new GridBagConstraints(); + gbcChckbxCase.anchor = GridBagConstraints.WEST; + gbcChckbxCase.insets = new Insets(0, 0, 5, 5); + gbcChckbxCase.gridx = 0; + gbcChckbxCase.gridy = 0; + settingsPanel.add(getCaseInsensitiveCheckBox(), gbcChckbxCase); + GridBagConstraints gbcChckbxDiacritical = new GridBagConstraints(); + gbcChckbxDiacritical.insets = new Insets(0, 0, 0, 5); + gbcChckbxDiacritical.anchor = GridBagConstraints.WEST; + gbcChckbxDiacritical.gridx = 0; + gbcChckbxDiacritical.gridy = 1; + settingsPanel.add(getDiacriticalInsensitiveCheckbox(), gbcChckbxDiacritical); + } + return settingsPanel; + } + private JCheckBox getDiacriticalInsensitiveCheckbox() { + if (chckbxDiacritical == null) { + chckbxDiacritical = new JCheckBox(Application.getString("SearchPanel.ignoreMarks", getLocale())); //$NON-NLS-1$ + chckbxDiacritical.setSelected(true); + chckbxDiacritical.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + searcher.setDiacriticalSensitive(!getDiacriticalInsensitiveCheckbox().isSelected()); + doSearch(); + } + }); + } + return chckbxDiacritical; + } + private void doSearch() { + String text = getSearchedTextField().getText(); + searcher.setSearchedText(text); + int[] offsets = searcher.getOffsets(); + searchedTextField.setBackground((text.length()>0) && (offsets.length==0)?Color.red:Color.white); + getResultPanel().setVisible(offsets.length>0); + if (offsets.length>0) { + setSelected(0); + } else { + textComponent.getHighlighter().removeAllHighlights(); + } + SearchPanel.this.validate(); + } + + private void setSelected(int offsetNum) { + try { + int[] offsets = searcher.getOffsets(); + searcher.highlight(offsets[offsetNum], null); + currentOffset = offsetNum; + getDownButton().setEnabled(currentOffset!=offsets.length-1); + getUpButton().setEnabled(currentOffset!=0); + getResultField().setText((currentOffset+1)+"/"+offsets.length); //$NON-NLS-1$ + } catch (BadLocationException e) { + throw new RuntimeException(e); + } + } + + private JTextField getResultField() { + if (resultField == null) { + resultField = new JTextField(); + resultField.setEditable(false); + resultField.setFocusable(false); + resultField.setColumns(5); + } + return resultField; + } + private JButton getUpButton() { + if (upButton == null) { + upButton = new JButton(new ImageIcon(getClass().getResource("/com/fathzer/soft/ajlib/swing/widget/up.png"))); //$NON-NLS-1$ + upButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setSelected(currentOffset-1); + } + }); + upButton.setContentAreaFilled(false); + upButton.setPreferredSize(new Dimension(16,12)); + } + return upButton; + } + private JButton getDownButton() { + if (downButton == null) { + downButton = new JButton(new ImageIcon(getClass().getResource("/com/fathzer/soft/ajlib/swing/widget/down.png"))); //$NON-NLS-1$ + downButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setSelected(currentOffset+1); + } + }); + downButton.setContentAreaFilled(false); + downButton.setPreferredSize(new Dimension(16,12)); + } + return downButton; + } + private JPanel getResultPanel() { + if (resultPanel == null) { + resultPanel = new JPanel(); + GridBagLayout gblResultPanel = new GridBagLayout(); + resultPanel.setLayout(gblResultPanel); + GridBagConstraints gbcResultField = new GridBagConstraints(); + gbcResultField.fill = GridBagConstraints.HORIZONTAL; + gbcResultField.gridheight = 2; + gbcResultField.gridx = 0; + gbcResultField.gridy = 0; + resultPanel.add(getResultField(), gbcResultField); + GridBagConstraints gbcUpButton = new GridBagConstraints(); + gbcUpButton.gridx = 1; + gbcUpButton.gridy = 0; + resultPanel.add(getUpButton(), gbcUpButton); + GridBagConstraints gbcDownButton = new GridBagConstraints(); + gbcDownButton.gridx = 1; + gbcDownButton.gridy = 1; + resultPanel.add(getDownButton(), gbcDownButton); + } + return resultPanel; + } + + /** Show/hide the search settings. + * @param visible true to show the settings, false to hide them. + */ + public void setSettingsVisible(boolean visible) { + this.getDiacriticalInsensitiveCheckbox().setVisible(visible); + this.getCaseInsensitiveCheckBox().setVisible(visible); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/TextWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/TextWidget.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/TextWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/TextWidget.java index 08164fd..4327839 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/TextWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/TextWidget.java @@ -1,326 +1,326 @@ -package com.fathzer.soft.ajlib.swing.widget; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.TreeSet; - -import javax.swing.AbstractListModel; -import javax.swing.DefaultListCellRenderer; -import javax.swing.JComponent; -import javax.swing.JList; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.border.AbstractBorder; -import javax.swing.border.Border; -import javax.swing.border.CompoundBorder; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.PlainDocument; - -import com.fathzer.soft.ajlib.utilities.NullUtils; -import com.fathzer.soft.ajlib.utilities.TextMatcher; - -/** A JTextField with a property that maps its text and the ability to define predefined values. - *
I've found no way to track efficiently the modifications of the text of a JTextField ... so I developed this widget. - *
DocumentListeners are intended to do it, unfortunately, when a text is replace in a field, the listener receive two events:
    - *
  1. One when the replaced text is removed.
  2. - *
  3. One when the replacing text is inserted
  4. - *
- * The first event is ... simply absolutely misleading, it corresponds to a value that the text never had. - *
Another problem with DocumentListener is that you can't modify the text into it (it throws IllegalStateException). - *
Another way was to use KeyListeners ... but some key events are throw a long time (probably the key auto-repeat interval) - * after the key was released. And others events (for example a click on an OK button) may occurs before the listener is informed of the change. - *
This widget guarantees that no "ghost" property change is thrown ! - *

This class implements a popup menu that allows to select the field content into a list of predefined values. - *
The difference with a JComboBox is the popup content and selection is updated when the user changes the text field content. - * It always contains only predefined text that contains the current widget text. - */ -public class TextWidget extends JTextField { - private static final long serialVersionUID = 1L; - - public static final String TEXT_PROPERTY = "text"; //$NON-NLS-1$ - /** A PropertyChangeEvent of this name is fired when a predefined value is selected. - * The values of the event are the field content. - */ - public static final String PREDEFINED_VALUE = "PREDEFINED_VALUE"; //$NON-NLS-1$ - - private JPopupMenu popup; - private JList list; - private String predefined=null; - private String lastText=""; //$NON-NLS-1$ - private int unsortedMax; - private String[] proposals; - - private static class UpperLineBorder extends AbstractBorder { - private static final long serialVersionUID = 1L; - protected Color lineColor; - - public UpperLineBorder(Color color) { - lineColor = color; - } - - @Override - public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { - g.setColor(lineColor); - g.drawLine(x+1, y, x+width-2, y); - } - } - - private class MyRenderer extends DefaultListCellRenderer { - private static final long serialVersionUID = 1L; - - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - if (((String)value).length()==0) { - value = " "; //$NON-NLS-1$ - } - JComponent label = (JComponent) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if ((unsortedMax>0) && (index==unsortedMax)) { - Border border = new UpperLineBorder(label.getForeground().brighter()); - label.setBorder(new CompoundBorder(border, label.getBorder())); - } - return label; - } - } - - /** Constructor. - */ - public TextWidget () { - // Revision 244 of this method contains a not efficient try to implement command-V in textfield on MacOS - this.setDocument(new MyDocument()); - } - - private void installPopup() { - if (popup==null) { - unsortedMax = Integer.MAX_VALUE; - popup = new JPopupMenu(); - list = new JList<>(new PopupListModel()); - list.setAutoscrolls(true); - list.setCellRenderer(new MyRenderer()); - popup.add(new JScrollPane(list)); - popup.setFocusable(false); - - // If the component looses the focus and the popup is shown, we have to hide the popup - // The following FocusListener will do that - addFocusListener(new FocusListener() { - @Override - public void focusLost(FocusEvent e) { - if (popup.isVisible() && !e.isTemporary() && (!list.equals(e.getOppositeComponent()))) { - popup.setVisible(false); - if (e.getOppositeComponent()!=null) { - e.getOppositeComponent().requestFocus(); - } - } - } - - @Override - public void focusGained(FocusEvent e) { - // Nothing to do in this case - } - }); - // When the user clicks the list, we have to set the value clicked in the list, hide the popup, then transfer back the focus to the textField - list.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton()==MouseEvent.BUTTON1) { - setPredefined(list.getSelectedValue()); - popup.setVisible(false); - } - } - @Override - public void mouseReleased(MouseEvent e) { - requestFocus(); - } - }); - // When the user press the down arrow, show the popup, if it's hidden. - // Up, down and enter key respectivly selects next element, selects previous element, set the textField to the selected element - // When a key is type, and changed the content of the field, this adapter also refresh the list selected element - addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_DOWN) { // down arrow key was pressed - if (!popup.isVisible()) { - showPopup(); - } else { - int index = list.getSelectedIndex(); - if (index < list.getModel().getSize()) { - index++; - list.setSelectedIndex(index); - } - } - } else if (e.getKeyCode() == KeyEvent.VK_UP) { // Up arrow key was pressed - if (popup.isVisible()) { - int index = list.getSelectedIndex(); - if (index > 0) { - index--; - list.setSelectedIndex(index); - } - } - } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { // Enter key pressed - if (popup.isVisible()) { - if (list.getSelectedIndex()>=0) { - setPredefined(list.getSelectedValue()); - } - popup.setVisible(false); - e.consume(); - } - } - } - - @Override - public void keyReleased(KeyEvent e) { - String text = getText(); - if (!text.equals(lastText)) { - fillModel(text); - lastText = text; - setPredefined((String)null); - showPopup(); - } - } - }); - } - } - - public TextWidget(int columns) { - this(); - this.setColumns(columns); - } - - private void setPredefined(String value) { - if (value!=null) { - lastText = value; - setText(value); - } - if (!NullUtils.areEquals(predefined, value)) { - String oldPredefined = predefined; - predefined = value; - this.firePropertyChange(PREDEFINED_VALUE, oldPredefined, predefined); - } - } - - /** Sets the predefined values allowed by the field. - * @param array The predefined values. - * @see #setPredefined(String[], int) - */ - public void setPredefined(String[] array) { - setPredefined (array, Integer.MAX_VALUE); - } - - /** Sets the predefined values allowed by the field. - * @param array The predefined values. - * @param unsortedMax Maximum number of unsorted values. - *
The values can be divided in two groups (which will be separated by a thin line). - *
    - *
  1. A group of values, that matches the current typed text, sorted in the same order as in the array argument.
  2. - *
  3. A group of values, that matches the current typed text, sorted in alphabetical order.
  4. - *
- *
This argument contains the size of the first group. Use Integer.MAX_VALUE to always display values in the same order as in the array. 0 to always sorted values. - */ - public void setPredefined(String[] array, int unsortedMax) { - installPopup(); - this.unsortedMax = unsortedMax; - this.proposals = array==null ? null : array.clone(); - fillModel(this.getText()); - } - - private void showPopup() { - if (list.getModel().getSize()!=0) { - Dimension size = popup.getPreferredSize(); - if (getWidth()>size.width) { - popup.setPreferredSize(new Dimension(getWidth(), size.height)); - } - popup.show(TextWidget.this, 0, getHeight()); - requestFocus(false); - } else { - popup.setVisible(false); - } - } - - private void fillModel(String text) { - TextMatcher matcher = new TextMatcher(TextMatcher.Kind.CONTAINS, text, false, false); //TODO Must match "starts with" ... to be implemented in TextMatcher - ArrayList okProbaSort = new ArrayList<>(); - TreeSet okAlphabeticSort = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - for (String value : this.proposals) { - if (matcher.matches(value)) { - if (okProbaSort.size()<=unsortedMax) { - okProbaSort.add(value); - } else { - okAlphabeticSort.add(value); - } - } - } - okProbaSort.addAll(okAlphabeticSort); - ((PopupListModel)list.getModel()).setValues(okProbaSort.toArray(new String[okProbaSort.size()])); - - if (0 != list.getSelectedIndex()) { - list.setSelectedIndex(0); - } - } - - private static final class PopupListModel extends AbstractListModel { - private static final long serialVersionUID = 1L; - String[] values; - - PopupListModel() { - this.values = new String[0]; - } - - @Override - public int getSize() { - return values.length; - } - - @Override - public String getElementAt(int index) { - return values[index]; - } - - public void setValues(String[] values) { - int n = this.values.length; - if (n>0) { - this.values = new String[0]; - fireIntervalRemoved(this, 0, n); - } - this.values = values.clone(); - fireIntervalAdded(this, 0, this.values.length); - } - } - - private class MyDocument extends PlainDocument { - private static final long serialVersionUID = 1L; - private boolean ignoreEvents = false; - - @Override - public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { - String oldValue = TextWidget.this.getText(); - this.ignoreEvents = true; - super.replace(offset, length, text, attrs); - this.ignoreEvents = false; - String newValue = TextWidget.this.getText(); - if (!oldValue.equals(newValue)) { - TextWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); - } - } - - @Override - public void remove(int offs, int len) throws BadLocationException { - String oldValue = TextWidget.this.getText(); - super.remove(offs, len); - String newValue = TextWidget.this.getText(); - if (!ignoreEvents && !oldValue.equals(newValue)) { - TextWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); - } - } - } - -} +package com.fathzer.soft.ajlib.swing.widget; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.TreeSet; + +import javax.swing.AbstractListModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.border.AbstractBorder; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; + +import com.fathzer.soft.ajlib.utilities.NullUtils; +import com.fathzer.soft.ajlib.utilities.TextMatcher; + +/** A JTextField with a property that maps its text and the ability to define predefined values. + *
I've found no way to track efficiently the modifications of the text of a JTextField ... so I developed this widget. + *
DocumentListeners are intended to do it, unfortunately, when a text is replace in a field, the listener receive two events:
    + *
  1. One when the replaced text is removed.
  2. + *
  3. One when the replacing text is inserted
  4. + *
+ * The first event is ... simply absolutely misleading, it corresponds to a value that the text never had. + *
Another problem with DocumentListener is that you can't modify the text into it (it throws IllegalStateException). + *
Another way was to use KeyListeners ... but some key events are throw a long time (probably the key auto-repeat interval) + * after the key was released. And others events (for example a click on an OK button) may occurs before the listener is informed of the change. + *
This widget guarantees that no "ghost" property change is thrown ! + *

This class implements a popup menu that allows to select the field content into a list of predefined values. + *
The difference with a JComboBox is the popup content and selection is updated when the user changes the text field content. + * It always contains only predefined text that contains the current widget text. + */ +public class TextWidget extends JTextField { + private static final long serialVersionUID = 1L; + + public static final String TEXT_PROPERTY = "text"; //$NON-NLS-1$ + /** A PropertyChangeEvent of this name is fired when a predefined value is selected. + * The values of the event are the field content. + */ + public static final String PREDEFINED_VALUE = "PREDEFINED_VALUE"; //$NON-NLS-1$ + + private JPopupMenu popup; + private JList list; + private String predefined=null; + private String lastText=""; //$NON-NLS-1$ + private int unsortedMax; + private String[] proposals; + + private static class UpperLineBorder extends AbstractBorder { + private static final long serialVersionUID = 1L; + protected Color lineColor; + + public UpperLineBorder(Color color) { + lineColor = color; + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + g.setColor(lineColor); + g.drawLine(x+1, y, x+width-2, y); + } + } + + private class MyRenderer extends DefaultListCellRenderer { + private static final long serialVersionUID = 1L; + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + if (((String)value).length()==0) { + value = " "; //$NON-NLS-1$ + } + JComponent label = (JComponent) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if ((unsortedMax>0) && (index==unsortedMax)) { + Border border = new UpperLineBorder(label.getForeground().brighter()); + label.setBorder(new CompoundBorder(border, label.getBorder())); + } + return label; + } + } + + /** Constructor. + */ + public TextWidget () { + // Revision 244 of this method contains a not efficient try to implement command-V in textfield on MacOS + this.setDocument(new MyDocument()); + } + + private void installPopup() { + if (popup==null) { + unsortedMax = Integer.MAX_VALUE; + popup = new JPopupMenu(); + list = new JList<>(new PopupListModel()); + list.setAutoscrolls(true); + list.setCellRenderer(new MyRenderer()); + popup.add(new JScrollPane(list)); + popup.setFocusable(false); + + // If the component looses the focus and the popup is shown, we have to hide the popup + // The following FocusListener will do that + addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (popup.isVisible() && !e.isTemporary() && (!list.equals(e.getOppositeComponent()))) { + popup.setVisible(false); + if (e.getOppositeComponent()!=null) { + e.getOppositeComponent().requestFocus(); + } + } + } + + @Override + public void focusGained(FocusEvent e) { + // Nothing to do in this case + } + }); + // When the user clicks the list, we have to set the value clicked in the list, hide the popup, then transfer back the focus to the textField + list.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton()==MouseEvent.BUTTON1) { + setPredefined(list.getSelectedValue()); + popup.setVisible(false); + } + } + @Override + public void mouseReleased(MouseEvent e) { + requestFocus(); + } + }); + // When the user press the down arrow, show the popup, if it's hidden. + // Up, down and enter key respectivly selects next element, selects previous element, set the textField to the selected element + // When a key is type, and changed the content of the field, this adapter also refresh the list selected element + addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_DOWN) { // down arrow key was pressed + if (!popup.isVisible()) { + showPopup(); + } else { + int index = list.getSelectedIndex(); + if (index < list.getModel().getSize()) { + index++; + list.setSelectedIndex(index); + } + } + } else if (e.getKeyCode() == KeyEvent.VK_UP) { // Up arrow key was pressed + if (popup.isVisible()) { + int index = list.getSelectedIndex(); + if (index > 0) { + index--; + list.setSelectedIndex(index); + } + } + } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { // Enter key pressed + if (popup.isVisible()) { + if (list.getSelectedIndex()>=0) { + setPredefined(list.getSelectedValue()); + } + popup.setVisible(false); + e.consume(); + } + } + } + + @Override + public void keyReleased(KeyEvent e) { + String text = getText(); + if (!text.equals(lastText)) { + fillModel(text); + lastText = text; + setPredefined((String)null); + showPopup(); + } + } + }); + } + } + + public TextWidget(int columns) { + this(); + this.setColumns(columns); + } + + private void setPredefined(String value) { + if (value!=null) { + lastText = value; + setText(value); + } + if (!NullUtils.areEquals(predefined, value)) { + String oldPredefined = predefined; + predefined = value; + this.firePropertyChange(PREDEFINED_VALUE, oldPredefined, predefined); + } + } + + /** Sets the predefined values allowed by the field. + * @param array The predefined values. + * @see #setPredefined(String[], int) + */ + public void setPredefined(String[] array) { + setPredefined (array, Integer.MAX_VALUE); + } + + /** Sets the predefined values allowed by the field. + * @param array The predefined values. + * @param unsortedMax Maximum number of unsorted values. + *
The values can be divided in two groups (which will be separated by a thin line). + *
    + *
  1. A group of values, that matches the current typed text, sorted in the same order as in the array argument.
  2. + *
  3. A group of values, that matches the current typed text, sorted in alphabetical order.
  4. + *
+ *
This argument contains the size of the first group. Use Integer.MAX_VALUE to always display values in the same order as in the array. 0 to always sorted values. + */ + public void setPredefined(String[] array, int unsortedMax) { + installPopup(); + this.unsortedMax = unsortedMax; + this.proposals = array==null ? null : array.clone(); + fillModel(this.getText()); + } + + private void showPopup() { + if (list.getModel().getSize()!=0) { + Dimension size = popup.getPreferredSize(); + if (getWidth()>size.width) { + popup.setPreferredSize(new Dimension(getWidth(), size.height)); + } + popup.show(TextWidget.this, 0, getHeight()); + requestFocus(false); + } else { + popup.setVisible(false); + } + } + + private void fillModel(String text) { + TextMatcher matcher = new TextMatcher(TextMatcher.Kind.CONTAINS, text, false, false); //TODO Must match "starts with" ... to be implemented in TextMatcher + ArrayList okProbaSort = new ArrayList<>(); + TreeSet okAlphabeticSort = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + for (String value : this.proposals) { + if (matcher.matches(value)) { + if (okProbaSort.size()<=unsortedMax) { + okProbaSort.add(value); + } else { + okAlphabeticSort.add(value); + } + } + } + okProbaSort.addAll(okAlphabeticSort); + ((PopupListModel)list.getModel()).setValues(okProbaSort.toArray(new String[okProbaSort.size()])); + + if (0 != list.getSelectedIndex()) { + list.setSelectedIndex(0); + } + } + + private static final class PopupListModel extends AbstractListModel { + private static final long serialVersionUID = 1L; + String[] values; + + PopupListModel() { + this.values = new String[0]; + } + + @Override + public int getSize() { + return values.length; + } + + @Override + public String getElementAt(int index) { + return values[index]; + } + + public void setValues(String[] values) { + int n = this.values.length; + if (n>0) { + this.values = new String[0]; + fireIntervalRemoved(this, 0, n); + } + this.values = values.clone(); + fireIntervalAdded(this, 0, this.values.length); + } + } + + private class MyDocument extends PlainDocument { + private static final long serialVersionUID = 1L; + private boolean ignoreEvents = false; + + @Override + public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { + String oldValue = TextWidget.this.getText(); + this.ignoreEvents = true; + super.replace(offset, length, text, attrs); + this.ignoreEvents = false; + String newValue = TextWidget.this.getText(); + if (!oldValue.equals(newValue)) { + TextWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); + } + } + + @Override + public void remove(int offs, int len) throws BadLocationException { + String oldValue = TextWidget.this.getText(); + super.remove(offs, len); + String newValue = TextWidget.this.getText(); + if (!ignoreEvents && !oldValue.equals(newValue)) { + TextWidget.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue); + } + } + } + +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/CalendarWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/CalendarWidget.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/date/CalendarWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/CalendarWidget.java index e0e2e8b..07167a6 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/CalendarWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/CalendarWidget.java @@ -1,437 +1,437 @@ -/* This is a modified version of the original DateChooserPanel.java from JFree. - * Changes:
    - *
  • The month selection is implemented with buttons instead of the originals JComboBox. - * This allows the component to be used in a popup.
  • - *
  • The "today button" is now in the month selection panel and hasn't an non localized english label
  • - *
  • Null dates are now allowed (no date is selected in the panel)
  • - *
- * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - - * ======================================================================== - * JCommon: a free general purpose class library for the Java(tm) platform - * ======================================================================== - * - * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. - * - * Project Info: http://www.jfree.org/jcommon/index.html - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library 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 Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * - * [Java is a trademark or registered trademark of Sun Microsystems, Inc. - * in the United States and other countries.] - * - * --------------------- - * DateChooserPanel.java - * --------------------- - * (C) Copyright 2000-2004, by Object Refinery Limited. - * - * Original Author: David Gilbert (for Object Refinery Limited); - * Contributor(s): -; - * - * $Id: DateChooserPanel.java,v 1.11 2007/11/02 17:50:36 taqua Exp $ - * - * Changes (from 26-Oct-2001) - * -------------------------- - * 26-Oct-2001: Changed package to com.jrefinery.ui.* (DG); - * 08-Dec-2001: Dropped the getMonths() method (DG); - * 13-Oct-2002: Fixed errors reported by Checkstyle (DG); - * 02-Nov-2005: Fixed a bug where the current day-of-the-month is past - * the end of the newly selected month when the month or year - * combo boxes are changed - see bug id 1344319 (DG); - * - */ - -package com.fathzer.soft.ajlib.swing.widget.date; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Font; -import java.awt.GridLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.DateFormatSymbols; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.SwingConstants; - -import com.fathzer.soft.ajlib.utilities.NullUtils; - -/** - * A panel that allows the user to select a date. - * @author David Gilbert (modified by Jean-Marc Astesana) - */ -public class CalendarWidget extends JPanel { - private static final long serialVersionUID = 1L; - public static final String DATE_PROPERTY = "DATE_PROPERTY"; - - /** - * The date selected in the panel. - */ - private Calendar chosenDate; - - /** - * The color for the selected date. - */ - private Color chosenDateButtonColor; - - /** - * The color for dates in the current month. - */ - private Color chosenMonthButtonColor; - - /** - * The color for dates that are visible, but not in the current month. - */ - private Color chosenOtherButtonColor; - - /** - * The first day-of-the-week. - */ - private int firstDayOfWeek; - - /** - * The font used to display the date. - */ - private Float fontRatio = 0.85f; - - /** - * An array of buttons used to display the days-of-the-month. - */ - private JButton[] buttons; - - /** - * An array of labels used to display day of week names. - */ - private JLabel[] days; - - private MonthWidget monthSelector; - - /** - * The ordered set of all seven days of a week, beginning with the - * 'firstDayOfWeek'. - */ - private int[] weekDays; - - /** - * Constructs a new date chooser panel, using today's date as the initial - * selection. - */ - public CalendarWidget() { - super(new BorderLayout()); - - //TODO It would be cool to choose colors from the current Look and Feel ... but I didn't succeed in doing that :-( - this.chosenDateButtonColor = Color.red; - this.chosenMonthButtonColor = Color.white; - this.chosenOtherButtonColor = Color.gray; - - // the default date is today... - this.chosenDate = Calendar.getInstance(getLocale()); - initializeDays(); - - monthSelector = new MonthWidget(); - add(monthSelector, BorderLayout.NORTH); - add(getCalendarPanel(), BorderLayout.CENTER); - monthSelector.addPropertyChangeListener(MonthWidget.DATE_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - refreshButtons(); - } - }); - monthSelector.getNow().addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setDate(new Date()); - } - }); - refreshButtons(); - } - - @Override - public void setLocale(Locale l) { - super.setLocale(l); - monthSelector.setLocale(l); - initializeDays(); - updateDays(); - refreshButtons(); - } - - private void initializeDays() { - this.firstDayOfWeek = Calendar.getInstance(getLocale()).getFirstDayOfWeek(); - this.weekDays = new int[7]; - for (int i = 0; i < 7; i++) { - this.weekDays[i] = ((this.firstDayOfWeek + i - 1) % 7) + 1; - } - } - - /** - * Sets the date chosen in the panel. - * - * @param theDate - * the new date. - */ - public void setDate(final Date theDate) { - Date old = this.getDate(); - if (!NullUtils.areEquals(theDate, old)) { - if (theDate == null) { - this.chosenDate = null; - } else { - if (this.chosenDate == null) { - this.chosenDate = Calendar.getInstance(getLocale()); - } - this.chosenDate.setTime(theDate); - this.monthSelector.setMonth(theDate); - } - refreshButtons(); - this.firePropertyChange(DATE_PROPERTY, old, theDate); - } - } - - /** - * Returns the date selected in the panel. - * - * @return the selected date. - */ - public Date getDate() { - return this.chosenDate == null ? null : this.chosenDate.getTime(); - } - - /** - * Returns a panel of buttons, each button representing a day in the month. - * This is a sub-component of the DatePanel. - * - * @return the panel. - */ - private JPanel getCalendarPanel() { - final JPanel p = new JPanel(new GridLayout(7, 7)); - final DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(getLocale()); - final String[] weekDays = dateFormatSymbols.getShortWeekdays(); - this.days = new JLabel[this.weekDays.length]; - - for (int i = 0; i < this.weekDays.length; i++) { - this.days[i] = new JLabel(weekDays[this.weekDays[i]], SwingConstants.CENTER); - p.add(this.days[i]); - } - - this.buttons = new JButton[42]; - ActionListener listener = new ActionListener() { - /** - * Handles action-events from the date panel. - * @param e information about the event that occurred. - */ - public void actionPerformed(final ActionEvent e) { - final JButton b = (JButton) e.getSource(); - final int i = Integer.parseInt(b.getName()); - final Calendar cal = getFirstVisibleDate(); - cal.add(Calendar.DATE, i); - setDate(cal.getTime()); - } - }; - Font bFont = null; - for (int i = 0; i < 42; i++) { - final JButton b = new JButton(""); - if (bFont==null) { - bFont = b.getFont().deriveFont(fontRatio*b.getFont().getSize()); - } - b.setMargin(new Insets(1, 1, 1, 1)); - b.setName(Integer.toString(i)); - b.setFont(bFont); - b.setFocusPainted(false); - b.setActionCommand("dateButtonClicked"); - b.addActionListener(listener); - this.buttons[i] = b; - p.add(b); - } - return p; - } - - private void updateDays() { - final DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(getLocale()); - final String[] weekDaysWordings = dateFormatSymbols.getShortWeekdays(); - for (int i = 0; i < days.length; i++) { - days[i].setText(weekDaysWordings[this.weekDays[i]]); - } - } - - /** - * Returns true if the two dates are equal (time of day is ignored). - * - * @param c1 - * the first date. - * @param c2 - * the second date. - * @return boolean. - */ - private boolean equalDates(final Calendar c1, final Calendar c2) { - // Be aware that c1 or c2 can be null - if ((c1 == null) && (c2 == null)) { - return true; - } - if ((c1 == null) || (c2 == null)) { - return false; - } - return (c1.get(Calendar.DATE) == c2.get(Calendar.DATE)) && (c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH)) - && (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)); - } - - /** - * Returns the first date that is visible in the grid. This should always be - * in the month preceding the month of the selected date. - * - * @return the date. - */ - private Calendar getFirstVisibleDate() { - final Calendar c = Calendar.getInstance(getLocale()); - c.setTime(this.monthSelector.getMonth()); - c.add(Calendar.DATE, -1); - while (c.get(Calendar.DAY_OF_WEEK) != getFirstDayOfWeek()) { - c.add(Calendar.DATE, -1); - } - return c; - } - - /** - * Returns the first day of the week (controls the labels in the date panel). - * - * @return the first day of the week. - */ - private int getFirstDayOfWeek() { - return this.firstDayOfWeek; - } - - /** - * Update the button labels and colors to reflect date selection. - */ - private void refreshButtons() { - final Calendar c = getFirstVisibleDate(); - final Calendar current = Calendar.getInstance(getLocale()); - current.setTime(this.monthSelector.getMonth()); - for (int i = 0; i < 42; i++) { - final JButton b = this.buttons[i]; - setButtonAppearance(b, c, current); - c.add(Calendar.DATE, 1); - } - } - - /** Sets a date button appearance. - * @param button The button to be set up - * @param date The date represented by the button - * @param currentMonth The currently displayed month - */ - private void setButtonAppearance(JButton button, Calendar date, Calendar currentMonth) { - String day = Integer.toString(date.get(Calendar.DATE)); - Color color; - if (equalDates(date, this.chosenDate)) { - color = this.chosenDateButtonColor; - } else if ((date.get(Calendar.MONTH) == currentMonth.get(Calendar.MONTH)) - && (date.get(Calendar.YEAR) == currentMonth.get(Calendar.YEAR))) { - color = this.chosenMonthButtonColor; - } else { - color = this.chosenOtherButtonColor; - } - button.setBackground(color); - if (equalDates(date, Calendar.getInstance())) { - day = ""+day+""; - } - button.setText(day); - } - - /** - * Returns the color for the currently selected date. - * - * @return a color. - */ - public Color getChosenDateButtonColor() { - return this.chosenDateButtonColor; - } - - /** - * Redefines the color for the currently selected date. - * - * @param chosenDateButtonColor - * the new color - */ - public void setChosenDateButtonColor(final Color chosenDateButtonColor) { - checkButtonColor(chosenDateButtonColor); - final Color oldValue = this.chosenDateButtonColor; - this.chosenDateButtonColor = chosenDateButtonColor; - refreshButtons(); - firePropertyChange("chosenDateButtonColor", oldValue, chosenDateButtonColor); - } - - /** - * Returns the color for the buttons representing the current month. - * - * @return the color for the current month. - */ - public Color getChosenMonthButtonColor() { - return this.chosenMonthButtonColor; - } - - /** - * Defines the color for the buttons representing the current month. - * - * @param chosenMonthButtonColor - * the color for the current month. - */ - public void setChosenMonthButtonColor(final Color chosenMonthButtonColor) { - checkButtonColor(chosenMonthButtonColor); - final Color oldValue = this.chosenMonthButtonColor; - this.chosenMonthButtonColor = chosenMonthButtonColor; - refreshButtons(); - firePropertyChange("chosenMonthButtonColor", oldValue, chosenMonthButtonColor); - } - - /** - * Returns the color for the buttons representing the other months. - * - * @return a color. - */ - public Color getChosenOtherButtonColor() { - return this.chosenOtherButtonColor; - } - - /** - * Redefines the color for the buttons representing the other months. - * - * @param chosenOtherButtonColor - * a color. - */ - public void setChosenOtherButtonColor(final Color chosenOtherButtonColor) { - checkButtonColor(chosenOtherButtonColor); - final Color oldValue = this.chosenOtherButtonColor; - this.chosenOtherButtonColor = chosenOtherButtonColor; - refreshButtons(); - firePropertyChange("chosenOtherButtonColor", oldValue, chosenOtherButtonColor); - } - - private static void checkButtonColor(Color color) { - if (color == null) { - throw new NullPointerException("UIColor must not be null."); - } - } -} +/* This is a modified version of the original DateChooserPanel.java from JFree. + * Changes:
    + *
  • The month selection is implemented with buttons instead of the originals JComboBox. + * This allows the component to be used in a popup.
  • + *
  • The "today button" is now in the month selection panel and hasn't an non localized english label
  • + *
  • Null dates are now allowed (no date is selected in the panel)
  • + *
+ * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + + * ======================================================================== + * JCommon: a free general purpose class library for the Java(tm) platform + * ======================================================================== + * + * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. + * + * Project Info: http://www.jfree.org/jcommon/index.html + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * [Java is a trademark or registered trademark of Sun Microsystems, Inc. + * in the United States and other countries.] + * + * --------------------- + * DateChooserPanel.java + * --------------------- + * (C) Copyright 2000-2004, by Object Refinery Limited. + * + * Original Author: David Gilbert (for Object Refinery Limited); + * Contributor(s): -; + * + * $Id: DateChooserPanel.java,v 1.11 2007/11/02 17:50:36 taqua Exp $ + * + * Changes (from 26-Oct-2001) + * -------------------------- + * 26-Oct-2001: Changed package to com.jrefinery.ui.* (DG); + * 08-Dec-2001: Dropped the getMonths() method (DG); + * 13-Oct-2002: Fixed errors reported by Checkstyle (DG); + * 02-Nov-2005: Fixed a bug where the current day-of-the-month is past + * the end of the newly selected month when the month or year + * combo boxes are changed - see bug id 1344319 (DG); + * + */ + +package com.fathzer.soft.ajlib.swing.widget.date; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.DateFormatSymbols; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; + +import com.fathzer.soft.ajlib.utilities.NullUtils; + +/** + * A panel that allows the user to select a date. + * @author David Gilbert (modified by Jean-Marc Astesana) + */ +public class CalendarWidget extends JPanel { + private static final long serialVersionUID = 1L; + public static final String DATE_PROPERTY = "DATE_PROPERTY"; + + /** + * The date selected in the panel. + */ + private Calendar chosenDate; + + /** + * The color for the selected date. + */ + private Color chosenDateButtonColor; + + /** + * The color for dates in the current month. + */ + private Color chosenMonthButtonColor; + + /** + * The color for dates that are visible, but not in the current month. + */ + private Color chosenOtherButtonColor; + + /** + * The first day-of-the-week. + */ + private int firstDayOfWeek; + + /** + * The font used to display the date. + */ + private Float fontRatio = 0.85f; + + /** + * An array of buttons used to display the days-of-the-month. + */ + private JButton[] buttons; + + /** + * An array of labels used to display day of week names. + */ + private JLabel[] days; + + private MonthWidget monthSelector; + + /** + * The ordered set of all seven days of a week, beginning with the + * 'firstDayOfWeek'. + */ + private int[] weekDays; + + /** + * Constructs a new date chooser panel, using today's date as the initial + * selection. + */ + public CalendarWidget() { + super(new BorderLayout()); + + //TODO It would be cool to choose colors from the current Look and Feel ... but I didn't succeed in doing that :-( + this.chosenDateButtonColor = Color.red; + this.chosenMonthButtonColor = Color.white; + this.chosenOtherButtonColor = Color.gray; + + // the default date is today... + this.chosenDate = Calendar.getInstance(getLocale()); + initializeDays(); + + monthSelector = new MonthWidget(); + add(monthSelector, BorderLayout.NORTH); + add(getCalendarPanel(), BorderLayout.CENTER); + monthSelector.addPropertyChangeListener(MonthWidget.DATE_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + refreshButtons(); + } + }); + monthSelector.getNow().addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setDate(new Date()); + } + }); + refreshButtons(); + } + + @Override + public void setLocale(Locale l) { + super.setLocale(l); + monthSelector.setLocale(l); + initializeDays(); + updateDays(); + refreshButtons(); + } + + private void initializeDays() { + this.firstDayOfWeek = Calendar.getInstance(getLocale()).getFirstDayOfWeek(); + this.weekDays = new int[7]; + for (int i = 0; i < 7; i++) { + this.weekDays[i] = ((this.firstDayOfWeek + i - 1) % 7) + 1; + } + } + + /** + * Sets the date chosen in the panel. + * + * @param theDate + * the new date. + */ + public void setDate(final Date theDate) { + Date old = this.getDate(); + if (!NullUtils.areEquals(theDate, old)) { + if (theDate == null) { + this.chosenDate = null; + } else { + if (this.chosenDate == null) { + this.chosenDate = Calendar.getInstance(getLocale()); + } + this.chosenDate.setTime(theDate); + this.monthSelector.setMonth(theDate); + } + refreshButtons(); + this.firePropertyChange(DATE_PROPERTY, old, theDate); + } + } + + /** + * Returns the date selected in the panel. + * + * @return the selected date. + */ + public Date getDate() { + return this.chosenDate == null ? null : this.chosenDate.getTime(); + } + + /** + * Returns a panel of buttons, each button representing a day in the month. + * This is a sub-component of the DatePanel. + * + * @return the panel. + */ + private JPanel getCalendarPanel() { + final JPanel p = new JPanel(new GridLayout(7, 7)); + final DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(getLocale()); + final String[] weekDays = dateFormatSymbols.getShortWeekdays(); + this.days = new JLabel[this.weekDays.length]; + + for (int i = 0; i < this.weekDays.length; i++) { + this.days[i] = new JLabel(weekDays[this.weekDays[i]], SwingConstants.CENTER); + p.add(this.days[i]); + } + + this.buttons = new JButton[42]; + ActionListener listener = new ActionListener() { + /** + * Handles action-events from the date panel. + * @param e information about the event that occurred. + */ + public void actionPerformed(final ActionEvent e) { + final JButton b = (JButton) e.getSource(); + final int i = Integer.parseInt(b.getName()); + final Calendar cal = getFirstVisibleDate(); + cal.add(Calendar.DATE, i); + setDate(cal.getTime()); + } + }; + Font bFont = null; + for (int i = 0; i < 42; i++) { + final JButton b = new JButton(""); + if (bFont==null) { + bFont = b.getFont().deriveFont(fontRatio*b.getFont().getSize()); + } + b.setMargin(new Insets(1, 1, 1, 1)); + b.setName(Integer.toString(i)); + b.setFont(bFont); + b.setFocusPainted(false); + b.setActionCommand("dateButtonClicked"); + b.addActionListener(listener); + this.buttons[i] = b; + p.add(b); + } + return p; + } + + private void updateDays() { + final DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(getLocale()); + final String[] weekDaysWordings = dateFormatSymbols.getShortWeekdays(); + for (int i = 0; i < days.length; i++) { + days[i].setText(weekDaysWordings[this.weekDays[i]]); + } + } + + /** + * Returns true if the two dates are equal (time of day is ignored). + * + * @param c1 + * the first date. + * @param c2 + * the second date. + * @return boolean. + */ + private boolean equalDates(final Calendar c1, final Calendar c2) { + // Be aware that c1 or c2 can be null + if ((c1 == null) && (c2 == null)) { + return true; + } + if ((c1 == null) || (c2 == null)) { + return false; + } + return (c1.get(Calendar.DATE) == c2.get(Calendar.DATE)) && (c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH)) + && (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)); + } + + /** + * Returns the first date that is visible in the grid. This should always be + * in the month preceding the month of the selected date. + * + * @return the date. + */ + private Calendar getFirstVisibleDate() { + final Calendar c = Calendar.getInstance(getLocale()); + c.setTime(this.monthSelector.getMonth()); + c.add(Calendar.DATE, -1); + while (c.get(Calendar.DAY_OF_WEEK) != getFirstDayOfWeek()) { + c.add(Calendar.DATE, -1); + } + return c; + } + + /** + * Returns the first day of the week (controls the labels in the date panel). + * + * @return the first day of the week. + */ + private int getFirstDayOfWeek() { + return this.firstDayOfWeek; + } + + /** + * Update the button labels and colors to reflect date selection. + */ + private void refreshButtons() { + final Calendar c = getFirstVisibleDate(); + final Calendar current = Calendar.getInstance(getLocale()); + current.setTime(this.monthSelector.getMonth()); + for (int i = 0; i < 42; i++) { + final JButton b = this.buttons[i]; + setButtonAppearance(b, c, current); + c.add(Calendar.DATE, 1); + } + } + + /** Sets a date button appearance. + * @param button The button to be set up + * @param date The date represented by the button + * @param currentMonth The currently displayed month + */ + private void setButtonAppearance(JButton button, Calendar date, Calendar currentMonth) { + String day = Integer.toString(date.get(Calendar.DATE)); + Color color; + if (equalDates(date, this.chosenDate)) { + color = this.chosenDateButtonColor; + } else if ((date.get(Calendar.MONTH) == currentMonth.get(Calendar.MONTH)) + && (date.get(Calendar.YEAR) == currentMonth.get(Calendar.YEAR))) { + color = this.chosenMonthButtonColor; + } else { + color = this.chosenOtherButtonColor; + } + button.setBackground(color); + if (equalDates(date, Calendar.getInstance())) { + day = ""+day+""; + } + button.setText(day); + } + + /** + * Returns the color for the currently selected date. + * + * @return a color. + */ + public Color getChosenDateButtonColor() { + return this.chosenDateButtonColor; + } + + /** + * Redefines the color for the currently selected date. + * + * @param chosenDateButtonColor + * the new color + */ + public void setChosenDateButtonColor(final Color chosenDateButtonColor) { + checkButtonColor(chosenDateButtonColor); + final Color oldValue = this.chosenDateButtonColor; + this.chosenDateButtonColor = chosenDateButtonColor; + refreshButtons(); + firePropertyChange("chosenDateButtonColor", oldValue, chosenDateButtonColor); + } + + /** + * Returns the color for the buttons representing the current month. + * + * @return the color for the current month. + */ + public Color getChosenMonthButtonColor() { + return this.chosenMonthButtonColor; + } + + /** + * Defines the color for the buttons representing the current month. + * + * @param chosenMonthButtonColor + * the color for the current month. + */ + public void setChosenMonthButtonColor(final Color chosenMonthButtonColor) { + checkButtonColor(chosenMonthButtonColor); + final Color oldValue = this.chosenMonthButtonColor; + this.chosenMonthButtonColor = chosenMonthButtonColor; + refreshButtons(); + firePropertyChange("chosenMonthButtonColor", oldValue, chosenMonthButtonColor); + } + + /** + * Returns the color for the buttons representing the other months. + * + * @return a color. + */ + public Color getChosenOtherButtonColor() { + return this.chosenOtherButtonColor; + } + + /** + * Redefines the color for the buttons representing the other months. + * + * @param chosenOtherButtonColor + * a color. + */ + public void setChosenOtherButtonColor(final Color chosenOtherButtonColor) { + checkButtonColor(chosenOtherButtonColor); + final Color oldValue = this.chosenOtherButtonColor; + this.chosenOtherButtonColor = chosenOtherButtonColor; + refreshButtons(); + firePropertyChange("chosenOtherButtonColor", oldValue, chosenOtherButtonColor); + } + + private static void checkButtonColor(Color color) { + if (color == null) { + throw new NullPointerException("UIColor must not be null."); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateField.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateField.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateField.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateField.java index 63bf027..fe00f18 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateField.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateField.java @@ -1,257 +1,257 @@ -package com.fathzer.soft.ajlib.swing.widget.date; - -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; - -import com.fathzer.soft.ajlib.swing.widget.TextWidget; -import com.fathzer.soft.ajlib.utilities.CoolDateFormatter; -import com.fathzer.soft.ajlib.utilities.NullUtils; - -/** This class allows the user to just enter a day, or a day and a month, instead of a complete date (day, month, year). - * It auto completes the typed date with the current month and year. - * This field allows you to define what an empty field means. By default, an empty field means a null date, but, using the - * setEmptyDate method, you can change this behavior. - * This field has an inputVerifier in order to prevent entering something that is not a date in the field. By default, an empty - * field is allowed, you can change that calling setIsEmptyNullDateValid. Keep in mind that if you called the setEmptyDate method - * with a non null argument, the empty field will always be valid. - * The up/down arrow keys increments/decrements the date. - */ -public class DateField extends TextWidget { - private static final long serialVersionUID = 1L; - public static final String DATE_PROPERTY = "date"; - public static final String CONTENT_VALID_PROPERTY = "contentValid"; - - private CoolDateFormatter formatter; - private Date date; - private boolean valid; - private Date emptyValue; - private boolean isEmptyNullDateValid; - - /** Constructor. - * Creates a new Date widget. The date is set to today, the empty date is set to null. - * @see #setEmptyDate(Date) - */ - public DateField() { - this(null); - } - - /** Constructor. - * Creates a new Date widget. The date is set to today. - * @param emptyDate The date to be set if the field becomes empty - * @see #setEmptyDate(Date) - */ - public DateField(Date emptyDate) { - super(); - this.setColumns(7); - this.isEmptyNullDateValid = true; - this.emptyValue = emptyDate; - formatter = new CoolDateFormatter(getLocale()); - // Set the field to today's date - Calendar calendar = Calendar.getInstance(getLocale()); - CoolDateFormatter.eraseTime(calendar); - this.setDate(calendar.getTime()); - this.addKeyListener(new KeyAdapter() { - @Override - public void keyTyped(KeyEvent e) { - // Nothing to do, all the job is done in keyPressed - } - - @Override - public void keyPressed(KeyEvent e) { - int increment = 0; - if (e.getKeyCode()==KeyEvent.VK_DOWN) { - // Set the date to next day after the current date - increment = -1; - } else if (e.getKeyCode()==KeyEvent.VK_UP) { - // Set the date to previous day before the current date - increment = 1; - } - if ((increment!=0) && (getDate()!=null)) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(getDate()); - calendar.add(Calendar.DATE, increment); - setDate(calendar.getTime()); - } - } - - }); - this.addPropertyChangeListener(TEXT_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - updateDate(); - } - }); - this.addFocusListener(new FocusListener() { - @Override - public void focusLost(FocusEvent e) { - if (!e.isTemporary()) { - DateField.super.setText(date==null?"":formatter.format(date)); - } - } - @Override - public void focusGained(FocusEvent e) { - // Nothing to do - } - }); - } - - @Override - public void setLocale(Locale locale) { - super.setLocale(locale); - this.formatter = new CoolDateFormatter(locale); - if (date!=null) { - this.setText(formatter.format(date)); - } - } - - /** Set the meaning of an empty field. - * @param date The date that a is equivalent to an empty field. - * By default, this date is null. - */ - public void setEmptyDate(Date date) { - this.emptyValue = date; - if (this.getText().trim().length()==0) { - updateDate(); - } - } - - /** Allow/Disallow this field to be empty (if it means a null date). - *
Keep in mind that a non null date for an empty field is always valid. - * @param valid true if null is a valid date. - * @see #setEmptyDate(Date) - */ - public void setIsEmptyNullDateIsValid(boolean valid) { - this.isEmptyNullDateValid = valid; - if (this.getText().trim().isEmpty()) { - updateDate(); - } - } - - private static Date parseRelativeDate(CharSequence text) { - Date result = null; - if (text.length()>1) { - char c = text.charAt(0); - if (c=='+' || c=='-') { - int count = 1; - while (text.length()>count && text.charAt(count)==c) { - count++; - } - String num =text.toString().substring(count); - if (count<=3 && num.matches("\\d+")) { - int nb = Integer.parseInt(num); - GregorianCalendar cal = new GregorianCalendar(); - cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DATE), 0,0,0); - cal.add(new int[]{Calendar.DATE, Calendar.MONTH, Calendar.YEAR}[count-1], c=='+'?nb:-nb); - return cal.getTime(); - } - } - } - return result; - } - - @SuppressWarnings("deprecation") - private void updateDate() { - String text = this.getText().trim(); - if (text.isEmpty()) { - internalSetDate(emptyValue, (emptyValue!=null) || isEmptyNullDateValid); - } else { - Date changed = parseRelativeDate(text); - if (changed==null) { - try { - changed = formatter.parse(text); - int year = changed.getYear()+1900; - if (year<10) { - // When the user enters a date with only one char for the year, it is interpreted as the full year (ie 9 -> year 9) - // So, we have to add the right century to this year - SimpleDateFormat simpleFormat = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT); - Date formatterStartYear = simpleFormat.get2DigitYearStart(); - year += ((formatterStartYear.getYear()+1900)/100)*100; - changed.setYear(year-1900); - // If that date is not in the 100 year period of the formatter, add one century - // Note: I compare the getTime() results, because, sometime, an exception is thrown that tells that instances are not of the same class - if (changed.getTime()-formatterStartYear.getTime()<0) { - changed.setYear(year-1800); - } - } - } catch (ParseException e) { - try { - int day = Integer.parseInt(text); - GregorianCalendar today = new GregorianCalendar(); - if ((day>0) && (day<=today.getActualMaximum(Calendar.DAY_OF_MONTH))) { - changed = new GregorianCalendar(today.get(Calendar.YEAR),today.get(Calendar.MONTH),day).getTime(); - } - } catch (NumberFormatException e1) { - try { - text = text+"/"+new GregorianCalendar().get(Calendar.YEAR); - changed = formatter.parse(text+"/"+new GregorianCalendar().get(Calendar.YEAR)); - } catch (ParseException e2) { - } - } - } - } - internalSetDate(changed, changed!=null); - } - } - - /** Get the widget current date. - * @return The date. If the value contained by the field is not valid, returns null. - */ - public Date getDate() { - return this.date; - } - - /** Set the widget date. - * The text field is updated. - * @param date The date to set. - */ - public void setDate(Date date) { - super.setText(date==null?"":formatter.format(date)); - if (date==null) { - date = emptyValue; - } - internalSetDate(date, date!=null || isEmptyNullDateValid); - } - - @Override - public void setText(String t) { - super.setText(t); - updateDate(); - } - - /** Set the date without changing the content of the TextField. - * This method does nothing if the date is equals to the current widget date. - * @param date - * @return true if the value was changed - */ - private boolean internalSetDate(Date date, boolean isValid) { - boolean changed = ! NullUtils.areEquals(date, this.date); - if (changed) { - Date old = this.date; - this.date = date; - firePropertyChange(DATE_PROPERTY, old, date); - } - if (isValid!=this.valid) { - this.valid = isValid; - firePropertyChange(CONTENT_VALID_PROPERTY, !this.valid, this.valid); - } - return changed; - } - - /** Gets the content validity. - * @return true if the content is valid, false if it is not. - */ - public boolean isContentValid() { - return this.valid; - } -} +package com.fathzer.soft.ajlib.swing.widget.date; + +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; + +import com.fathzer.soft.ajlib.swing.widget.TextWidget; +import com.fathzer.soft.ajlib.utilities.CoolDateFormatter; +import com.fathzer.soft.ajlib.utilities.NullUtils; + +/** This class allows the user to just enter a day, or a day and a month, instead of a complete date (day, month, year). + * It auto completes the typed date with the current month and year. + * This field allows you to define what an empty field means. By default, an empty field means a null date, but, using the + * setEmptyDate method, you can change this behavior. + * This field has an inputVerifier in order to prevent entering something that is not a date in the field. By default, an empty + * field is allowed, you can change that calling setIsEmptyNullDateValid. Keep in mind that if you called the setEmptyDate method + * with a non null argument, the empty field will always be valid. + * The up/down arrow keys increments/decrements the date. + */ +public class DateField extends TextWidget { + private static final long serialVersionUID = 1L; + public static final String DATE_PROPERTY = "date"; + public static final String CONTENT_VALID_PROPERTY = "contentValid"; + + private CoolDateFormatter formatter; + private Date date; + private boolean valid; + private Date emptyValue; + private boolean isEmptyNullDateValid; + + /** Constructor. + * Creates a new Date widget. The date is set to today, the empty date is set to null. + * @see #setEmptyDate(Date) + */ + public DateField() { + this(null); + } + + /** Constructor. + * Creates a new Date widget. The date is set to today. + * @param emptyDate The date to be set if the field becomes empty + * @see #setEmptyDate(Date) + */ + public DateField(Date emptyDate) { + super(); + this.setColumns(7); + this.isEmptyNullDateValid = true; + this.emptyValue = emptyDate; + formatter = new CoolDateFormatter(getLocale()); + // Set the field to today's date + Calendar calendar = Calendar.getInstance(getLocale()); + CoolDateFormatter.eraseTime(calendar); + this.setDate(calendar.getTime()); + this.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + // Nothing to do, all the job is done in keyPressed + } + + @Override + public void keyPressed(KeyEvent e) { + int increment = 0; + if (e.getKeyCode()==KeyEvent.VK_DOWN) { + // Set the date to next day after the current date + increment = -1; + } else if (e.getKeyCode()==KeyEvent.VK_UP) { + // Set the date to previous day before the current date + increment = 1; + } + if ((increment!=0) && (getDate()!=null)) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(getDate()); + calendar.add(Calendar.DATE, increment); + setDate(calendar.getTime()); + } + } + + }); + this.addPropertyChangeListener(TEXT_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + updateDate(); + } + }); + this.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (!e.isTemporary()) { + DateField.super.setText(date==null?"":formatter.format(date)); + } + } + @Override + public void focusGained(FocusEvent e) { + // Nothing to do + } + }); + } + + @Override + public void setLocale(Locale locale) { + super.setLocale(locale); + this.formatter = new CoolDateFormatter(locale); + if (date!=null) { + this.setText(formatter.format(date)); + } + } + + /** Set the meaning of an empty field. + * @param date The date that a is equivalent to an empty field. + * By default, this date is null. + */ + public void setEmptyDate(Date date) { + this.emptyValue = date; + if (this.getText().trim().length()==0) { + updateDate(); + } + } + + /** Allow/Disallow this field to be empty (if it means a null date). + *
Keep in mind that a non null date for an empty field is always valid. + * @param valid true if null is a valid date. + * @see #setEmptyDate(Date) + */ + public void setIsEmptyNullDateIsValid(boolean valid) { + this.isEmptyNullDateValid = valid; + if (this.getText().trim().isEmpty()) { + updateDate(); + } + } + + private static Date parseRelativeDate(CharSequence text) { + Date result = null; + if (text.length()>1) { + char c = text.charAt(0); + if (c=='+' || c=='-') { + int count = 1; + while (text.length()>count && text.charAt(count)==c) { + count++; + } + String num =text.toString().substring(count); + if (count<=3 && num.matches("\\d+")) { + int nb = Integer.parseInt(num); + GregorianCalendar cal = new GregorianCalendar(); + cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DATE), 0,0,0); + cal.add(new int[]{Calendar.DATE, Calendar.MONTH, Calendar.YEAR}[count-1], c=='+'?nb:-nb); + return cal.getTime(); + } + } + } + return result; + } + + @SuppressWarnings("deprecation") + private void updateDate() { + String text = this.getText().trim(); + if (text.isEmpty()) { + internalSetDate(emptyValue, (emptyValue!=null) || isEmptyNullDateValid); + } else { + Date changed = parseRelativeDate(text); + if (changed==null) { + try { + changed = formatter.parse(text); + int year = changed.getYear()+1900; + if (year<10) { + // When the user enters a date with only one char for the year, it is interpreted as the full year (ie 9 -> year 9) + // So, we have to add the right century to this year + SimpleDateFormat simpleFormat = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT); + Date formatterStartYear = simpleFormat.get2DigitYearStart(); + year += ((formatterStartYear.getYear()+1900)/100)*100; + changed.setYear(year-1900); + // If that date is not in the 100 year period of the formatter, add one century + // Note: I compare the getTime() results, because, sometime, an exception is thrown that tells that instances are not of the same class + if (changed.getTime()-formatterStartYear.getTime()<0) { + changed.setYear(year-1800); + } + } + } catch (ParseException e) { + try { + int day = Integer.parseInt(text); + GregorianCalendar today = new GregorianCalendar(); + if ((day>0) && (day<=today.getActualMaximum(Calendar.DAY_OF_MONTH))) { + changed = new GregorianCalendar(today.get(Calendar.YEAR),today.get(Calendar.MONTH),day).getTime(); + } + } catch (NumberFormatException e1) { + try { + text = text+"/"+new GregorianCalendar().get(Calendar.YEAR); + changed = formatter.parse(text+"/"+new GregorianCalendar().get(Calendar.YEAR)); + } catch (ParseException e2) { + } + } + } + } + internalSetDate(changed, changed!=null); + } + } + + /** Get the widget current date. + * @return The date. If the value contained by the field is not valid, returns null. + */ + public Date getDate() { + return this.date; + } + + /** Set the widget date. + * The text field is updated. + * @param date The date to set. + */ + public void setDate(Date date) { + super.setText(date==null?"":formatter.format(date)); + if (date==null) { + date = emptyValue; + } + internalSetDate(date, date!=null || isEmptyNullDateValid); + } + + @Override + public void setText(String t) { + super.setText(t); + updateDate(); + } + + /** Set the date without changing the content of the TextField. + * This method does nothing if the date is equals to the current widget date. + * @param date + * @return true if the value was changed + */ + private boolean internalSetDate(Date date, boolean isValid) { + boolean changed = ! NullUtils.areEquals(date, this.date); + if (changed) { + Date old = this.date; + this.date = date; + firePropertyChange(DATE_PROPERTY, old, date); + } + if (isValid!=this.valid) { + this.valid = isValid; + firePropertyChange(CONTENT_VALID_PROPERTY, !this.valid, this.valid); + } + return changed; + } + + /** Gets the content validity. + * @return true if the content is valid, false if it is not. + */ + public boolean isContentValid() { + return this.valid; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateWidget.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateWidget.java index 19841f9..54404d0 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/DateWidget.java @@ -1,171 +1,171 @@ -package com.fathzer.soft.ajlib.swing.widget.date; - -import java.awt.GridBagLayout; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import java.awt.GridBagConstraints; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Date; -import java.util.Locale; - -import javax.swing.JLabel; - -import com.fathzer.soft.ajlib.swing.Utils; - - -/** This panel contains a DateField and a button that shows a calendar popup. - * As this widget (especially the DateWidget it contains) represents years with two digits, it can only represent dates near today (ie, impossible to represent a date before 1900) - * @see DateField - * @see CalendarWidget - */ -public class DateWidget extends JPanel { - private static final long serialVersionUID = 1L; - /** Date change property name. - * The panel is a bean. Every time the chosen date changed, it sends a PropertyChangedEvent. - */ - public static final String DATE_PROPERTY = DateField.DATE_PROPERTY; // @jve:decl-index=0: - /** Content validity property name. - * The panel is a bean. Every time the content validity changed, it sends a PropertyChangedEvent. - */ - public static final String CONTENT_VALID_PROPERTY = DateField.CONTENT_VALID_PROPERTY; - - private DateField dateField = null; - private CalendarWidget dateChooser; - private JPopupMenu popup; - private JLabel jLabel = null; - - /** - * This is the default constructor. - * Creates a new panel with the system default locale. - * The date is set to today - */ - public DateWidget() { - super(); - popup = new JPopupMenu(); - dateChooser = new CalendarWidget(); - popup.add(dateChooser); - initialize(); - dateChooser.addPropertyChangeListener(CalendarWidget.DATE_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - getDateField().setDate((Date)evt.getNewValue()); - popup.setVisible(false); - } - }); - } - - /** Get the currently chosen date. - * @return the currently chosen date (null if the date is wrong). It is guaranteed that the hours, minutes, seconds, - * milliseconds of the date are set to 0. - */ - public Date getDate() { - return getDateField().getDate(); - } - - /** Set the currently chosen date. - * @param date the date to be set. - */ - public void setDate(Date date) { - getDateField().setDate(date); - // No need to fire a property change. - // The change property event will be sent by the property change listener - // that is waiting for change of the DateWidget - } - - /** Set the number of columns of the date text field. - * @param nb number of columns of the text field - */ - public void setColumns(int nb) { - this.getDateField().setColumns(nb); - } - - /** Set the locale. - * Changes the calendar popup appearence and the text field format. - */ - @Override - public void setLocale(Locale locale) { - getDateField().setLocale(locale); - dateChooser.setLocale(locale); - } - - /** Set the DateWidget tooltip. - * @param text the new tooltip text - */ - @Override - public void setToolTipText(String text) { - getDateField().setToolTipText(text); - } - - /** - * This method initializes this - */ - private void initialize() { - GridBagConstraints gridBagConstraints11 = new GridBagConstraints(); - gridBagConstraints11.gridx = 1; - gridBagConstraints11.fill = GridBagConstraints.VERTICAL; - gridBagConstraints11.gridy = 0; - jLabel = new JLabel(); - jLabel.setIcon(Utils.createIcon(DateWidget.class.getResource(MonthWidget.RES_PATH+"calendar.png"), 16*getFont().getSize()/12)); - jLabel.addMouseListener(new java.awt.event.MouseAdapter() { - @Override - public void mouseClicked(java.awt.event.MouseEvent e) { - if (jLabel.isEnabled() && !popup.isVisible()) { - DateField widget = getDateField(); - dateChooser.setDate(widget.getDate()); - popup.show(widget, 0, widget.getHeight()); - } - } - }); - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.fill = GridBagConstraints.BOTH; - gridBagConstraints.gridy = 0; - gridBagConstraints.weightx = 1.0; - gridBagConstraints.gridx = 0; - this.setLayout(new GridBagLayout()); - this.add(getDateField(), gridBagConstraints); - this.add(jLabel, gridBagConstraints11); - } - - /** - * Gets the DateWidget used by this component. - * @return a DateWidget instance - */ - public DateField getDateField() { - if (dateField == null) { - dateField = new DateField(); - PropertyChangeListener listener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); - } - }; - dateField.addPropertyChangeListener(DateField.DATE_PROPERTY, listener); - dateField.addPropertyChangeListener(DateField.CONTENT_VALID_PROPERTY, listener); - } - return dateField; - } - - /** Sets the validity of null date. - * @param valid true if null should be considered valid. - * @see DateField#setIsEmptyNullDateIsValid(boolean) - */ - public void setIsEmptyNullDateIsValid(boolean valid) { - this.getDateField().setIsEmptyNullDateIsValid(valid); - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - dateField.setEnabled(enabled); - jLabel.setEnabled(enabled); - } - - /** Gets the content validity. - * @return true if the content is valid, false if it is not. - */ - public boolean isContentValid() { - return this.dateField.isContentValid(); - } -} +package com.fathzer.soft.ajlib.swing.widget.date; + +import java.awt.GridBagLayout; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; + +import java.awt.GridBagConstraints; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Date; +import java.util.Locale; + +import javax.swing.JLabel; + +import com.fathzer.soft.ajlib.swing.Utils; + + +/** This panel contains a DateField and a button that shows a calendar popup. + * As this widget (especially the DateWidget it contains) represents years with two digits, it can only represent dates near today (ie, impossible to represent a date before 1900) + * @see DateField + * @see CalendarWidget + */ +public class DateWidget extends JPanel { + private static final long serialVersionUID = 1L; + /** Date change property name. + * The panel is a bean. Every time the chosen date changed, it sends a PropertyChangedEvent. + */ + public static final String DATE_PROPERTY = DateField.DATE_PROPERTY; // @jve:decl-index=0: + /** Content validity property name. + * The panel is a bean. Every time the content validity changed, it sends a PropertyChangedEvent. + */ + public static final String CONTENT_VALID_PROPERTY = DateField.CONTENT_VALID_PROPERTY; + + private DateField dateField = null; + private CalendarWidget dateChooser; + private JPopupMenu popup; + private JLabel jLabel = null; + + /** + * This is the default constructor. + * Creates a new panel with the system default locale. + * The date is set to today + */ + public DateWidget() { + super(); + popup = new JPopupMenu(); + dateChooser = new CalendarWidget(); + popup.add(dateChooser); + initialize(); + dateChooser.addPropertyChangeListener(CalendarWidget.DATE_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + getDateField().setDate((Date)evt.getNewValue()); + popup.setVisible(false); + } + }); + } + + /** Get the currently chosen date. + * @return the currently chosen date (null if the date is wrong). It is guaranteed that the hours, minutes, seconds, + * milliseconds of the date are set to 0. + */ + public Date getDate() { + return getDateField().getDate(); + } + + /** Set the currently chosen date. + * @param date the date to be set. + */ + public void setDate(Date date) { + getDateField().setDate(date); + // No need to fire a property change. + // The change property event will be sent by the property change listener + // that is waiting for change of the DateWidget + } + + /** Set the number of columns of the date text field. + * @param nb number of columns of the text field + */ + public void setColumns(int nb) { + this.getDateField().setColumns(nb); + } + + /** Set the locale. + * Changes the calendar popup appearence and the text field format. + */ + @Override + public void setLocale(Locale locale) { + getDateField().setLocale(locale); + dateChooser.setLocale(locale); + } + + /** Set the DateWidget tooltip. + * @param text the new tooltip text + */ + @Override + public void setToolTipText(String text) { + getDateField().setToolTipText(text); + } + + /** + * This method initializes this + */ + private void initialize() { + GridBagConstraints gridBagConstraints11 = new GridBagConstraints(); + gridBagConstraints11.gridx = 1; + gridBagConstraints11.fill = GridBagConstraints.VERTICAL; + gridBagConstraints11.gridy = 0; + jLabel = new JLabel(); + jLabel.setIcon(Utils.createIcon(DateWidget.class.getResource(MonthWidget.RES_PATH+"calendar.png"), 16*getFont().getSize()/12)); + jLabel.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mouseClicked(java.awt.event.MouseEvent e) { + if (jLabel.isEnabled() && !popup.isVisible()) { + DateField widget = getDateField(); + dateChooser.setDate(widget.getDate()); + popup.show(widget, 0, widget.getHeight()); + } + } + }); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagConstraints.gridy = 0; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.gridx = 0; + this.setLayout(new GridBagLayout()); + this.add(getDateField(), gridBagConstraints); + this.add(jLabel, gridBagConstraints11); + } + + /** + * Gets the DateWidget used by this component. + * @return a DateWidget instance + */ + public DateField getDateField() { + if (dateField == null) { + dateField = new DateField(); + PropertyChangeListener listener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); + } + }; + dateField.addPropertyChangeListener(DateField.DATE_PROPERTY, listener); + dateField.addPropertyChangeListener(DateField.CONTENT_VALID_PROPERTY, listener); + } + return dateField; + } + + /** Sets the validity of null date. + * @param valid true if null should be considered valid. + * @see DateField#setIsEmptyNullDateIsValid(boolean) + */ + public void setIsEmptyNullDateIsValid(boolean valid) { + this.getDateField().setIsEmptyNullDateIsValid(valid); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + dateField.setEnabled(enabled); + jLabel.setEnabled(enabled); + } + + /** Gets the content validity. + * @return true if the content is valid, false if it is not. + */ + public boolean isContentValid() { + return this.dateField.isContentValid(); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/MonthWidget.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/MonthWidget.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/date/MonthWidget.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/MonthWidget.java index cd95a70..0d6afb1 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/MonthWidget.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/MonthWidget.java @@ -1,235 +1,235 @@ -package com.fathzer.soft.ajlib.swing.widget.date; - -import java.awt.GridBagLayout; -import javax.swing.JPanel; -import javax.swing.JButton; -import java.awt.GridBagConstraints; -import javax.swing.JLabel; -import java.awt.Insets; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; - -import javax.swing.SwingConstants; - -import com.fathzer.soft.ajlib.swing.Utils; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - - -/** A month selector. - */ -public class MonthWidget extends JPanel { - private static final long serialVersionUID = 1L; - static final String RES_PATH = "/com/fathzer/soft/ajlib/swing/widget/date/"; - - public static final String DATE_PROPERTY = "MONTH"; - - private JButton previousYear = null; - private JButton previousMonth = null; - private JLabel currentMonth = null; - private JButton nextMonth = null; - private JButton nextYear = null; - - private Calendar currentDate; - private DateFormat formater; - - private JButton now = null; - private JPanel buttonsPanel; - - /** - * This is the default constructor - */ - public MonthWidget() { - this.currentDate = Calendar.getInstance(); - this.currentDate.set(this.currentDate.get(Calendar.YEAR), this.currentDate.get(Calendar.MONTH), 1, 0, 0, 0); - this.currentDate.set(Calendar.MILLISECOND, 0); - this.formater = new SimpleDateFormat("MMMM yyyy"); - initialize(); - } - - @Override - public void setLocale(Locale l) { - super.setLocale(l); - this.formater = new SimpleDateFormat("MMMM yyyy", l); - this.currentMonth.setText(this.formater.format(currentDate.getTime())); - } - - @SuppressWarnings("deprecation") - public void setMonth(Date date) { - if ((date.getYear()+1900!=this.currentDate.get(Calendar.YEAR)) || (date.getMonth()!=this.currentDate.get(Calendar.MONTH))) { - Date old = this.currentDate.getTime(); - this.currentDate.set(date.getYear()+1900, date.getMonth(), 1); - this.currentMonth.setText(formater.format(date)); - this.firePropertyChange(DATE_PROPERTY, old, this.currentDate.getTime()); - } - } - - /** Get the currently selected month. - * @return The first day of the selected month at 0:0:0 AM. - */ - public Date getMonth() { - return this.currentDate.getTime(); - } - - /** Move forward or backward the currently selected month. - * @param months The number of months to increment the current month. This amount can be negative. - */ - public void add(int months) { - Date old = this.currentDate.getTime(); - this.currentDate.add(Calendar.MONTH, months); - Date newDate = this.currentDate.getTime(); - this.currentMonth.setText(formater.format(newDate)); - this.firePropertyChange(DATE_PROPERTY, old, newDate); - } - - /** - * This method initializes this - */ - private void initialize() { - GridBagConstraints gridBagConstraints2 = new GridBagConstraints(); - gridBagConstraints2.gridx = 0; - gridBagConstraints2.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints2.weightx = 1.0D; - gridBagConstraints2.anchor = GridBagConstraints.CENTER; - gridBagConstraints2.insets = new Insets(5, 5, 5, 0); - gridBagConstraints2.gridy = 0; - currentMonth = new JLabel(); - currentMonth.setText(formater.format(this.currentDate.getTime())); - currentMonth.setHorizontalTextPosition(SwingConstants.CENTER); - currentMonth.setHorizontalAlignment(SwingConstants.CENTER); - this.setLayout(new GridBagLayout()); - GridBagConstraints gbcButtonsPanel = new GridBagConstraints(); - gbcButtonsPanel.gridx = 0; - gbcButtonsPanel.gridy = 1; - add(getButtonsPanel(), gbcButtonsPanel); - this.add(currentMonth, gridBagConstraints2); - } - - /** - * This method initializes previousYear - * - * @return javax.swing.JButton - */ - private JButton getPreviousYear() { - if (previousYear == null) { - previousYear = new JButton(); - previousYear.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"fast-rewind.png"), 16*getFont().getSize()/12)); - previousYear.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { - add(-12); - } - }); - } - return previousYear; - } - - /** - * This method initializes previousMonth - * - * @return javax.swing.JButton - */ - private JButton getPreviousMonth() { - if (previousMonth == null) { - previousMonth = new JButton(); - previousMonth.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"rewind.png"), 16*getFont().getSize()/12)); - previousMonth.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { - add(-1); - } - }); - } - return previousMonth; - } - - /** - * This method initializes nextMonth - * - * @return javax.swing.JButton - */ - private JButton getNextMonth() { - if (nextMonth == null) { - nextMonth = new JButton(); - nextMonth.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"forward.png"), 16*getFont().getSize()/12)); - nextMonth.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { - add(1); - } - }); - } - return nextMonth; - } - - /** - * This method initializes nextYear - * - * @return javax.swing.JButton - */ - private JButton getNextYear() { - if (nextYear == null) { - nextYear = new JButton(); - nextYear.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"fast-forward.png"), 16*getFont().getSize()/12)); - nextYear.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent e) { - add(12); - } - }); - } - return nextYear; - } - - /** - * This method initializes now - * @return javax.swing.JButton - */ - public JButton getNow() { - if (now == null) { - now = new JButton(); - now.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"stop.png"), 16*getFont().getSize()/12)); - now.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setMonth(new Date()); - } - }); - } - return now; - } - private JPanel getButtonsPanel() { - if (buttonsPanel == null) { - buttonsPanel = new JPanel(); - GridBagLayout gblButtonsPanel = new GridBagLayout(); - buttonsPanel.setLayout(gblButtonsPanel); - GridBagConstraints gbcPreviousYear = new GridBagConstraints(); - gbcPreviousYear.insets = new Insets(0, 0, 0, 5); - gbcPreviousYear.gridx = 0; - gbcPreviousYear.gridy = 0; - buttonsPanel.add(getPreviousYear(), gbcPreviousYear); - GridBagConstraints gbcPreviousMonth = new GridBagConstraints(); - gbcPreviousMonth.insets = new Insets(0, 0, 0, 5); - gbcPreviousMonth.gridx = 1; - gbcPreviousMonth.gridy = 0; - buttonsPanel.add(getPreviousMonth(), gbcPreviousMonth); - GridBagConstraints gbcNow = new GridBagConstraints(); - gbcNow.fill = GridBagConstraints.HORIZONTAL; - gbcNow.insets = new Insets(0, 0, 0, 5); - gbcNow.gridx = 2; - gbcNow.gridy = 0; - buttonsPanel.add(getNow(), gbcNow); - GridBagConstraints gbcNextMonth = new GridBagConstraints(); - gbcNextMonth.insets = new Insets(0, 0, 0, 5); - gbcNextMonth.gridx = 3; - gbcNextMonth.gridy = 0; - buttonsPanel.add(getNextMonth(), gbcNextMonth); - GridBagConstraints gbcNextYear = new GridBagConstraints(); - gbcNextYear.anchor = GridBagConstraints.WEST; - gbcNextYear.gridx = 4; - gbcNextYear.gridy = 0; - buttonsPanel.add(getNextYear(), gbcNextYear); - } - return buttonsPanel; - } -} +package com.fathzer.soft.ajlib.swing.widget.date; + +import java.awt.GridBagLayout; +import javax.swing.JPanel; +import javax.swing.JButton; +import java.awt.GridBagConstraints; +import javax.swing.JLabel; +import java.awt.Insets; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import javax.swing.SwingConstants; + +import com.fathzer.soft.ajlib.swing.Utils; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + + +/** A month selector. + */ +public class MonthWidget extends JPanel { + private static final long serialVersionUID = 1L; + static final String RES_PATH = "/com/fathzer/soft/ajlib/swing/widget/date/"; + + public static final String DATE_PROPERTY = "MONTH"; + + private JButton previousYear = null; + private JButton previousMonth = null; + private JLabel currentMonth = null; + private JButton nextMonth = null; + private JButton nextYear = null; + + private Calendar currentDate; + private DateFormat formater; + + private JButton now = null; + private JPanel buttonsPanel; + + /** + * This is the default constructor + */ + public MonthWidget() { + this.currentDate = Calendar.getInstance(); + this.currentDate.set(this.currentDate.get(Calendar.YEAR), this.currentDate.get(Calendar.MONTH), 1, 0, 0, 0); + this.currentDate.set(Calendar.MILLISECOND, 0); + this.formater = new SimpleDateFormat("MMMM yyyy"); + initialize(); + } + + @Override + public void setLocale(Locale l) { + super.setLocale(l); + this.formater = new SimpleDateFormat("MMMM yyyy", l); + this.currentMonth.setText(this.formater.format(currentDate.getTime())); + } + + @SuppressWarnings("deprecation") + public void setMonth(Date date) { + if ((date.getYear()+1900!=this.currentDate.get(Calendar.YEAR)) || (date.getMonth()!=this.currentDate.get(Calendar.MONTH))) { + Date old = this.currentDate.getTime(); + this.currentDate.set(date.getYear()+1900, date.getMonth(), 1); + this.currentMonth.setText(formater.format(date)); + this.firePropertyChange(DATE_PROPERTY, old, this.currentDate.getTime()); + } + } + + /** Get the currently selected month. + * @return The first day of the selected month at 0:0:0 AM. + */ + public Date getMonth() { + return this.currentDate.getTime(); + } + + /** Move forward or backward the currently selected month. + * @param months The number of months to increment the current month. This amount can be negative. + */ + public void add(int months) { + Date old = this.currentDate.getTime(); + this.currentDate.add(Calendar.MONTH, months); + Date newDate = this.currentDate.getTime(); + this.currentMonth.setText(formater.format(newDate)); + this.firePropertyChange(DATE_PROPERTY, old, newDate); + } + + /** + * This method initializes this + */ + private void initialize() { + GridBagConstraints gridBagConstraints2 = new GridBagConstraints(); + gridBagConstraints2.gridx = 0; + gridBagConstraints2.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints2.weightx = 1.0D; + gridBagConstraints2.anchor = GridBagConstraints.CENTER; + gridBagConstraints2.insets = new Insets(5, 5, 5, 0); + gridBagConstraints2.gridy = 0; + currentMonth = new JLabel(); + currentMonth.setText(formater.format(this.currentDate.getTime())); + currentMonth.setHorizontalTextPosition(SwingConstants.CENTER); + currentMonth.setHorizontalAlignment(SwingConstants.CENTER); + this.setLayout(new GridBagLayout()); + GridBagConstraints gbcButtonsPanel = new GridBagConstraints(); + gbcButtonsPanel.gridx = 0; + gbcButtonsPanel.gridy = 1; + add(getButtonsPanel(), gbcButtonsPanel); + this.add(currentMonth, gridBagConstraints2); + } + + /** + * This method initializes previousYear + * + * @return javax.swing.JButton + */ + private JButton getPreviousYear() { + if (previousYear == null) { + previousYear = new JButton(); + previousYear.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"fast-rewind.png"), 16*getFont().getSize()/12)); + previousYear.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + add(-12); + } + }); + } + return previousYear; + } + + /** + * This method initializes previousMonth + * + * @return javax.swing.JButton + */ + private JButton getPreviousMonth() { + if (previousMonth == null) { + previousMonth = new JButton(); + previousMonth.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"rewind.png"), 16*getFont().getSize()/12)); + previousMonth.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + add(-1); + } + }); + } + return previousMonth; + } + + /** + * This method initializes nextMonth + * + * @return javax.swing.JButton + */ + private JButton getNextMonth() { + if (nextMonth == null) { + nextMonth = new JButton(); + nextMonth.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"forward.png"), 16*getFont().getSize()/12)); + nextMonth.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + add(1); + } + }); + } + return nextMonth; + } + + /** + * This method initializes nextYear + * + * @return javax.swing.JButton + */ + private JButton getNextYear() { + if (nextYear == null) { + nextYear = new JButton(); + nextYear.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"fast-forward.png"), 16*getFont().getSize()/12)); + nextYear.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + add(12); + } + }); + } + return nextYear; + } + + /** + * This method initializes now + * @return javax.swing.JButton + */ + public JButton getNow() { + if (now == null) { + now = new JButton(); + now.setIcon(Utils.createIcon(getClass().getResource(RES_PATH+"stop.png"), 16*getFont().getSize()/12)); + now.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setMonth(new Date()); + } + }); + } + return now; + } + private JPanel getButtonsPanel() { + if (buttonsPanel == null) { + buttonsPanel = new JPanel(); + GridBagLayout gblButtonsPanel = new GridBagLayout(); + buttonsPanel.setLayout(gblButtonsPanel); + GridBagConstraints gbcPreviousYear = new GridBagConstraints(); + gbcPreviousYear.insets = new Insets(0, 0, 0, 5); + gbcPreviousYear.gridx = 0; + gbcPreviousYear.gridy = 0; + buttonsPanel.add(getPreviousYear(), gbcPreviousYear); + GridBagConstraints gbcPreviousMonth = new GridBagConstraints(); + gbcPreviousMonth.insets = new Insets(0, 0, 0, 5); + gbcPreviousMonth.gridx = 1; + gbcPreviousMonth.gridy = 0; + buttonsPanel.add(getPreviousMonth(), gbcPreviousMonth); + GridBagConstraints gbcNow = new GridBagConstraints(); + gbcNow.fill = GridBagConstraints.HORIZONTAL; + gbcNow.insets = new Insets(0, 0, 0, 5); + gbcNow.gridx = 2; + gbcNow.gridy = 0; + buttonsPanel.add(getNow(), gbcNow); + GridBagConstraints gbcNextMonth = new GridBagConstraints(); + gbcNextMonth.insets = new Insets(0, 0, 0, 5); + gbcNextMonth.gridx = 3; + gbcNextMonth.gridy = 0; + buttonsPanel.add(getNextMonth(), gbcNextMonth); + GridBagConstraints gbcNextYear = new GridBagConstraints(); + gbcNextYear.anchor = GridBagConstraints.WEST; + gbcNextYear.gridx = 4; + gbcNextYear.gridy = 0; + buttonsPanel.add(getNextYear(), gbcNextYear); + } + return buttonsPanel; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/package-info.java similarity index 98% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/date/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/package-info.java index ffd6d63..bf16d82 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/date/package-info.java @@ -1,2 +1,2 @@ -/** Swing date widgets*/ +/** Swing date widgets*/ package com.fathzer.soft.ajlib.swing.widget.date; \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/widget/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/package-info.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/widget/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/package-info.java index a06a5d6..e15eff2 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/widget/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/widget/package-info.java @@ -1,2 +1,2 @@ -/** Swing widgets*/ -package com.fathzer.soft.ajlib.swing.widget; +/** Swing widgets*/ +package com.fathzer.soft.ajlib.swing.widget; diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/worker/DefaultWorkInProgressPanel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/DefaultWorkInProgressPanel.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/worker/DefaultWorkInProgressPanel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/DefaultWorkInProgressPanel.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressFrame.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressFrame.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressFrame.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressFrame.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressPanel.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressPanel.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressPanel.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressPanel.java index dbcf69c..c908aba 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressPanel.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/WorkInProgressPanel.java @@ -1,12 +1,12 @@ -package com.fathzer.soft.ajlib.swing.worker; - -import javax.swing.JPanel; - -/** A panel reporting the progress of a background task. - * @see Worker - */ -@SuppressWarnings("serial") -public abstract class WorkInProgressPanel extends JPanel { - public abstract Worker getWorker(); - public abstract void setSwingWorker(Worker worker); +package com.fathzer.soft.ajlib.swing.worker; + +import javax.swing.JPanel; + +/** A panel reporting the progress of a background task. + * @see Worker + */ +@SuppressWarnings("serial") +public abstract class WorkInProgressPanel extends JPanel { + public abstract Worker getWorker(); + public abstract void setSwingWorker(Worker worker); } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/worker/Worker.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/Worker.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/worker/Worker.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/Worker.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/worker/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/package-info.java similarity index 98% rename from src/main/java/com/fathzer/soft/ajlib/swing/worker/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/package-info.java index 0753b48..7b5d7a0 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/worker/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/swing/worker/package-info.java @@ -1,2 +1,2 @@ -/** Utilities to implement background tasks*/ +/** Utilities to implement background tasks*/ package com.fathzer.soft.ajlib.swing.worker; \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/CSVWriter.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/CSVWriter.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/utilities/CSVWriter.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/CSVWriter.java index 02d462e..eb98594 100644 --- a/src/main/java/com/fathzer/soft/ajlib/utilities/CSVWriter.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/CSVWriter.java @@ -1,135 +1,135 @@ -package com.fathzer.soft.ajlib.utilities; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.Writer; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Locale; - -/** CSV Writer. - *
This class is able to write data in the CSV format. - *
If you're looking for a CSV parser, I recommend using OpenCSV. - */ -public class CSVWriter { - private static final String BLANK = " "; - - private boolean lineIsEmpty; - private BufferedWriter writer; - private char separator; - private char quote; - private boolean alwaysQuoteCells; - - private String quoteSeq; - private String doubleQuoteSeq; - - /** Constructor. - *
Builds a new instance with the following default attributes:
    - *
  • separator: ;
  • - *
  • quoting character: "
  • - *
  • alwaysQuote: false
  • - *
- * @param writer The writer where to write the data - */ - public CSVWriter(Writer writer) { - this.writer = writer instanceof BufferedWriter?(BufferedWriter)writer:new BufferedWriter(writer); - this.lineIsEmpty = true; - this.separator = ';'; - this.alwaysQuoteCells = false; - this.setQuote('"'); - } - - public void writeCell(String cell) throws IOException { - if (!lineIsEmpty) { - writer.append(separator); - } - lineIsEmpty = false; - if (cell==null) { - cell = ""; - } - boolean quoteCells = alwaysQuoteCells || (cell.indexOf(separator)>=0) || (cell.indexOf(quote)>=0) || cell.startsWith(BLANK) || cell.endsWith(BLANK); - cell = cell.replace(quoteSeq, doubleQuoteSeq); - if (quoteCells) { - writer.append(quote); - } - writer.append(cell); - if (quoteCells) { - writer.append(quote); - } - } - - public void newLine() throws IOException { - writer.newLine(); - lineIsEmpty = true; - } - - /** Ensures all the data is written to the underlying writer. - * @throws IOException If something goes wrong - */ - public void flush() throws IOException { - writer.flush(); - } - - /** - * @return the separator - */ - public char getSeparator() { - return separator; - } - - /** - * @param separator the separator to set - */ - public void setSeparator(char separator) { - this.separator = separator; - } - - /** - * @return the quote - */ - public char getQuote() { - return quote; - } - - /** - * @param quote the quote to set - */ - public void setQuote(char quote) { - this.quote = quote; - this.quoteSeq = new String(new char[]{quote}); - this.doubleQuoteSeq = this.quoteSeq + this.quoteSeq; - } - - /** - * @return the alwaysQuoteCells - */ - public boolean isAlwaysQuoteCells() { - return alwaysQuoteCells; - } - - /** - * @param alwaysQuoteCells the alwaysQuoteCells to set - */ - public void setAlwaysQuoteCells(boolean alwaysQuoteCells) { - this.alwaysQuoteCells = alwaysQuoteCells; - } - - /** Returns a formatter to use to output decimal number. - *
Excel is very demanding on the number formats. It doesn't tolerate currency suffix or prefix, nor grouping separators. - *
This method returns a formatter that outputs strings that will be recognized as numbers by excel. - * @param locale The locale. - * @return a NumberFormat instance - */ - public static NumberFormat getDecimalFormater(Locale locale) { - NumberFormat result = NumberFormat.getInstance(locale); - if (result instanceof DecimalFormat) { - // We don't use the currency instance, because it would have output some currency prefix or suffix, not very easy - // to manipulate with an excel like application - NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale); - result.setMinimumFractionDigits(currencyFormat.getMinimumFractionDigits()); - result.setMaximumFractionDigits(currencyFormat.getMaximumFractionDigits()); - } - result.setGroupingUsed(false); - return result; - } +package com.fathzer.soft.ajlib.utilities; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +/** CSV Writer. + *
This class is able to write data in the CSV format. + *
If you're looking for a CSV parser, I recommend using OpenCSV. + */ +public class CSVWriter { + private static final String BLANK = " "; + + private boolean lineIsEmpty; + private BufferedWriter writer; + private char separator; + private char quote; + private boolean alwaysQuoteCells; + + private String quoteSeq; + private String doubleQuoteSeq; + + /** Constructor. + *
Builds a new instance with the following default attributes:
    + *
  • separator: ;
  • + *
  • quoting character: "
  • + *
  • alwaysQuote: false
  • + *
+ * @param writer The writer where to write the data + */ + public CSVWriter(Writer writer) { + this.writer = writer instanceof BufferedWriter?(BufferedWriter)writer:new BufferedWriter(writer); + this.lineIsEmpty = true; + this.separator = ';'; + this.alwaysQuoteCells = false; + this.setQuote('"'); + } + + public void writeCell(String cell) throws IOException { + if (!lineIsEmpty) { + writer.append(separator); + } + lineIsEmpty = false; + if (cell==null) { + cell = ""; + } + boolean quoteCells = alwaysQuoteCells || (cell.indexOf(separator)>=0) || (cell.indexOf(quote)>=0) || cell.startsWith(BLANK) || cell.endsWith(BLANK); + cell = cell.replace(quoteSeq, doubleQuoteSeq); + if (quoteCells) { + writer.append(quote); + } + writer.append(cell); + if (quoteCells) { + writer.append(quote); + } + } + + public void newLine() throws IOException { + writer.newLine(); + lineIsEmpty = true; + } + + /** Ensures all the data is written to the underlying writer. + * @throws IOException If something goes wrong + */ + public void flush() throws IOException { + writer.flush(); + } + + /** + * @return the separator + */ + public char getSeparator() { + return separator; + } + + /** + * @param separator the separator to set + */ + public void setSeparator(char separator) { + this.separator = separator; + } + + /** + * @return the quote + */ + public char getQuote() { + return quote; + } + + /** + * @param quote the quote to set + */ + public void setQuote(char quote) { + this.quote = quote; + this.quoteSeq = new String(new char[]{quote}); + this.doubleQuoteSeq = this.quoteSeq + this.quoteSeq; + } + + /** + * @return the alwaysQuoteCells + */ + public boolean isAlwaysQuoteCells() { + return alwaysQuoteCells; + } + + /** + * @param alwaysQuoteCells the alwaysQuoteCells to set + */ + public void setAlwaysQuoteCells(boolean alwaysQuoteCells) { + this.alwaysQuoteCells = alwaysQuoteCells; + } + + /** Returns a formatter to use to output decimal number. + *
Excel is very demanding on the number formats. It doesn't tolerate currency suffix or prefix, nor grouping separators. + *
This method returns a formatter that outputs strings that will be recognized as numbers by excel. + * @param locale The locale. + * @return a NumberFormat instance + */ + public static NumberFormat getDecimalFormater(Locale locale) { + NumberFormat result = NumberFormat.getInstance(locale); + if (result instanceof DecimalFormat) { + // We don't use the currency instance, because it would have output some currency prefix or suffix, not very easy + // to manipulate with an excel like application + NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale); + result.setMinimumFractionDigits(currencyFormat.getMinimumFractionDigits()); + result.setMaximumFractionDigits(currencyFormat.getMaximumFractionDigits()); + } + result.setGroupingUsed(false); + return result; + } } \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/CoolDateFormatter.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/CoolDateFormatter.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/utilities/CoolDateFormatter.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/CoolDateFormatter.java diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/FileUtils.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/FileUtils.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/utilities/FileUtils.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/FileUtils.java index 495bde4..cdd8833 100644 --- a/src/main/java/com/fathzer/soft/ajlib/utilities/FileUtils.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/FileUtils.java @@ -1,288 +1,288 @@ -package com.fathzer.soft.ajlib.utilities; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.channels.FileLock; -import java.text.MessageFormat; -import java.text.ParseException; - -/** Utility to perform some operations on files. - */ -public class FileUtils { - private static final String ACCESS_DENIED_MESSAGE = "Write access to file refused"; - - private FileUtils() { - } - - /** Gets the canonical file of a file even on windows where links are ignored by File.getCanonicalPath(). - *
Even if the link is broken, the method returns the linked file. You should use File.exists() to test if the returned file is available. - *
If the link is a link to a link to a file, this method returns the final file. - * @param file the file to test - * @return a File - * @throws IOException If something goes wrong - */ - public static File getCanonical(File file) throws IOException { - if (!file.exists()) { - return file; - } - try { - // Java has nothing to decode shortcut (what a pity!). - // we will try to decode ourselves - if (WindowsShortcut.isPotentialValidLink(file)) { - return new File(new WindowsShortcut(file).getRealFilename()); - } - } catch (ParseException e) { - // Something went wrong - // In such a case, let file.CanonicalFile a chance to do better. - } - return file.getCanonicalFile(); - } - - /** Move a file from one path to another. - *
Unlike java.io.File.renameTo, this method always moves the file (if it doesn't fail). - * If the source and the destination paths are not on the same file system, the file is copied to the new file system - * and then erased from the old one. - * @param src The src path - * @param dest The dest path - * @throws IOException If the move fails - */ - public static void move(File src, File dest) throws IOException { - // Check whether the destination directory exists or not. - // If not, create it. - File parent = dest.getAbsoluteFile().getParentFile(); - if (!parent.exists()) { - parent.mkdirs(); - } - // Try to simply rename the file - if (!src.renameTo(dest)) { - // renameTo may fail if src and dest files are not on the same file system. - // Then we have to copy the src file. - // Before, we will ensure we will be able to erase the src file after the copy - if (src.canWrite()) { - copy(src, dest, true); - // Now, deletes the src file - if (!src.delete()) { - // Oh ... we were thinking we had the right to delete the file ... but we can't: - // Delete the dest file - dest.delete(); - throw new SecurityException(ACCESS_DENIED_MESSAGE); - } - } else { - throw new SecurityException(ACCESS_DENIED_MESSAGE); - } - } - } - - /** Copy a file to another. - * @param src The src path - * @param dest The dest path - * @param overrideExisting true if copying to an existing file is ok. - * @throws IOException If the copy fails - */ - public static void copy(File src, File dest, boolean overrideExisting) throws IOException { - if (dest.exists() && !overrideExisting) { - throw new IOException(MessageFormat.format("File {0} already exists", dest)); - } - try (InputStream in = new BufferedInputStream(new FileInputStream(src))) { - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(dest))) { - int c; - while ((c = in.read()) != -1) { - out.write(c); - } - } - } - dest.setLastModified(src.lastModified()); - } - - /** Deletes recursively a directory. - *
This means that the directory and all of its files or subfolders are deleted. - * @param file the directory to be deleted (if is is a file, the file will be deleted). - * @return true if the directory has been successfully deleted. - */ - public static boolean deleteDirectory(File file) { - if (file.isDirectory()) { - File[] files = file.listFiles(); - for (int i = 0; i < files.length; i++) { - deleteDirectory(files[i]); - } - } - return file.delete(); - } - - /** Tests whether a file is contained in a directory. - *
The directory and the file may not exists, the search is done on the absolute paths. - * @param file The file to be tested - * @param directory The tested directory - * @return true if the file is contained in the directory. - */ - public static boolean isIncluded(File file, File directory) { - for (File parent = file.getParentFile(); parent!=null; parent = parent.getParentFile()) { - if (parent.equals(directory)) { - return true; - } - } - return false; - } - - /** Gets a FileOutputStream even on a windows hidden file. - *
Under windows, it is impossible to write directly in a hidden file with Java. - * You have to make the file visible first. That's what this method try to do. - *
When the file was initially hidden, the close method of the returned stream hide it again. - * @param file The file to be opened for writing - * @return a new stream - * @throws IOException If something goes wrong - */ - public static FileOutputStream getHiddenCompliantStream(File file) throws IOException { - if (file.isHidden() && System.getProperty("os.name", "?").startsWith("Windows")) { - try { - Process process = Runtime.getRuntime().exec("attrib -H \""+file.getAbsolutePath()+"\""); - try { - int result = process.waitFor(); - if (result==0) { - return new HiddenFileOutputStream(file); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } catch (IOException e) { - // This try catch block is empty because this exception, in this context, means that the attrib command is not available. - // In such a case, we just have to do ... nothing: If the OutputStream creation fails, an IOException will be thrown - } - } - // If the file was not hidden, or if making the file visible failed, try to open a classic stream. - return new FileOutputStream(file); - } - - /** The FileOutputStream returned by getHiddenCompliantStream method when it is called on a Windows hidden file. - * @see FileUtils#getHiddenCompliantStream(File) - */ - private static class HiddenFileOutputStream extends FileOutputStream { - private File file; - - HiddenFileOutputStream(File file) throws FileNotFoundException { - super(file); - this.file = file; - } - - @Override - public void close() throws IOException { - super.close(); - Process process = Runtime.getRuntime().exec("attrib +H \""+file.getAbsolutePath()+"\""); - try { - process.waitFor(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - - /** Tests whether the application can write to a file (or a folder). - *
It differs from File.canWrite because File.canWrite ignore the security policies of the platform. - * This method returns true only if the calling thread have all the rights necessary to write to the file, and the file - * is not already locked. - * @param file The file or folder to test - * @return true if the file or folder exists and the calling thread can write into it. - *
If the file doesn't exist, it returns true if the parent folder is writable. - */ - public static boolean isWritable(File file) { - if (!file.exists()) { - File parentFile = file.getAbsoluteFile().getParentFile(); - return parentFile==null?false:isWritable(parentFile); - } - if (!file.canWrite()) { - return false; - } - if (file.isDirectory()) { - // If the file is a folder, the easiest way is to create a temporary file. - try { - File f = File.createTempFile("ajlib", null, file); - f.delete(); - return true; - } catch (IOException e) { - return false; - } catch (SecurityException e) { - return false; - } - } else { - // If the argument is a file, we will simply open it for writing - try (FileOutputStream x = new FileOutputStream(file,true)) { - FileLock lock = null; - lock = x.getChannel().tryLock(); - if (lock==null) { - return false; - } else { - lock.release(); - return true; - } - } catch (FileNotFoundException e) { - // File is locked by another application - return false; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - /** Tests whether the application can read from a file (or a folder). - *
It differs from File.canRead because File.canRead ignore the security policies of the platform. - * This method returns true only if the calling thread have all the rights necessary to read from the file. - * @param file The file or folder to test - * @return true if the file or folder exists and the calling thread can write into it - */ - public static boolean isReadable(File file) { - if (!file.canRead()) { - return false; - } - if (file.isDirectory()) { - return true; - } - // If the argument is a file, we will simply to open it for reading - try { - FileInputStream x = new FileInputStream(file); - x.close(); - return true; - } catch (FileNotFoundException e) { - // The application have the right to read the file - return false; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** Gets the extension of a file. - *
For example, the extension of file "file.xml" is ".xml". - * @param file The file. - * @return The extension including the point, or null if fileName does not have an extension. - */ - public static String getExtension(File file) { - String name = file.getName(); - int index = name.lastIndexOf('.'); - if (index<0) { - return null; - } else { - return name.substring(index); - } - } - - /** Gets the root name of a file. - *
For example, the root name of file "file.xml" is "file". - * @param file The file. - * @return The extension including the point, or null if fileName does not have an extension. - */ - public static String getRootName(File file) { - String name = file.getName(); - int index = name.lastIndexOf('.'); - if (index<0) { - return name; - } else { - return name.substring(0,index); - } - } -} +package com.fathzer.soft.ajlib.utilities; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileLock; +import java.text.MessageFormat; +import java.text.ParseException; + +/** Utility to perform some operations on files. + */ +public class FileUtils { + private static final String ACCESS_DENIED_MESSAGE = "Write access to file refused"; + + private FileUtils() { + } + + /** Gets the canonical file of a file even on windows where links are ignored by File.getCanonicalPath(). + *
Even if the link is broken, the method returns the linked file. You should use File.exists() to test if the returned file is available. + *
If the link is a link to a link to a file, this method returns the final file. + * @param file the file to test + * @return a File + * @throws IOException If something goes wrong + */ + public static File getCanonical(File file) throws IOException { + if (!file.exists()) { + return file; + } + try { + // Java has nothing to decode shortcut (what a pity!). + // we will try to decode ourselves + if (WindowsShortcut.isPotentialValidLink(file)) { + return new File(new WindowsShortcut(file).getRealFilename()); + } + } catch (ParseException e) { + // Something went wrong + // In such a case, let file.CanonicalFile a chance to do better. + } + return file.getCanonicalFile(); + } + + /** Move a file from one path to another. + *
Unlike java.io.File.renameTo, this method always moves the file (if it doesn't fail). + * If the source and the destination paths are not on the same file system, the file is copied to the new file system + * and then erased from the old one. + * @param src The src path + * @param dest The dest path + * @throws IOException If the move fails + */ + public static void move(File src, File dest) throws IOException { + // Check whether the destination directory exists or not. + // If not, create it. + File parent = dest.getAbsoluteFile().getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } + // Try to simply rename the file + if (!src.renameTo(dest)) { + // renameTo may fail if src and dest files are not on the same file system. + // Then we have to copy the src file. + // Before, we will ensure we will be able to erase the src file after the copy + if (src.canWrite()) { + copy(src, dest, true); + // Now, deletes the src file + if (!src.delete()) { + // Oh ... we were thinking we had the right to delete the file ... but we can't: + // Delete the dest file + dest.delete(); + throw new SecurityException(ACCESS_DENIED_MESSAGE); + } + } else { + throw new SecurityException(ACCESS_DENIED_MESSAGE); + } + } + } + + /** Copy a file to another. + * @param src The src path + * @param dest The dest path + * @param overrideExisting true if copying to an existing file is ok. + * @throws IOException If the copy fails + */ + public static void copy(File src, File dest, boolean overrideExisting) throws IOException { + if (dest.exists() && !overrideExisting) { + throw new IOException(MessageFormat.format("File {0} already exists", dest)); + } + try (InputStream in = new BufferedInputStream(new FileInputStream(src))) { + try (OutputStream out = new BufferedOutputStream(new FileOutputStream(dest))) { + int c; + while ((c = in.read()) != -1) { + out.write(c); + } + } + } + dest.setLastModified(src.lastModified()); + } + + /** Deletes recursively a directory. + *
This means that the directory and all of its files or subfolders are deleted. + * @param file the directory to be deleted (if is is a file, the file will be deleted). + * @return true if the directory has been successfully deleted. + */ + public static boolean deleteDirectory(File file) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + for (int i = 0; i < files.length; i++) { + deleteDirectory(files[i]); + } + } + return file.delete(); + } + + /** Tests whether a file is contained in a directory. + *
The directory and the file may not exists, the search is done on the absolute paths. + * @param file The file to be tested + * @param directory The tested directory + * @return true if the file is contained in the directory. + */ + public static boolean isIncluded(File file, File directory) { + for (File parent = file.getParentFile(); parent!=null; parent = parent.getParentFile()) { + if (parent.equals(directory)) { + return true; + } + } + return false; + } + + /** Gets a FileOutputStream even on a windows hidden file. + *
Under windows, it is impossible to write directly in a hidden file with Java. + * You have to make the file visible first. That's what this method try to do. + *
When the file was initially hidden, the close method of the returned stream hide it again. + * @param file The file to be opened for writing + * @return a new stream + * @throws IOException If something goes wrong + */ + public static FileOutputStream getHiddenCompliantStream(File file) throws IOException { + if (file.isHidden() && System.getProperty("os.name", "?").startsWith("Windows")) { + try { + Process process = Runtime.getRuntime().exec("attrib -H \""+file.getAbsolutePath()+"\""); + try { + int result = process.waitFor(); + if (result==0) { + return new HiddenFileOutputStream(file); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } catch (IOException e) { + // This try catch block is empty because this exception, in this context, means that the attrib command is not available. + // In such a case, we just have to do ... nothing: If the OutputStream creation fails, an IOException will be thrown + } + } + // If the file was not hidden, or if making the file visible failed, try to open a classic stream. + return new FileOutputStream(file); + } + + /** The FileOutputStream returned by getHiddenCompliantStream method when it is called on a Windows hidden file. + * @see FileUtils#getHiddenCompliantStream(File) + */ + private static class HiddenFileOutputStream extends FileOutputStream { + private File file; + + HiddenFileOutputStream(File file) throws FileNotFoundException { + super(file); + this.file = file; + } + + @Override + public void close() throws IOException { + super.close(); + Process process = Runtime.getRuntime().exec("attrib +H \""+file.getAbsolutePath()+"\""); + try { + process.waitFor(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + /** Tests whether the application can write to a file (or a folder). + *
It differs from File.canWrite because File.canWrite ignore the security policies of the platform. + * This method returns true only if the calling thread have all the rights necessary to write to the file, and the file + * is not already locked. + * @param file The file or folder to test + * @return true if the file or folder exists and the calling thread can write into it. + *
If the file doesn't exist, it returns true if the parent folder is writable. + */ + public static boolean isWritable(File file) { + if (!file.exists()) { + File parentFile = file.getAbsoluteFile().getParentFile(); + return parentFile==null?false:isWritable(parentFile); + } + if (!file.canWrite()) { + return false; + } + if (file.isDirectory()) { + // If the file is a folder, the easiest way is to create a temporary file. + try { + File f = File.createTempFile("ajlib", null, file); + f.delete(); + return true; + } catch (IOException e) { + return false; + } catch (SecurityException e) { + return false; + } + } else { + // If the argument is a file, we will simply open it for writing + try (FileOutputStream x = new FileOutputStream(file,true)) { + FileLock lock = null; + lock = x.getChannel().tryLock(); + if (lock==null) { + return false; + } else { + lock.release(); + return true; + } + } catch (FileNotFoundException e) { + // File is locked by another application + return false; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + /** Tests whether the application can read from a file (or a folder). + *
It differs from File.canRead because File.canRead ignore the security policies of the platform. + * This method returns true only if the calling thread have all the rights necessary to read from the file. + * @param file The file or folder to test + * @return true if the file or folder exists and the calling thread can write into it + */ + public static boolean isReadable(File file) { + if (!file.canRead()) { + return false; + } + if (file.isDirectory()) { + return true; + } + // If the argument is a file, we will simply to open it for reading + try { + FileInputStream x = new FileInputStream(file); + x.close(); + return true; + } catch (FileNotFoundException e) { + // The application have the right to read the file + return false; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** Gets the extension of a file. + *
For example, the extension of file "file.xml" is ".xml". + * @param file The file. + * @return The extension including the point, or null if fileName does not have an extension. + */ + public static String getExtension(File file) { + String name = file.getName(); + int index = name.lastIndexOf('.'); + if (index<0) { + return null; + } else { + return name.substring(index); + } + } + + /** Gets the root name of a file. + *
For example, the root name of file "file.xml" is "file". + * @param file The file. + * @return The extension including the point, or null if fileName does not have an extension. + */ + public static String getRootName(File file) { + String name = file.getName(); + int index = name.lastIndexOf('.'); + if (index<0) { + return name; + } else { + return name.substring(0,index); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/ListUtils.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/ListUtils.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/utilities/ListUtils.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/ListUtils.java diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/LocalizationData.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/LocalizationData.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/utilities/LocalizationData.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/LocalizationData.java diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/NullUtils.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/NullUtils.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/utilities/NullUtils.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/NullUtils.java diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/RuntimeUtils.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/RuntimeUtils.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/utilities/RuntimeUtils.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/RuntimeUtils.java index 0595d6e..0136c12 100644 --- a/src/main/java/com/fathzer/soft/ajlib/utilities/RuntimeUtils.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/RuntimeUtils.java @@ -1,45 +1,45 @@ -package com.fathzer.soft.ajlib.utilities; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; - -public abstract class RuntimeUtils { - private RuntimeUtils() { - // To prevent class being instantiated - } - - private static class UnexpectedException extends RuntimeException { - private static final long serialVersionUID = 1L; - - public UnexpectedException(Throwable cause) { - super("Unexpected exception", cause); - } - - } - - public static int getJavaMajorVersion() { - try { - // Try with java 9 API - final Method m = Runtime.class.getMethod("version"); - final Object version = m.invoke(null); - return ((List) version.getClass().getMethod("version").invoke(version)).get(0); - } catch (NoSuchMethodException e) { - // Try parsing System.getProperty - String version = System.getProperty("java.version"); - if (version.startsWith("1.")) { - version = version.substring(2, 3); - } else { - final int dot = version.indexOf("."); - if (dot != -1) { - version = version.substring(0, dot); - } - } - return Integer.parseInt(version); - } catch (IllegalAccessException e) { - throw new UnexpectedException(e); - } catch (InvocationTargetException e) { - throw new UnexpectedException(e); - } - } -} +package com.fathzer.soft.ajlib.utilities; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +public abstract class RuntimeUtils { + private RuntimeUtils() { + // To prevent class being instantiated + } + + private static class UnexpectedException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public UnexpectedException(Throwable cause) { + super("Unexpected exception", cause); + } + + } + + public static int getJavaMajorVersion() { + try { + // Try with java 9 API + final Method m = Runtime.class.getMethod("version"); + final Object version = m.invoke(null); + return ((List) version.getClass().getMethod("version").invoke(version)).get(0); + } catch (NoSuchMethodException e) { + // Try parsing System.getProperty + String version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + version = version.substring(2, 3); + } else { + final int dot = version.indexOf("."); + if (dot != -1) { + version = version.substring(0, dot); + } + } + return Integer.parseInt(version); + } catch (IllegalAccessException e) { + throw new UnexpectedException(e); + } catch (InvocationTargetException e) { + throw new UnexpectedException(e); + } + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/StringUtils.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/StringUtils.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/utilities/StringUtils.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/StringUtils.java index e7604ca..1074e7f 100644 --- a/src/main/java/com/fathzer/soft/ajlib/utilities/StringUtils.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/StringUtils.java @@ -1,42 +1,42 @@ -package com.fathzer.soft.ajlib.utilities; - -import java.util.LinkedList; -import java.util.List; - -/** Utility to parse the fields separated by a char in a string. - */ -public abstract class StringUtils { - public static final String EMPTY = ""; //$NON-NLS-1$ - - private StringUtils() { - super(); - } - - /** Splits a string into fields. - *
The main advantage vs String#split is that the developer has not to deal with separators that - * are reserved characters of the regular expressions syntax - *
Some examples:
    - *
  • split("",',') -> {""}
  • - *
  • split(",a,",',') -> {"","a",""}
  • - *
- * @param string The String to split - * @param separator The field separator - * @return an array of Strings. - */ - public static String[] split(String string, char separator) { - List result = new LinkedList<>(); - StringBuilder buffer = new StringBuilder(); - for (int i = 0; i < string.length(); i++) { - if (string.charAt(i)==separator) { - result.add(buffer.toString()); - if (buffer.length()>0) { - buffer.delete(0, buffer.length()); - } - } else { - buffer.append(string.charAt(i)); - } - } - result.add(buffer.toString()); - return result.toArray(new String[result.size()]); - } -} +package com.fathzer.soft.ajlib.utilities; + +import java.util.LinkedList; +import java.util.List; + +/** Utility to parse the fields separated by a char in a string. + */ +public abstract class StringUtils { + public static final String EMPTY = ""; //$NON-NLS-1$ + + private StringUtils() { + super(); + } + + /** Splits a string into fields. + *
The main advantage vs String#split is that the developer has not to deal with separators that + * are reserved characters of the regular expressions syntax + *
Some examples:
    + *
  • split("",',') -> {""}
  • + *
  • split(",a,",',') -> {"","a",""}
  • + *
+ * @param string The String to split + * @param separator The field separator + * @return an array of Strings. + */ + public static String[] split(String string, char separator) { + List result = new LinkedList<>(); + StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < string.length(); i++) { + if (string.charAt(i)==separator) { + result.add(buffer.toString()); + if (buffer.length()>0) { + buffer.delete(0, buffer.length()); + } + } else { + buffer.append(string.charAt(i)); + } + } + result.add(buffer.toString()); + return result.toArray(new String[result.size()]); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/TextMatcher.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/TextMatcher.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/utilities/TextMatcher.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/TextMatcher.java index 04a4b91..8a3b034 100644 --- a/src/main/java/com/fathzer/soft/ajlib/utilities/TextMatcher.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/TextMatcher.java @@ -1,159 +1,159 @@ -package com.fathzer.soft.ajlib.utilities; - -import java.text.Normalizer; -import java.util.regex.Pattern; - -/** A text matcher. - *
A text matcher allows you to test whether a string matches a filter accordingly to some options as:
    - *
  • What condition means "matches":
      - *
    • String matches is equals to the filter.
    • - *
    • String contains the filter.
    • - *
    • String matches the filter (interpreted as a regular expression).
    • - *
  • - *
  • Case could be ignored ... or not.
  • - *
  • Diacritical marks could be ignored ... or not.
  • - *
- * @see Kind#REGULAR - * @see Kind#EQUALS - * @see Kind#CONTAINS - */ -public class TextMatcher { - /** A kind of comparison a TextMatcher can perform. */ - public enum Kind { - /** Kind of comparison: Does a string matches the filter (filter is interpreted as a regular expression) ? */ - REGULAR, - /** Kind of comparison: Does a string is equal to the filter ? */ - EQUALS, - /** Kind of comparison: Does a string contains the filter ? */ - CONTAINS - } - - private Kind kind; - private String filter; - private boolean caseSensitive; - private boolean diacriticalSensitive; - private Object internalFilter; - - /** Constructor. - * @param kind The kind of matcher. - * @param filter The filter string - * @param caseSensitive sets the case sensitivity of the comparison (true if the comparison is case sensitive) - * @param diacriticalSensitive sets the diacritical sensitivity of the comparison (false to ignore diacritical marks). - * @see Kind#REGULAR - * @see Kind#EQUALS - * @see Kind#CONTAINS - */ - public TextMatcher(Kind kind, String filter, boolean caseSensitive, boolean diacriticalSensitive) { - super(); - if (kind==null) { - throw new IllegalArgumentException(); - } - this.kind = kind; - this.filter = filter; - this.caseSensitive = caseSensitive; - this.diacriticalSensitive = diacriticalSensitive; - // If diacriticals have to be ignored, we need to remove the diacritical marks from the filter string - if (!diacriticalSensitive) { - filter = removeDiacriticals(filter); - } - if (kind==Kind.REGULAR) { - if (caseSensitive) { - internalFilter = Pattern.compile(filter); - } else { - internalFilter = Pattern.compile(filter, Pattern.UNICODE_CASE+Pattern.CASE_INSENSITIVE); - } - } else if ((kind==Kind.EQUALS) || ((kind==Kind.CONTAINS) && caseSensitive)) { - internalFilter = filter; - } else { - internalFilter = filter.toUpperCase(); - } - } - - /** Removes the diacritical marks from a string. - * @param string The string to process - * @return a new String with no diacritical marks - */ - public static String removeDiacriticals(String string) { - return Normalizer.normalize(string, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); - } - - /** Gets the kind of comparison. - * @return a Kind of comparison. - * @see Kind#REGULAR - * @see Kind#EQUALS - * @see Kind#CONTAINS - */ - public Kind getKind() { - return kind; - } - - /** Gets the filter string. - * @return a String - */ - public String getFilter() { - return filter; - } - - /** Tests whether this matcher is case sensitive. - * @return true if the matcher is case sensitive. - */ - public boolean isCaseSensitive() { - return caseSensitive; - } - - /** Tests whether this matcher is diacritical sensitive. - * @return true if the matcher is diacritical sensitive. - */ - public boolean isDiacriticalSensitive() { - return diacriticalSensitive; - } - - /** Tests whether a string matches this matcher. - * @param text the string to test - * @return true if the string matches. If the string is null, this method always returns false - */ - public boolean matches(String text) { - if (text==null) { - return false; - } - if (!diacriticalSensitive) { - text = removeDiacriticals(text); - } - if (kind==Kind.REGULAR) { - return ((Pattern)internalFilter).matcher(text).matches(); - } else if (kind==Kind.EQUALS) { - return caseSensitive?text.equals(internalFilter):text.equalsIgnoreCase((String) internalFilter); - } else if (kind==Kind.CONTAINS) { - if (caseSensitive) { - return text.contains((CharSequence) internalFilter); - } else { - return text.toUpperCase().contains((CharSequence) internalFilter); - } - } else { - throw new UnsupportedOperationException(); - } - } - - @Override - public int hashCode() { - return filter.hashCode(); - } - - /** - * Tests whether the argument object is equals to this. - *
Two TextMatcher are equals if their kind, filter, caseSensitive and diacriticalSensitive attributes are equals. - *
Note that two equivalent TextMatchers can not be equals; For instance one with "FILTER" as filter, the other with "filter" - * and the caseSensitive attribute set to false. - * @param obj a textMatcher to test. - * @see #TextMatcher(Kind, String, boolean, boolean) - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof TextMatcher) { - TextMatcher other = (TextMatcher) obj; - return this.kind.equals(other.kind) && this.filter.equals(other.filter) - && (this.caseSensitive==other.caseSensitive) && (this.diacriticalSensitive==other.diacriticalSensitive); - } - return false; - } -} +package com.fathzer.soft.ajlib.utilities; + +import java.text.Normalizer; +import java.util.regex.Pattern; + +/** A text matcher. + *
A text matcher allows you to test whether a string matches a filter accordingly to some options as:
    + *
  • What condition means "matches":
      + *
    • String matches is equals to the filter.
    • + *
    • String contains the filter.
    • + *
    • String matches the filter (interpreted as a regular expression).
    • + *
  • + *
  • Case could be ignored ... or not.
  • + *
  • Diacritical marks could be ignored ... or not.
  • + *
+ * @see Kind#REGULAR + * @see Kind#EQUALS + * @see Kind#CONTAINS + */ +public class TextMatcher { + /** A kind of comparison a TextMatcher can perform. */ + public enum Kind { + /** Kind of comparison: Does a string matches the filter (filter is interpreted as a regular expression) ? */ + REGULAR, + /** Kind of comparison: Does a string is equal to the filter ? */ + EQUALS, + /** Kind of comparison: Does a string contains the filter ? */ + CONTAINS + } + + private Kind kind; + private String filter; + private boolean caseSensitive; + private boolean diacriticalSensitive; + private Object internalFilter; + + /** Constructor. + * @param kind The kind of matcher. + * @param filter The filter string + * @param caseSensitive sets the case sensitivity of the comparison (true if the comparison is case sensitive) + * @param diacriticalSensitive sets the diacritical sensitivity of the comparison (false to ignore diacritical marks). + * @see Kind#REGULAR + * @see Kind#EQUALS + * @see Kind#CONTAINS + */ + public TextMatcher(Kind kind, String filter, boolean caseSensitive, boolean diacriticalSensitive) { + super(); + if (kind==null) { + throw new IllegalArgumentException(); + } + this.kind = kind; + this.filter = filter; + this.caseSensitive = caseSensitive; + this.diacriticalSensitive = diacriticalSensitive; + // If diacriticals have to be ignored, we need to remove the diacritical marks from the filter string + if (!diacriticalSensitive) { + filter = removeDiacriticals(filter); + } + if (kind==Kind.REGULAR) { + if (caseSensitive) { + internalFilter = Pattern.compile(filter); + } else { + internalFilter = Pattern.compile(filter, Pattern.UNICODE_CASE+Pattern.CASE_INSENSITIVE); + } + } else if ((kind==Kind.EQUALS) || ((kind==Kind.CONTAINS) && caseSensitive)) { + internalFilter = filter; + } else { + internalFilter = filter.toUpperCase(); + } + } + + /** Removes the diacritical marks from a string. + * @param string The string to process + * @return a new String with no diacritical marks + */ + public static String removeDiacriticals(String string) { + return Normalizer.normalize(string, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); + } + + /** Gets the kind of comparison. + * @return a Kind of comparison. + * @see Kind#REGULAR + * @see Kind#EQUALS + * @see Kind#CONTAINS + */ + public Kind getKind() { + return kind; + } + + /** Gets the filter string. + * @return a String + */ + public String getFilter() { + return filter; + } + + /** Tests whether this matcher is case sensitive. + * @return true if the matcher is case sensitive. + */ + public boolean isCaseSensitive() { + return caseSensitive; + } + + /** Tests whether this matcher is diacritical sensitive. + * @return true if the matcher is diacritical sensitive. + */ + public boolean isDiacriticalSensitive() { + return diacriticalSensitive; + } + + /** Tests whether a string matches this matcher. + * @param text the string to test + * @return true if the string matches. If the string is null, this method always returns false + */ + public boolean matches(String text) { + if (text==null) { + return false; + } + if (!diacriticalSensitive) { + text = removeDiacriticals(text); + } + if (kind==Kind.REGULAR) { + return ((Pattern)internalFilter).matcher(text).matches(); + } else if (kind==Kind.EQUALS) { + return caseSensitive?text.equals(internalFilter):text.equalsIgnoreCase((String) internalFilter); + } else if (kind==Kind.CONTAINS) { + if (caseSensitive) { + return text.contains((CharSequence) internalFilter); + } else { + return text.toUpperCase().contains((CharSequence) internalFilter); + } + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public int hashCode() { + return filter.hashCode(); + } + + /** + * Tests whether the argument object is equals to this. + *
Two TextMatcher are equals if their kind, filter, caseSensitive and diacriticalSensitive attributes are equals. + *
Note that two equivalent TextMatchers can not be equals; For instance one with "FILTER" as filter, the other with "filter" + * and the caseSensitive attribute set to false. + * @param obj a textMatcher to test. + * @see #TextMatcher(Kind, String, boolean, boolean) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof TextMatcher) { + TextMatcher other = (TextMatcher) obj; + return this.kind.equals(other.kind) && this.filter.equals(other.filter) + && (this.caseSensitive==other.caseSensitive) && (this.diacriticalSensitive==other.diacriticalSensitive); + } + return false; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/WindowsShortcut.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/WindowsShortcut.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/utilities/WindowsShortcut.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/WindowsShortcut.java diff --git a/src/main/java/com/fathzer/soft/ajlib/utilities/package-info.java b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/package-info.java similarity index 98% rename from src/main/java/com/fathzer/soft/ajlib/utilities/package-info.java rename to ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/package-info.java index a5bd892..9dbcb08 100644 --- a/src/main/java/com/fathzer/soft/ajlib/utilities/package-info.java +++ b/ajlib/src/main/java/com/fathzer/soft/ajlib/utilities/package-info.java @@ -1,2 +1,2 @@ -/** General purpose utilities*/ +/** General purpose utilities*/ package com.fathzer.soft.ajlib.utilities; \ No newline at end of file diff --git a/src/main/resources/com/fathzer/soft/ajlib/Resources.properties b/ajlib/src/main/resources/com/fathzer/soft/ajlib/Resources.properties similarity index 97% rename from src/main/resources/com/fathzer/soft/ajlib/Resources.properties rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/Resources.properties index ef8dfdf..7c5b46a 100644 --- a/src/main/resources/com/fathzer/soft/ajlib/Resources.properties +++ b/ajlib/src/main/resources/com/fathzer/soft/ajlib/Resources.properties @@ -1,65 +1,65 @@ -#Translation tips: -#Some strings contain patterns like {0}. These patterns has to remain unchanged when you translate these string. -#For those who are familiar with MessageFormat, be aware that AJLib uses another library that do not use -#single quote as an escape char. So, you have not to double every single quote. - -#Menu localization -MainMenu.file=File -MainMenu.file.mnemonic=F -MainMenu.quit=Quit -MainMenu.quit.shortcut=Q -MainMenu.quit.mnemonic=Q -MainMenu.quit.tooltip=Quits the application - -#Generic wordings -GenericButton.ignore=Ignore -GenericButton.cancel=Cancel -GenericButton.cancel.toolTip=Cancels the current input -GenericButton.ok=Validate -GenericButton.ok.toolTip=Validates the current input -Generic.warning=Warning -Generic.error=Error - -Browser.unsupported.message=Unable to open a web browser.

The URL ({0}) has been copied to the clipboard.
You can now paste it to your web browser. - -# This dialog is opened when the file to save the data is already existing -# Dialog title -saveDialog.FileExist.title=Warning -# Dialog message -saveDialog.FileExist.message=File already exists. Would you like to overwrite it? -openDialog.fileDoesntExist=The selected file does not exist -openDialog.targetDoesntExist=The target of the selected shortcut does not exist. - -# ExcelPane -ExcelPane.error.message=Error while saving the file ({0}) -ExcelPane.save=Save -ExcelPane.csv.wording=CSV Files - -#File selection panels -FileSelectionPanel.file=File: -FileSelectionPanel.change=Change -URIChooserDialog.noFileSelected=This button is disabled because no file is selected -URIChooserDialog.openButton.title=Open -URIChooserDialog.saveButton.title=Save -FileChooserPanel.title=Computer -FileChooserPanel.tooltip.save=Select this tab to save data to your local storage -FileChooserPanel.tooltip.open=Select this tab to read data from your local storage - -saveDialog.fileNotWritable=Sorry, this application can't write to this file.

Most commons causes are:
- The file is opened in another application.
- The file is located in a directory you are not allowed to write in.
- The file is write protected. -openDialog.fileNotReadable=Sorry, this application can't read this file.

Most commons causes are:
- The file is opened in another application.
- The owner of this file does not give you the right to read. - -SearchPanel.find=Find: -SearchPanel.ignoreMarks=Ignore accents -SearchPanel.ingnoreCase=Ignore case - -FileSelector.new=New -FileSelector.new.tooltip=Creates a new data file -FileSelector.open=Open -FileSelector.open.tooltip=Opens an existing data file -FileSelector.save=Save -FileSelector.save.tooltip=Saves data to disk -FileSelector.saveAs=Save as... -FileSelector.saveAs.tooltip=Saves data to a different location -FileSelector.unsavedChanges=Unsaved changes -FileSelector.unsavedChanges.question=There are unsaved changes. What do you want to do? - +#Translation tips: +#Some strings contain patterns like {0}. These patterns has to remain unchanged when you translate these string. +#For those who are familiar with MessageFormat, be aware that AJLib uses another library that do not use +#single quote as an escape char. So, you have not to double every single quote. + +#Menu localization +MainMenu.file=File +MainMenu.file.mnemonic=F +MainMenu.quit=Quit +MainMenu.quit.shortcut=Q +MainMenu.quit.mnemonic=Q +MainMenu.quit.tooltip=Quits the application + +#Generic wordings +GenericButton.ignore=Ignore +GenericButton.cancel=Cancel +GenericButton.cancel.toolTip=Cancels the current input +GenericButton.ok=Validate +GenericButton.ok.toolTip=Validates the current input +Generic.warning=Warning +Generic.error=Error + +Browser.unsupported.message=Unable to open a web browser.

The URL ({0}) has been copied to the clipboard.
You can now paste it to your web browser. + +# This dialog is opened when the file to save the data is already existing +# Dialog title +saveDialog.FileExist.title=Warning +# Dialog message +saveDialog.FileExist.message=File already exists. Would you like to overwrite it? +openDialog.fileDoesntExist=The selected file does not exist +openDialog.targetDoesntExist=The target of the selected shortcut does not exist. + +# ExcelPane +ExcelPane.error.message=Error while saving the file ({0}) +ExcelPane.save=Save +ExcelPane.csv.wording=CSV Files + +#File selection panels +FileSelectionPanel.file=File: +FileSelectionPanel.change=Change +URIChooserDialog.noFileSelected=This button is disabled because no file is selected +URIChooserDialog.openButton.title=Open +URIChooserDialog.saveButton.title=Save +FileChooserPanel.title=Computer +FileChooserPanel.tooltip.save=Select this tab to save data to your local storage +FileChooserPanel.tooltip.open=Select this tab to read data from your local storage + +saveDialog.fileNotWritable=Sorry, this application can't write to this file.

Most commons causes are:
- The file is opened in another application.
- The file is located in a directory you are not allowed to write in.
- The file is write protected. +openDialog.fileNotReadable=Sorry, this application can't read this file.

Most commons causes are:
- The file is opened in another application.
- The owner of this file does not give you the right to read. + +SearchPanel.find=Find: +SearchPanel.ignoreMarks=Ignore accents +SearchPanel.ingnoreCase=Ignore case + +FileSelector.new=New +FileSelector.new.tooltip=Creates a new data file +FileSelector.open=Open +FileSelector.open.tooltip=Opens an existing data file +FileSelector.save=Save +FileSelector.save.tooltip=Saves data to disk +FileSelector.saveAs=Save as... +FileSelector.saveAs.tooltip=Saves data to a different location +FileSelector.unsavedChanges=Unsaved changes +FileSelector.unsavedChanges.question=There are unsaved changes. What do you want to do? + diff --git a/src/main/resources/com/fathzer/soft/ajlib/Resources_fr.properties b/ajlib/src/main/resources/com/fathzer/soft/ajlib/Resources_fr.properties similarity index 97% rename from src/main/resources/com/fathzer/soft/ajlib/Resources_fr.properties rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/Resources_fr.properties index c7f6315..ae870ce 100644 --- a/src/main/resources/com/fathzer/soft/ajlib/Resources_fr.properties +++ b/ajlib/src/main/resources/com/fathzer/soft/ajlib/Resources_fr.properties @@ -1,60 +1,60 @@ -#Menu localization -MainMenu.file=Fichier -MainMenu.file.mnemonic=F -MainMenu.quit=Quitter -MainMenu.quit.shortcut=Q -MainMenu.quit.mnemonic=Q -MainMenu.quit.tooltip=Quitte l'application - -#Generic wordings -GenericButton.ignore=Ignorer -GenericButton.cancel=Annuler -GenericButton.cancel.toolTip=Annule la saisie en cours -GenericButton.ok=Valider -GenericButton.ok.toolTip=Valide la saisie en cours -Generic.warning=Alerte -Generic.error=Erreur - -Browser.unsupported.message=Impossible d'ouvrir un navigateur Web.

L'URL ({0}) a été copiée dans le presse-papiers.
Vous pouvez maintenant la coller dans votre navigateur Web. - -# This dialog is opened when the file to save the data is already existing -# Dialog title -saveDialog.FileExist.title=Attention -# Dialog message -saveDialog.FileExist.message=Le fichier existe déjà. Voulez-vous l'écraser ? -openDialog.fileDoesntExist=Le fichier sélectionné n'existe pas -openDialog.targetDoesntExist=La cible du raccourci sélectionné n'existe pas - -# ExcelPane -ExcelPane.error.message=L'erreur {0} est survenue lors de la sauvegarde du fichier -ExcelPane.save=Enregistrer -ExcelPane.csv.wording=Fichier CSV - -#File selection panel -FileSelectionPanel.file=Fichier : -FileSelectionPanel.change=Changer -URIChooserDialog.noFileSelected=Ce bouton est inactif car aucun fichier n'est sélectionné -URIChooserDialog.openButton.title=Ouvrir -URIChooserDialog.saveButton.title=Enregistrer -FileChooserPanel.title=Ordinateur -FileChooserPanel.tooltip.save=Selectionnez cet onglet pour sauver sur un support de stockage de votre ordinateur -FileChooserPanel.tooltip.open=Selectionnez cet onglet pour lire à partir d'un support de stockage de votre ordinateur - -#To be translated -saveDialog.fileNotWritable=Cette application ne peut pas écrire dans ce fichier.

Les causes courantes sont :
- Le fichier est ouvert dans une autre application.
- Le fichier est dans un dossier dans lequel vous n'êtes pas autorisé à écrire.
- Le fichier est protégé en écriture. -openDialog.fileNotReadable=Cette application ne peut pas lire ce fichier.

Les causes courantes sont :
- Le fichier est ouvert dans une autre application.
- Le propriétaire de ce fichier ne vous a pas donné le droit de le lire. - -SearchPanel.find=Chercher : -SearchPanel.ignoreMarks=Ignorer les accents -SearchPanel.ingnoreCase=Ignorer la casse - -FileSelector.new=Nouveau -FileSelector.new.tooltip=Crée un nouveau fichier de données -FileSelector.open=Ouvrir -FileSelector.open.tooltip=Ouvre un fichier de données -FileSelector.save=Enregistrer -FileSelector.save.tooltip=Enregistre les données sur disque -FileSelector.saveAs=Enregistrer sous... -FileSelector.saveAs.tooltip=Enregistre les données à un nouvel emplacement -FileSelector.unsavedChanges=Des modifications n'ont pas été sauvegardées -FileSelector.unsavedChanges.question=Des modifications n'ont pas été sauvegardées. Que fait-on ? +#Menu localization +MainMenu.file=Fichier +MainMenu.file.mnemonic=F +MainMenu.quit=Quitter +MainMenu.quit.shortcut=Q +MainMenu.quit.mnemonic=Q +MainMenu.quit.tooltip=Quitte l'application + +#Generic wordings +GenericButton.ignore=Ignorer +GenericButton.cancel=Annuler +GenericButton.cancel.toolTip=Annule la saisie en cours +GenericButton.ok=Valider +GenericButton.ok.toolTip=Valide la saisie en cours +Generic.warning=Alerte +Generic.error=Erreur + +Browser.unsupported.message=Impossible d'ouvrir un navigateur Web.

L'URL ({0}) a été copiée dans le presse-papiers.
Vous pouvez maintenant la coller dans votre navigateur Web. + +# This dialog is opened when the file to save the data is already existing +# Dialog title +saveDialog.FileExist.title=Attention +# Dialog message +saveDialog.FileExist.message=Le fichier existe déjà. Voulez-vous l'écraser ? +openDialog.fileDoesntExist=Le fichier sélectionné n'existe pas +openDialog.targetDoesntExist=La cible du raccourci sélectionné n'existe pas + +# ExcelPane +ExcelPane.error.message=L'erreur {0} est survenue lors de la sauvegarde du fichier +ExcelPane.save=Enregistrer +ExcelPane.csv.wording=Fichier CSV + +#File selection panel +FileSelectionPanel.file=Fichier : +FileSelectionPanel.change=Changer +URIChooserDialog.noFileSelected=Ce bouton est inactif car aucun fichier n'est sélectionné +URIChooserDialog.openButton.title=Ouvrir +URIChooserDialog.saveButton.title=Enregistrer +FileChooserPanel.title=Ordinateur +FileChooserPanel.tooltip.save=Selectionnez cet onglet pour sauver sur un support de stockage de votre ordinateur +FileChooserPanel.tooltip.open=Selectionnez cet onglet pour lire à partir d'un support de stockage de votre ordinateur + +#To be translated +saveDialog.fileNotWritable=Cette application ne peut pas écrire dans ce fichier.

Les causes courantes sont :
- Le fichier est ouvert dans une autre application.
- Le fichier est dans un dossier dans lequel vous n'êtes pas autorisé à écrire.
- Le fichier est protégé en écriture. +openDialog.fileNotReadable=Cette application ne peut pas lire ce fichier.

Les causes courantes sont :
- Le fichier est ouvert dans une autre application.
- Le propriétaire de ce fichier ne vous a pas donné le droit de le lire. + +SearchPanel.find=Chercher : +SearchPanel.ignoreMarks=Ignorer les accents +SearchPanel.ingnoreCase=Ignorer la casse + +FileSelector.new=Nouveau +FileSelector.new.tooltip=Crée un nouveau fichier de données +FileSelector.open=Ouvrir +FileSelector.open.tooltip=Ouvre un fichier de données +FileSelector.save=Enregistrer +FileSelector.save.tooltip=Enregistre les données sur disque +FileSelector.saveAs=Enregistrer sous... +FileSelector.saveAs.tooltip=Enregistre les données à un nouvel emplacement +FileSelector.unsavedChanges=Des modifications n'ont pas été sauvegardées +FileSelector.unsavedChanges.question=Des modifications n'ont pas été sauvegardées. Que fait-on ? diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/New.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/New.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/New.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/New.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/Open.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/Open.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/Open.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/Open.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/Save.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/Save.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/Save.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/Save.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/SaveAs.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/SaveAs.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/SaveAs.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/SaveAs.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/calendar.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/calendar.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/calendar.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/calendar.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-forward.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-forward.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-forward.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-forward.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-rewind.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-rewind.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-rewind.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/fast-rewind.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/forward.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/forward.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/forward.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/forward.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/rewind.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/rewind.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/rewind.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/rewind.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/stop.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/stop.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/stop.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/date/stop.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/down.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/down.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/down.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/down.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/first.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/first.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/first.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/first.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/last.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/last.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/last.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/last.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/next.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/next.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/next.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/next.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/previous.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/previous.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/previous.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/previous.png diff --git a/src/main/resources/com/fathzer/soft/ajlib/swing/widget/up.png b/ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/up.png similarity index 100% rename from src/main/resources/com/fathzer/soft/ajlib/swing/widget/up.png rename to ajlib/src/main/resources/com/fathzer/soft/ajlib/swing/widget/up.png diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/CoolDateFormatterTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/CoolDateFormatterTest.java similarity index 100% rename from src/test/java/com/fathzer/soft/ajlib/junit/CoolDateFormatterTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/CoolDateFormatterTest.java diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/CurrencyWidgetTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/CurrencyWidgetTest.java similarity index 96% rename from src/test/java/com/fathzer/soft/ajlib/junit/CurrencyWidgetTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/CurrencyWidgetTest.java index 68887cc..1a2b9a0 100644 --- a/src/test/java/com/fathzer/soft/ajlib/junit/CurrencyWidgetTest.java +++ b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/CurrencyWidgetTest.java @@ -1,46 +1,46 @@ -package com.fathzer.soft.ajlib.junit; - -import static org.junit.Assert.*; - -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Locale; - -import org.junit.Test; - -import com.fathzer.soft.ajlib.swing.widget.CurrencyWidget; -import com.fathzer.soft.ajlib.swing.widget.NumberWidget; - -public class CurrencyWidgetTest { - @Test - public void test() { - CurrencyWidget w = new CurrencyWidget(Locale.FRANCE); - double value = 5000.0; - w.setValue(value); - assertEquals("5 000,00 €", w.getText()); - assertEquals(value, w.getValue(), 0.001); - - value = -5000.0; - w.setValue(value); - assertEquals("-5 000,00 €", w.getText()); - assertEquals(value, w.getValue(), 0.001); - } - - private static class MyWidget extends NumberWidget { - private static final long serialVersionUID = 1L; - - @Override - public DecimalFormat patchJavaBug4510618(DecimalFormat format) { - return super.patchJavaBug4510618(format); - } - } - - @Test - public void fuckingJavaDecimalFormatTest() { - DecimalFormat format = (DecimalFormat)NumberFormat.getCurrencyInstance(Locale.FRENCH); - final String x = format.format(5000.0).replace((char)0x202F, ' ').replace((char)0x00A0, ' '); - - format = new MyWidget().patchJavaBug4510618(format); - assertEquals(x, format.format(5000.0)); - } -} +package com.fathzer.soft.ajlib.junit; + +import static org.junit.Assert.*; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +import org.junit.Test; + +import com.fathzer.soft.ajlib.swing.widget.CurrencyWidget; +import com.fathzer.soft.ajlib.swing.widget.NumberWidget; + +public class CurrencyWidgetTest { + @Test + public void test() { + CurrencyWidget w = new CurrencyWidget(Locale.FRANCE); + double value = 5000.0; + w.setValue(value); + assertEquals("5 000,00 €", w.getText()); + assertEquals(value, w.getValue(), 0.001); + + value = -5000.0; + w.setValue(value); + assertEquals("-5 000,00 €", w.getText()); + assertEquals(value, w.getValue(), 0.001); + } + + private static class MyWidget extends NumberWidget { + private static final long serialVersionUID = 1L; + + @Override + public DecimalFormat patchJavaBug4510618(DecimalFormat format) { + return super.patchJavaBug4510618(format); + } + } + + @Test + public void fuckingJavaDecimalFormatTest() { + DecimalFormat format = (DecimalFormat)NumberFormat.getCurrencyInstance(Locale.FRENCH); + final String x = format.format(5000.0).replace((char)0x202F, ' ').replace((char)0x00A0, ' '); + + format = new MyWidget().patchJavaBug4510618(format); + assertEquals(x, format.format(5000.0)); + } +} diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/DateWidgetTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/DateWidgetTest.java similarity index 96% rename from src/test/java/com/fathzer/soft/ajlib/junit/DateWidgetTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/DateWidgetTest.java index cf36053..d8c9d11 100644 --- a/src/test/java/com/fathzer/soft/ajlib/junit/DateWidgetTest.java +++ b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/DateWidgetTest.java @@ -1,75 +1,75 @@ -package com.fathzer.soft.ajlib.junit; - -import static org.junit.Assert.*; -import static org.junit.Assume.assumeFalse; - -import java.awt.GraphicsEnvironment; -import java.awt.event.KeyEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -import javax.swing.JFrame; - -import org.junit.Test; - -import com.fathzer.soft.ajlib.junit.utils.AbstractSwingTest; -import com.fathzer.soft.ajlib.swing.widget.date.DateField; -import com.fathzer.soft.ajlib.swing.widget.date.DateWidget; - -public class DateWidgetTest extends AbstractSwingTest { - - @Test - public void test() throws Throwable { - assumeFalse(GraphicsEnvironment.isHeadless()); - - final DateWidgetFrame test = new DateWidgetFrame(); - test.initTest(); - assertTrue(test.widget.isContentValid()); - assertTrue(test.widget.getDateField().isContentValid()); - MyListener widgetListener = new MyListener(); - test.widget.addPropertyChangeListener(DateWidget.CONTENT_VALID_PROPERTY, widgetListener); - MyListener fieldListener = new MyListener(); - test.widget.getDateField().addPropertyChangeListener(DateField.CONTENT_VALID_PROPERTY, fieldListener); - // Let some time to allow window to acquire the focus - robot.delay(100); - robot.keyPress(KeyEvent.VK_X); - robot.keyRelease(KeyEvent.VK_X); - robot.delay(50); - assertEquals("x", test.widget.getDateField().getText()); - assertFalse(test.widget.isContentValid()); - assertFalse(test.widget.getDateField().isContentValid()); - widgetListener.reset(); - fieldListener.reset(); - robot.keyPress(KeyEvent.VK_BACK_SPACE); - robot.keyRelease(KeyEvent.VK_BACK_SPACE); - robot.delay(50); - assertTrue(test.widget.getDateField().getText().isEmpty()); - assertTrue(test.widget.isContentValid()); - assertTrue(fieldListener.changed); - assertTrue(widgetListener.changed); - } - - private static final class MyListener implements PropertyChangeListener { - private boolean changed; - - @Override - public void propertyChange(PropertyChangeEvent evt) { - changed=true; - } - - public void reset() { - changed = false; - } - } - - private static class DateWidgetFrame extends AbstractSwingWindow { - private DateWidget widget; - - protected void populate(JFrame f) { - widget = new DateWidget(); - widget.setDate(null); - f.add(widget); - f.pack(); - } - } +package com.fathzer.soft.ajlib.junit; + +import static org.junit.Assert.*; +import static org.junit.Assume.assumeFalse; + +import java.awt.GraphicsEnvironment; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.JFrame; + +import org.junit.Test; + +import com.fathzer.soft.ajlib.junit.utils.AbstractSwingTest; +import com.fathzer.soft.ajlib.swing.widget.date.DateField; +import com.fathzer.soft.ajlib.swing.widget.date.DateWidget; + +public class DateWidgetTest extends AbstractSwingTest { + + @Test + public void test() throws Throwable { + assumeFalse(GraphicsEnvironment.isHeadless()); + + final DateWidgetFrame test = new DateWidgetFrame(); + test.initTest(); + assertTrue(test.widget.isContentValid()); + assertTrue(test.widget.getDateField().isContentValid()); + MyListener widgetListener = new MyListener(); + test.widget.addPropertyChangeListener(DateWidget.CONTENT_VALID_PROPERTY, widgetListener); + MyListener fieldListener = new MyListener(); + test.widget.getDateField().addPropertyChangeListener(DateField.CONTENT_VALID_PROPERTY, fieldListener); + // Let some time to allow window to acquire the focus + robot.delay(100); + robot.keyPress(KeyEvent.VK_X); + robot.keyRelease(KeyEvent.VK_X); + robot.delay(50); + assertEquals("x", test.widget.getDateField().getText()); + assertFalse(test.widget.isContentValid()); + assertFalse(test.widget.getDateField().isContentValid()); + widgetListener.reset(); + fieldListener.reset(); + robot.keyPress(KeyEvent.VK_BACK_SPACE); + robot.keyRelease(KeyEvent.VK_BACK_SPACE); + robot.delay(50); + assertTrue(test.widget.getDateField().getText().isEmpty()); + assertTrue(test.widget.isContentValid()); + assertTrue(fieldListener.changed); + assertTrue(widgetListener.changed); + } + + private static final class MyListener implements PropertyChangeListener { + private boolean changed; + + @Override + public void propertyChange(PropertyChangeEvent evt) { + changed=true; + } + + public void reset() { + changed = false; + } + } + + private static class DateWidgetFrame extends AbstractSwingWindow { + private DateWidget widget; + + protected void populate(JFrame f) { + widget = new DateWidget(); + widget.setDate(null); + f.add(widget); + f.pack(); + } + } } \ No newline at end of file diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/FileUtilsTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/FileUtilsTest.java similarity index 96% rename from src/test/java/com/fathzer/soft/ajlib/junit/FileUtilsTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/FileUtilsTest.java index 27f6f71..700a271 100644 --- a/src/test/java/com/fathzer/soft/ajlib/junit/FileUtilsTest.java +++ b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/FileUtilsTest.java @@ -1,26 +1,26 @@ -package com.fathzer.soft.ajlib.junit; - -import static org.junit.Assert.*; - -import java.io.File; - -import org.junit.Test; - -import com.fathzer.soft.ajlib.utilities.FileUtils; -import com.fathzer.soft.ajlib.utilities.StringUtils; - -/** Tests for StringUtils class. - * @see StringUtils - */ -public class FileUtilsTest { - - @Test - public void testExtension() { - assertEquals(".xml", FileUtils.getExtension(new File("x.xml"))); - assertEquals(null, FileUtils.getExtension(new File(""))); - assertEquals(".", FileUtils.getExtension(new File("."))); - assertEquals("x", FileUtils.getRootName(new File("x.xml"))); - assertEquals("", FileUtils.getRootName(new File(""))); - assertEquals("", FileUtils.getRootName(new File("."))); - } -} +package com.fathzer.soft.ajlib.junit; + +import static org.junit.Assert.*; + +import java.io.File; + +import org.junit.Test; + +import com.fathzer.soft.ajlib.utilities.FileUtils; +import com.fathzer.soft.ajlib.utilities.StringUtils; + +/** Tests for StringUtils class. + * @see StringUtils + */ +public class FileUtilsTest { + + @Test + public void testExtension() { + assertEquals(".xml", FileUtils.getExtension(new File("x.xml"))); + assertEquals(null, FileUtils.getExtension(new File(""))); + assertEquals(".", FileUtils.getExtension(new File("."))); + assertEquals("x", FileUtils.getRootName(new File("x.xml"))); + assertEquals("", FileUtils.getRootName(new File(""))); + assertEquals("", FileUtils.getRootName(new File("."))); + } +} diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/ListUtilsTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/ListUtilsTest.java similarity index 96% rename from src/test/java/com/fathzer/soft/ajlib/junit/ListUtilsTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/ListUtilsTest.java index c535f7f..3b381ae 100644 --- a/src/test/java/com/fathzer/soft/ajlib/junit/ListUtilsTest.java +++ b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/ListUtilsTest.java @@ -1,63 +1,63 @@ -package com.fathzer.soft.ajlib.junit; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.BeforeClass; -import org.junit.Test; - -import com.fathzer.soft.ajlib.utilities.ListUtils; -import com.fathzer.soft.ajlib.utilities.StringUtils; - -/** Tests for StringUtils class. - * @see StringUtils - */ -public class ListUtilsTest { - private static Collection list; - - @BeforeClass - public static void init() { - list = new ArrayList(); - for (int i = 0; i < 10; i++) { - list.add(i); - } - list = Collections.unmodifiableCollection(list); - } - - @Test - public void testOne() { - List tested = new ArrayList(list); - ListUtils.move(tested, 4, -2); - assertIs(new int[]{0,1,4,2,3,5,6,7,8,9},tested); - ListUtils.move(tested, 2, 2); - assertIs(new int[]{0,1,2,3,4,5,6,7,8,9},tested); - } - - @Test - public void testMany() { - List tested = new ArrayList(list); - ListUtils.move(tested, new int[]{4,8,9}, -3); - assertIs(new int[]{0,4,1,2,3,8,9,5,6,7},tested); - ListUtils.move(tested, new int[]{1,5,6}, 3); - assertIs(new int[]{0,1,2,3,4,5,6,7,8,9},tested); - } - - private void assertIs(int[] expected, List actual) { - if (expected.length!=actual.size()) { - err(expected, actual, "(different lengths"); - } - for (int i = 0; i < expected.length; i++) { - if (expected[i] != actual.get(i)) { - String message = "(expected["+i+"]="+expected[i]+" is not actual["+i+"]="+actual.get(i)+")"; - err(expected, actual, message); - } - } - } - - private void err(int[] expected, List actual, String message) { - throw new AssertionError("Expected <"+Arrays.toString(expected)+"> but was <"+actual+"> "+message); - } -} +package com.fathzer.soft.ajlib.junit; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.fathzer.soft.ajlib.utilities.ListUtils; +import com.fathzer.soft.ajlib.utilities.StringUtils; + +/** Tests for StringUtils class. + * @see StringUtils + */ +public class ListUtilsTest { + private static Collection list; + + @BeforeClass + public static void init() { + list = new ArrayList(); + for (int i = 0; i < 10; i++) { + list.add(i); + } + list = Collections.unmodifiableCollection(list); + } + + @Test + public void testOne() { + List tested = new ArrayList(list); + ListUtils.move(tested, 4, -2); + assertIs(new int[]{0,1,4,2,3,5,6,7,8,9},tested); + ListUtils.move(tested, 2, 2); + assertIs(new int[]{0,1,2,3,4,5,6,7,8,9},tested); + } + + @Test + public void testMany() { + List tested = new ArrayList(list); + ListUtils.move(tested, new int[]{4,8,9}, -3); + assertIs(new int[]{0,4,1,2,3,8,9,5,6,7},tested); + ListUtils.move(tested, new int[]{1,5,6}, 3); + assertIs(new int[]{0,1,2,3,4,5,6,7,8,9},tested); + } + + private void assertIs(int[] expected, List actual) { + if (expected.length!=actual.size()) { + err(expected, actual, "(different lengths"); + } + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual.get(i)) { + String message = "(expected["+i+"]="+expected[i]+" is not actual["+i+"]="+actual.get(i)+")"; + err(expected, actual, message); + } + } + } + + private void err(int[] expected, List actual, String message) { + throw new AssertionError("Expected <"+Arrays.toString(expected)+"> but was <"+actual+"> "+message); + } +} diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/LocalizationDataTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/LocalizationDataTest.java similarity index 96% rename from src/test/java/com/fathzer/soft/ajlib/junit/LocalizationDataTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/LocalizationDataTest.java index 4d7ff26..b04498b 100644 --- a/src/test/java/com/fathzer/soft/ajlib/junit/LocalizationDataTest.java +++ b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/LocalizationDataTest.java @@ -1,22 +1,22 @@ -package com.fathzer.soft.ajlib.junit; - -import static org.junit.Assert.*; - -import java.util.Locale; - - -import org.junit.Test; - -import com.fathzer.soft.ajlib.utilities.LocalizationData; - -public class LocalizationDataTest { - private static final String NAME = "name"; - - @Test - public void test() { - LocalizationData loc = new LocalizationData("com.fathzer.soft.ajlib.junit.Resources"); - assertEquals (loc.getString(NAME, Locale.US), Locale.US.getLanguage()); - assertEquals (loc.getString(NAME, Locale.FRANCE), Locale.FRANCE.getLanguage()); - } - -} +package com.fathzer.soft.ajlib.junit; + +import static org.junit.Assert.*; + +import java.util.Locale; + + +import org.junit.Test; + +import com.fathzer.soft.ajlib.utilities.LocalizationData; + +public class LocalizationDataTest { + private static final String NAME = "name"; + + @Test + public void test() { + LocalizationData loc = new LocalizationData("com.fathzer.soft.ajlib.junit.Resources"); + assertEquals (loc.getString(NAME, Locale.US), Locale.US.getLanguage()); + assertEquals (loc.getString(NAME, Locale.FRANCE), Locale.FRANCE.getLanguage()); + } + +} diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/StringUtilsTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/StringUtilsTest.java similarity index 96% rename from src/test/java/com/fathzer/soft/ajlib/junit/StringUtilsTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/StringUtilsTest.java index 99bbd1f..24c1148 100644 --- a/src/test/java/com/fathzer/soft/ajlib/junit/StringUtilsTest.java +++ b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/StringUtilsTest.java @@ -1,28 +1,28 @@ -package com.fathzer.soft.ajlib.junit; - -import static org.junit.Assert.*; - - -import org.junit.Test; - -import com.fathzer.soft.ajlib.utilities.StringUtils; - -/** Tests for StringUtils class. - * @see StringUtils - */ -public class StringUtilsTest { - - @Test - public void testSplit() { - // Tests empty imput string - assertArrayEquals(new String[]{""}, StringUtils.split("", ',')); - // Tests string which not contains the delimiter - assertArrayEquals(new String[]{"ABC"}, StringUtils.split("ABC", ',')); - // Tests standard use - assertArrayEquals(new String[]{"A","B","C"}, StringUtils.split("A,B,C", ',')); - // Tests string beginning and ending by the delimiter - assertArrayEquals(new String[]{"","A","","B","C",""}, StringUtils.split(",A,,B,C,", ',')); - // Test exotic delimiter - assertArrayEquals(new String[]{"A","B","C"}, StringUtils.split("A\\B\\C", '\\')); - } -} +package com.fathzer.soft.ajlib.junit; + +import static org.junit.Assert.*; + + +import org.junit.Test; + +import com.fathzer.soft.ajlib.utilities.StringUtils; + +/** Tests for StringUtils class. + * @see StringUtils + */ +public class StringUtilsTest { + + @Test + public void testSplit() { + // Tests empty imput string + assertArrayEquals(new String[]{""}, StringUtils.split("", ',')); + // Tests string which not contains the delimiter + assertArrayEquals(new String[]{"ABC"}, StringUtils.split("ABC", ',')); + // Tests standard use + assertArrayEquals(new String[]{"A","B","C"}, StringUtils.split("A,B,C", ',')); + // Tests string beginning and ending by the delimiter + assertArrayEquals(new String[]{"","A","","B","C",""}, StringUtils.split(",A,,B,C,", ',')); + // Test exotic delimiter + assertArrayEquals(new String[]{"A","B","C"}, StringUtils.split("A\\B\\C", '\\')); + } +} diff --git a/src/test/java/com/fathzer/soft/ajlib/junit/utils/AbstractSwingTest.java b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/utils/AbstractSwingTest.java similarity index 95% rename from src/test/java/com/fathzer/soft/ajlib/junit/utils/AbstractSwingTest.java rename to ajlib/src/test/java/com/fathzer/soft/ajlib/junit/utils/AbstractSwingTest.java index f64516b..08b5cdf 100644 --- a/src/test/java/com/fathzer/soft/ajlib/junit/utils/AbstractSwingTest.java +++ b/ajlib/src/test/java/com/fathzer/soft/ajlib/junit/utils/AbstractSwingTest.java @@ -1,62 +1,62 @@ -package com.fathzer.soft.ajlib.junit.utils; - -import java.awt.AWTException; -import java.awt.EventQueue; -import java.awt.FlowLayout; -import java.awt.GraphicsEnvironment; -import java.awt.Robot; - -import javax.swing.JFrame; - -import org.junit.BeforeClass; - -public abstract class AbstractSwingTest { - protected static Robot robot; - - @BeforeClass - public static void init() throws AWTException { - if (!GraphicsEnvironment.isHeadless()) { - robot = new Robot(); - } - } - - protected static abstract class AbstractSwingWindow { - private Throwable exception; - - public void initTest() throws InterruptedException, Throwable { - EventQueue.invokeLater(new Runnable() { - public void run() { - build(); - } - }); - synchronized (this) { - wait(); - } - if (exception!=null) { - throw exception; - } - } - - private void build() { - try { - init(); - } catch (Throwable e) { - exception = e; - } - synchronized (this) { - notifyAll(); - } - } - - private void init() { - JFrame f = new JFrame(); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.setLocationRelativeTo(null); - f.setLayout(new FlowLayout()); - populate(f); - f.setVisible(true); - } - - protected abstract void populate(JFrame f); - } +package com.fathzer.soft.ajlib.junit.utils; + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.GraphicsEnvironment; +import java.awt.Robot; + +import javax.swing.JFrame; + +import org.junit.BeforeClass; + +public abstract class AbstractSwingTest { + protected static Robot robot; + + @BeforeClass + public static void init() throws AWTException { + if (!GraphicsEnvironment.isHeadless()) { + robot = new Robot(); + } + } + + protected static abstract class AbstractSwingWindow { + private Throwable exception; + + public void initTest() throws InterruptedException, Throwable { + EventQueue.invokeLater(new Runnable() { + public void run() { + build(); + } + }); + synchronized (this) { + wait(); + } + if (exception!=null) { + throw exception; + } + } + + private void build() { + try { + init(); + } catch (Throwable e) { + exception = e; + } + synchronized (this) { + notifyAll(); + } + } + + private void init() { + JFrame f = new JFrame(); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setLocationRelativeTo(null); + f.setLayout(new FlowLayout()); + populate(f); + f.setVisible(true); + } + + protected abstract void populate(JFrame f); + } } \ No newline at end of file diff --git a/src/test/resources/com/fathzer/soft/ajlib/junit/Resources.properties b/ajlib/src/test/resources/com/fathzer/soft/ajlib/junit/Resources.properties similarity index 100% rename from src/test/resources/com/fathzer/soft/ajlib/junit/Resources.properties rename to ajlib/src/test/resources/com/fathzer/soft/ajlib/junit/Resources.properties diff --git a/src/test/resources/com/fathzer/soft/ajlib/junit/Resources_en.properties b/ajlib/src/test/resources/com/fathzer/soft/ajlib/junit/Resources_en.properties similarity index 100% rename from src/test/resources/com/fathzer/soft/ajlib/junit/Resources_en.properties rename to ajlib/src/test/resources/com/fathzer/soft/ajlib/junit/Resources_en.properties diff --git a/src/test/resources/com/fathzer/soft/ajlib/junit/Resources_fr.properties b/ajlib/src/test/resources/com/fathzer/soft/ajlib/junit/Resources_fr.properties similarity index 100% rename from src/test/resources/com/fathzer/soft/ajlib/junit/Resources_fr.properties rename to ajlib/src/test/resources/com/fathzer/soft/ajlib/junit/Resources_fr.properties diff --git a/demo/pom.xml b/demo/pom.xml new file mode 100644 index 0000000..e4b7a3b --- /dev/null +++ b/demo/pom.xml @@ -0,0 +1,50 @@ + + 4.0.0 + + com.fathzer + ajlib-parent-pom + 1.0.0 + + ajlib-demo + 0.3.16 + + jar + + A-JLib-demo + + + + com.fathzer + ajlib + 0.3.16 + + + + + + + maven-shade-plugin + 3.4.1 + + true + + + com.fathzer.soft.ajlib.swing.demo.AJLibDemo + + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemo.java b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemo.java similarity index 95% rename from src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemo.java rename to demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemo.java index 0a3089e..1d672e6 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemo.java +++ b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemo.java @@ -1,39 +1,39 @@ -package com.fathzer.soft.ajlib.swing.demo; - -import javax.swing.JPanel; - -import com.fathzer.soft.ajlib.swing.framework.Application; - -/** This is a simple demonstration application. */ -public class AJLibDemo extends Application { - public static final AJLibDemo DEMO = new AJLibDemo(); - - private AJLibDemoPanel demoPanel; - - private AJLibDemo() { - } - - @Override - public String getName() { - return "AJLibDemo"; - } - - @Override - protected JPanel buildMainPanel() { - if (demoPanel==null) { - demoPanel = new AJLibDemoPanel(); - } - return demoPanel; - } - - /** - * @param args Program arguments - */ - public static void main(String[] args) { - DEMO.launch(); - } - - public static void setMessage(String text) { - DEMO.demoPanel.setMessage(text); - } -} +package com.fathzer.soft.ajlib.swing.demo; + +import javax.swing.JPanel; + +import com.fathzer.soft.ajlib.swing.framework.Application; + +/** This is a simple demonstration application. */ +public class AJLibDemo extends Application { + public static final AJLibDemo DEMO = new AJLibDemo(); + + private AJLibDemoPanel demoPanel; + + private AJLibDemo() { + } + + @Override + public String getName() { + return "AJLibDemo"; + } + + @Override + protected JPanel buildMainPanel() { + if (demoPanel==null) { + demoPanel = new AJLibDemoPanel(); + } + return demoPanel; + } + + /** + * @param args Program arguments + */ + public static void main(String[] args) { + DEMO.launch(); + } + + public static void setMessage(String text) { + DEMO.demoPanel.setMessage(text); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemoPanel.java b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemoPanel.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemoPanel.java rename to demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemoPanel.java index 11d7046..b121340 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemoPanel.java +++ b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/AJLibDemoPanel.java @@ -1,59 +1,59 @@ -package com.fathzer.soft.ajlib.swing.demo; - -import javax.swing.JPanel; -import javax.swing.JLabel; -import java.awt.GridBagLayout; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import javax.swing.JTabbedPane; - -@SuppressWarnings("serial") -public class AJLibDemoPanel extends JPanel { - - private JLabel messageLabel; - - /** - * Create the panel. - */ - public AJLibDemoPanel() { - - initialize(); - } - private void initialize() { - GridBagLayout gridBagLayout = new GridBagLayout(); - setLayout(gridBagLayout); - - JTabbedPane panel = new JTabbedPane(); - GridBagConstraints gbcPanel = new GridBagConstraints(); - gbcPanel.weightx = 1.0; - gbcPanel.weighty = 1.0; - gbcPanel.fill = GridBagConstraints.BOTH; - gbcPanel.gridx = 0; - gbcPanel.gridy = 0; - add(panel, gbcPanel); - - panel.add("widget", new WidgetsDemoPanel()); - - panel.add("widget.date", new DateDemoPanel()); - - panel.add("rotating label", new RotatingLabelPanel()); - - panel.add("worker", new WorkerDemoPanel()); - - panel.add("dialog", new DialogsPanel()); - - messageLabel = new JLabel(" "); - GridBagConstraints gbcMessageLabel = new GridBagConstraints(); - gbcMessageLabel.insets = new Insets(5, 5, 0, 5); - gbcMessageLabel.weightx = 1.0; - gbcMessageLabel.fill = GridBagConstraints.HORIZONTAL; - gbcMessageLabel.anchor = GridBagConstraints.NORTHWEST; - gbcMessageLabel.gridx = 0; - gbcMessageLabel.gridy = 1; - add(messageLabel, gbcMessageLabel); - } - - public void setMessage(String text) { - messageLabel.setText(text); - } -} +package com.fathzer.soft.ajlib.swing.demo; + +import javax.swing.JPanel; +import javax.swing.JLabel; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import javax.swing.JTabbedPane; + +@SuppressWarnings("serial") +public class AJLibDemoPanel extends JPanel { + + private JLabel messageLabel; + + /** + * Create the panel. + */ + public AJLibDemoPanel() { + + initialize(); + } + private void initialize() { + GridBagLayout gridBagLayout = new GridBagLayout(); + setLayout(gridBagLayout); + + JTabbedPane panel = new JTabbedPane(); + GridBagConstraints gbcPanel = new GridBagConstraints(); + gbcPanel.weightx = 1.0; + gbcPanel.weighty = 1.0; + gbcPanel.fill = GridBagConstraints.BOTH; + gbcPanel.gridx = 0; + gbcPanel.gridy = 0; + add(panel, gbcPanel); + + panel.add("widget", new WidgetsDemoPanel()); + + panel.add("widget.date", new DateDemoPanel()); + + panel.add("rotating label", new RotatingLabelPanel()); + + panel.add("worker", new WorkerDemoPanel()); + + panel.add("dialog", new DialogsPanel()); + + messageLabel = new JLabel(" "); + GridBagConstraints gbcMessageLabel = new GridBagConstraints(); + gbcMessageLabel.insets = new Insets(5, 5, 0, 5); + gbcMessageLabel.weightx = 1.0; + gbcMessageLabel.fill = GridBagConstraints.HORIZONTAL; + gbcMessageLabel.anchor = GridBagConstraints.NORTHWEST; + gbcMessageLabel.gridx = 0; + gbcMessageLabel.gridy = 1; + add(messageLabel, gbcMessageLabel); + } + + public void setMessage(String text) { + messageLabel.setText(text); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/demo/DateDemoPanel.java b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/DateDemoPanel.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/demo/DateDemoPanel.java rename to demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/DateDemoPanel.java index 5332f9b..cd4e302 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/demo/DateDemoPanel.java +++ b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/DateDemoPanel.java @@ -1,101 +1,101 @@ -package com.fathzer.soft.ajlib.swing.demo; - -import javax.swing.JPanel; -import java.awt.GridBagLayout; -import javax.swing.JLabel; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.DateFormat; -import java.util.Date; - -import javax.swing.border.TitledBorder; - -import com.fathzer.soft.ajlib.swing.widget.date.CalendarWidget; -import com.fathzer.soft.ajlib.swing.widget.date.DateField; -import com.fathzer.soft.ajlib.swing.widget.date.DateWidget; - -@SuppressWarnings("serial") -public class DateDemoPanel extends JPanel { - private DateField textField; - - /** - * Create the panel. - */ - public DateDemoPanel() { - GridBagLayout gridBagLayout = new GridBagLayout(); - setLayout(gridBagLayout); - - PropertyChangeListener listener = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - Date old = (Date) evt.getOldValue(); - Date date = (Date) evt.getNewValue(); - DateFormat format = DateFormat.getDateInstance(DateFormat.MEDIUM); - String oldString = old == null ? "?" : format.format(old); - String dateString = date == null ? "?" : format.format(date); - AJLibDemo.setMessage("Date changed from "+oldString+" to "+dateString); - } - }; - - JLabel lblNewLabel = new JLabel("DateWidget:"); - GridBagConstraints gbcLblNewLabel = new GridBagConstraints(); - gbcLblNewLabel.insets = new Insets(0, 5, 5, 5); - gbcLblNewLabel.anchor = GridBagConstraints.WEST; - gbcLblNewLabel.gridx = 0; - gbcLblNewLabel.gridy = 0; - add(lblNewLabel, gbcLblNewLabel); - - DateWidget panel = new DateWidget(); - panel.addPropertyChangeListener(DateWidget.DATE_PROPERTY, listener); - GridBagConstraints gbcPanel = new GridBagConstraints(); - gbcPanel.insets = new Insets(0, 0, 5, 0); - gbcPanel.anchor = GridBagConstraints.NORTHWEST; - gbcPanel.gridx = 1; - gbcPanel.gridy = 0; - add(panel, gbcPanel); - - JLabel lblDatefield = new JLabel("DateField:"); - GridBagConstraints gbcLblDatefield = new GridBagConstraints(); - gbcLblDatefield.anchor = GridBagConstraints.WEST; - gbcLblDatefield.insets = new Insets(0, 5, 5, 5); - gbcLblDatefield.gridx = 0; - gbcLblDatefield.gridy = 1; - add(lblDatefield, gbcLblDatefield); - - textField = new DateField(); - textField.addPropertyChangeListener(DateField.DATE_PROPERTY, listener); - GridBagConstraints gbcTextField = new GridBagConstraints(); - gbcTextField.insets = new Insets(0, 0, 5, 0); - gbcTextField.anchor = GridBagConstraints.WEST; - gbcTextField.gridx = 1; - gbcTextField.gridy = 1; - add(textField, gbcTextField); - textField.setColumns(10); - - JPanel panel1 = new JPanel(); - panel1.setBorder(new TitledBorder(null, "CalendarWidget", TitledBorder.LEADING, TitledBorder.TOP, null, null)); - GridBagConstraints gbcPanel1 = new GridBagConstraints(); - gbcPanel1.anchor = GridBagConstraints.NORTHWEST; - gbcPanel1.weighty = 1.0; - gbcPanel1.weightx = 1.0; - gbcPanel1.gridwidth = 2; - gbcPanel1.insets = new Insets(0, 0, 0, 5); - gbcPanel1.gridx = 0; - gbcPanel1.gridy = 2; - add(panel1, gbcPanel1); - GridBagLayout gblPanel1 = new GridBagLayout(); - panel1.setLayout(gblPanel1); - - CalendarWidget calendarWidget = new CalendarWidget(); - calendarWidget.addPropertyChangeListener(CalendarWidget.DATE_PROPERTY, listener); - GridBagConstraints gbcCalendarWidget = new GridBagConstraints(); - gbcCalendarWidget.anchor = GridBagConstraints.WEST; - gbcCalendarWidget.insets = new Insets(0, 0, 5, 0); - gbcCalendarWidget.gridwidth = 2; - gbcCalendarWidget.gridx = 0; - gbcCalendarWidget.gridy = 0; - panel1.add(calendarWidget, gbcCalendarWidget); - } -} +package com.fathzer.soft.ajlib.swing.demo; + +import javax.swing.JPanel; +import java.awt.GridBagLayout; +import javax.swing.JLabel; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.DateFormat; +import java.util.Date; + +import javax.swing.border.TitledBorder; + +import com.fathzer.soft.ajlib.swing.widget.date.CalendarWidget; +import com.fathzer.soft.ajlib.swing.widget.date.DateField; +import com.fathzer.soft.ajlib.swing.widget.date.DateWidget; + +@SuppressWarnings("serial") +public class DateDemoPanel extends JPanel { + private DateField textField; + + /** + * Create the panel. + */ + public DateDemoPanel() { + GridBagLayout gridBagLayout = new GridBagLayout(); + setLayout(gridBagLayout); + + PropertyChangeListener listener = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + Date old = (Date) evt.getOldValue(); + Date date = (Date) evt.getNewValue(); + DateFormat format = DateFormat.getDateInstance(DateFormat.MEDIUM); + String oldString = old == null ? "?" : format.format(old); + String dateString = date == null ? "?" : format.format(date); + AJLibDemo.setMessage("Date changed from "+oldString+" to "+dateString); + } + }; + + JLabel lblNewLabel = new JLabel("DateWidget:"); + GridBagConstraints gbcLblNewLabel = new GridBagConstraints(); + gbcLblNewLabel.insets = new Insets(0, 5, 5, 5); + gbcLblNewLabel.anchor = GridBagConstraints.WEST; + gbcLblNewLabel.gridx = 0; + gbcLblNewLabel.gridy = 0; + add(lblNewLabel, gbcLblNewLabel); + + DateWidget panel = new DateWidget(); + panel.addPropertyChangeListener(DateWidget.DATE_PROPERTY, listener); + GridBagConstraints gbcPanel = new GridBagConstraints(); + gbcPanel.insets = new Insets(0, 0, 5, 0); + gbcPanel.anchor = GridBagConstraints.NORTHWEST; + gbcPanel.gridx = 1; + gbcPanel.gridy = 0; + add(panel, gbcPanel); + + JLabel lblDatefield = new JLabel("DateField:"); + GridBagConstraints gbcLblDatefield = new GridBagConstraints(); + gbcLblDatefield.anchor = GridBagConstraints.WEST; + gbcLblDatefield.insets = new Insets(0, 5, 5, 5); + gbcLblDatefield.gridx = 0; + gbcLblDatefield.gridy = 1; + add(lblDatefield, gbcLblDatefield); + + textField = new DateField(); + textField.addPropertyChangeListener(DateField.DATE_PROPERTY, listener); + GridBagConstraints gbcTextField = new GridBagConstraints(); + gbcTextField.insets = new Insets(0, 0, 5, 0); + gbcTextField.anchor = GridBagConstraints.WEST; + gbcTextField.gridx = 1; + gbcTextField.gridy = 1; + add(textField, gbcTextField); + textField.setColumns(10); + + JPanel panel1 = new JPanel(); + panel1.setBorder(new TitledBorder(null, "CalendarWidget", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + GridBagConstraints gbcPanel1 = new GridBagConstraints(); + gbcPanel1.anchor = GridBagConstraints.NORTHWEST; + gbcPanel1.weighty = 1.0; + gbcPanel1.weightx = 1.0; + gbcPanel1.gridwidth = 2; + gbcPanel1.insets = new Insets(0, 0, 0, 5); + gbcPanel1.gridx = 0; + gbcPanel1.gridy = 2; + add(panel1, gbcPanel1); + GridBagLayout gblPanel1 = new GridBagLayout(); + panel1.setLayout(gblPanel1); + + CalendarWidget calendarWidget = new CalendarWidget(); + calendarWidget.addPropertyChangeListener(CalendarWidget.DATE_PROPERTY, listener); + GridBagConstraints gbcCalendarWidget = new GridBagConstraints(); + gbcCalendarWidget.anchor = GridBagConstraints.WEST; + gbcCalendarWidget.insets = new Insets(0, 0, 5, 0); + gbcCalendarWidget.gridwidth = 2; + gbcCalendarWidget.gridx = 0; + gbcCalendarWidget.gridy = 0; + panel1.add(calendarWidget, gbcCalendarWidget); + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/demo/DialogsPanel.java b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/DialogsPanel.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/demo/DialogsPanel.java rename to demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/DialogsPanel.java index d15f378..c99796b 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/demo/DialogsPanel.java +++ b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/DialogsPanel.java @@ -1,80 +1,80 @@ -package com.fathzer.soft.ajlib.swing.demo; - -import javax.swing.JPanel; -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; - -import com.fathzer.soft.ajlib.swing.Utils; -import com.fathzer.soft.ajlib.swing.dialog.AbstractDialog; -import com.fathzer.soft.ajlib.swing.dialog.FileChooser; -import com.fathzer.soft.ajlib.swing.widget.HTMLPane; - - -import java.awt.event.ActionListener; -import java.io.File; -import java.awt.event.ActionEvent; - -@SuppressWarnings("serial") -public class DialogsPanel extends JPanel { - private JButton btnOpenDialog; - private JButton btnOpenDirectoryChooser; - - /** - * Create the panel. - */ - public DialogsPanel() { - add(getBtnOpenDialog()); - add(getBtnOpenDirectoryChooser()); - } - - private JButton getBtnOpenDialog() { - if (btnOpenDialog == null) { - btnOpenDialog = new JButton("Open dialog"); - btnOpenDialog.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - AbstractDialog dialog = new AbstractDialog(Utils.getOwnerWindow(btnOpenDialog),"a very simple panel",null) { - @Override - protected JPanel createCenterPane() { - JPanel panel = new JPanel(); - HTMLPane htmlPane = new HTMLPane("This is a very simple dialog, displaying a link." + - "
You should override createCentePane and buildResult methods to create more complex dialogs."); - panel.add(htmlPane); - return panel; - } - - @Override - protected Boolean buildResult() { - return Boolean.TRUE; - } - }; - dialog.setVisible(true); - if (dialog.getResult()!=null) { - AJLibDemo.setMessage("The dialog was validated"); - } else { - AJLibDemo.setMessage("The dialog was cancelled"); - } - } - }); - } - return btnOpenDialog; - } - private String path; - private JButton getBtnOpenDirectoryChooser() { - if (btnOpenDirectoryChooser == null) { - btnOpenDirectoryChooser = new JButton("Open directory chooser"); - btnOpenDirectoryChooser.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JFileChooser chooser = new FileChooser(path); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - File file = chooser.showOpenDialog(DialogsPanel.this)==JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; - if (file!=null) { - path = file.getPath(); - JOptionPane.showMessageDialog(DialogsPanel.this, "You selected "+path); - } - } - }); - } - return btnOpenDirectoryChooser; - } -} +package com.fathzer.soft.ajlib.swing.demo; + +import javax.swing.JPanel; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; + +import com.fathzer.soft.ajlib.swing.Utils; +import com.fathzer.soft.ajlib.swing.dialog.AbstractDialog; +import com.fathzer.soft.ajlib.swing.dialog.FileChooser; +import com.fathzer.soft.ajlib.swing.widget.HTMLPane; + + +import java.awt.event.ActionListener; +import java.io.File; +import java.awt.event.ActionEvent; + +@SuppressWarnings("serial") +public class DialogsPanel extends JPanel { + private JButton btnOpenDialog; + private JButton btnOpenDirectoryChooser; + + /** + * Create the panel. + */ + public DialogsPanel() { + add(getBtnOpenDialog()); + add(getBtnOpenDirectoryChooser()); + } + + private JButton getBtnOpenDialog() { + if (btnOpenDialog == null) { + btnOpenDialog = new JButton("Open dialog"); + btnOpenDialog.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + AbstractDialog dialog = new AbstractDialog(Utils.getOwnerWindow(btnOpenDialog),"a very simple panel",null) { + @Override + protected JPanel createCenterPane() { + JPanel panel = new JPanel(); + HTMLPane htmlPane = new HTMLPane("This is a very simple dialog, displaying a link." + + "
You should override createCentePane and buildResult methods to create more complex dialogs."); + panel.add(htmlPane); + return panel; + } + + @Override + protected Boolean buildResult() { + return Boolean.TRUE; + } + }; + dialog.setVisible(true); + if (dialog.getResult()!=null) { + AJLibDemo.setMessage("The dialog was validated"); + } else { + AJLibDemo.setMessage("The dialog was cancelled"); + } + } + }); + } + return btnOpenDialog; + } + private String path; + private JButton getBtnOpenDirectoryChooser() { + if (btnOpenDirectoryChooser == null) { + btnOpenDirectoryChooser = new JButton("Open directory chooser"); + btnOpenDirectoryChooser.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new FileChooser(path); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + File file = chooser.showOpenDialog(DialogsPanel.this)==JFileChooser.APPROVE_OPTION ? chooser.getSelectedFile() : null; + if (file!=null) { + path = file.getPath(); + JOptionPane.showMessageDialog(DialogsPanel.this, "You selected "+path); + } + } + }); + } + return btnOpenDirectoryChooser; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/demo/RotatingLabelPanel.java b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/RotatingLabelPanel.java similarity index 100% rename from src/main/java/com/fathzer/soft/ajlib/swing/demo/RotatingLabelPanel.java rename to demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/RotatingLabelPanel.java diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/demo/WidgetsDemoPanel.java b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/WidgetsDemoPanel.java similarity index 97% rename from src/main/java/com/fathzer/soft/ajlib/swing/demo/WidgetsDemoPanel.java rename to demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/WidgetsDemoPanel.java index 1d12688..8a35956 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/demo/WidgetsDemoPanel.java +++ b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/WidgetsDemoPanel.java @@ -1,190 +1,190 @@ -package com.fathzer.soft.ajlib.swing.demo; - -import javax.swing.JPanel; -import javax.swing.JLabel; - -import com.fathzer.soft.ajlib.swing.widget.CurrencyWidget; -import com.fathzer.soft.ajlib.swing.widget.NumberWidget; -import com.fathzer.soft.ajlib.swing.widget.TextWidget; - -import java.awt.Color; -import java.awt.GridBagLayout; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.text.MessageFormat; -import com.fathzer.soft.ajlib.swing.widget.PageSelector; - - -public class WidgetsDemoPanel extends JPanel { - private static final class DefaultPropertyChangeListener implements PropertyChangeListener { - private String what; - private DefaultPropertyChangeListener(String what) { - this.what = what; - } - @Override - public void propertyChange(PropertyChangeEvent evt) { - String message; - if (evt.getOldValue()==null) { - message = MessageFormat.format("{0} is set to {1}", what, evt.getNewValue()); - } else if (evt.getNewValue()==null) { - message = MessageFormat.format("{0} is wrong", what); - } else { - message = MessageFormat.format("{0} changed from {1} to {2}", what, evt.getOldValue(), evt.getNewValue()); - } - AJLibDemo.setMessage(message); - } - } - private static final long serialVersionUID = 1L; - private JLabel lblTextwidget; - private TextWidget textWidget; - private JLabel lblNumberWidget; - private NumberWidget numberWidget; - private JLabel lblNewLabel; - private CurrencyWidget currencyWidget; - private PageSelector pageSelector; - private JLabel lblNewLabel1; - - /** - * Create the panel. - */ - public WidgetsDemoPanel() { - - initialize(); - } - private void initialize() { - GridBagLayout gridBagLayout = new GridBagLayout(); - setLayout(gridBagLayout); - GridBagConstraints gbcLblTextwidget = new GridBagConstraints(); - gbcLblTextwidget.fill = GridBagConstraints.BOTH; - gbcLblTextwidget.insets = new Insets(0, 0, 5, 5); - gbcLblTextwidget.gridx = 0; - gbcLblTextwidget.gridy = 0; - add(getLblTextwidget(), gbcLblTextwidget); - GridBagConstraints gbcTextWidget = new GridBagConstraints(); - gbcTextWidget.fill = GridBagConstraints.HORIZONTAL; - gbcTextWidget.insets = new Insets(0, 0, 5, 0); - gbcTextWidget.gridx = 1; - gbcTextWidget.gridy = 0; - add(getTextWidget(), gbcTextWidget); - GridBagConstraints gbcLblNumberWidget = new GridBagConstraints(); - gbcLblNumberWidget.anchor = GridBagConstraints.WEST; - gbcLblNumberWidget.insets = new Insets(0, 0, 5, 5); - gbcLblNumberWidget.gridx = 0; - gbcLblNumberWidget.gridy = 1; - add(getLblNumberWidget(), gbcLblNumberWidget); - GridBagConstraints gbcNumberWidget = new GridBagConstraints(); - gbcNumberWidget.insets = new Insets(0, 0, 5, 0); - gbcNumberWidget.weightx = 1.0; - gbcNumberWidget.fill = GridBagConstraints.HORIZONTAL; - gbcNumberWidget.gridx = 1; - gbcNumberWidget.gridy = 1; - add(getNumberWidget(), gbcNumberWidget); - GridBagConstraints gbcLblNewLabel = new GridBagConstraints(); - gbcLblNewLabel.anchor = GridBagConstraints.WEST; - gbcLblNewLabel.insets = new Insets(0, 0, 5, 5); - gbcLblNewLabel.gridx = 0; - gbcLblNewLabel.gridy = 2; - add(getLblNewLabel(), gbcLblNewLabel); - GridBagConstraints gbcCurrencyWidget = new GridBagConstraints(); - gbcCurrencyWidget.insets = new Insets(0, 0, 5, 0); - gbcCurrencyWidget.fill = GridBagConstraints.HORIZONTAL; - gbcCurrencyWidget.gridx = 1; - gbcCurrencyWidget.gridy = 2; - add(getCurrencyWidget(), gbcCurrencyWidget); - GridBagConstraints gbcLblNewLabel1 = new GridBagConstraints(); - gbcLblNewLabel1.anchor = GridBagConstraints.WEST; - gbcLblNewLabel1.insets = new Insets(0, 0, 0, 5); - gbcLblNewLabel1.gridx = 0; - gbcLblNewLabel1.gridy = 3; - add(getLblNewLabel1(), gbcLblNewLabel1); - GridBagConstraints gbcPageSelector = new GridBagConstraints(); - gbcPageSelector.anchor = GridBagConstraints.WEST; - gbcPageSelector.gridx = 1; - gbcPageSelector.gridy = 3; - add(getPageSelector(), gbcPageSelector); - } - - private JLabel getLblTextwidget() { - if (lblTextwidget == null) { - lblTextwidget = new JLabel("TextWidget:"); - } - return lblTextwidget; - } - private TextWidget getTextWidget() { - if (textWidget == null) { - textWidget = new TextWidget(10); - textWidget.setPredefined(new String[]{"dummy", "this string", "another one", "next one", "last one", "One more"}, 2); - textWidget.addPropertyChangeListener(TextWidget.TEXT_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - AJLibDemo.setMessage("Text changed from "+evt.getOldValue()+" to "+evt.getNewValue()); - } - }); - } - return textWidget; - } - private JLabel getLblNumberWidget() { - if (lblNumberWidget == null) { - lblNumberWidget = new JLabel("Number widget:"); - } - return lblNumberWidget; - } - private NumberWidget getNumberWidget() { - if (numberWidget == null) { - numberWidget = new NumberWidget(); - numberWidget.setToolTipText("This widget allows you to enter a double."); - numberWidget.setColumns(10); - numberWidget.addPropertyChangeListener(NumberWidget.VALUE_PROPERTY, new DefaultPropertyChangeListener("Number")); - numberWidget.addPropertyChangeListener(NumberWidget.CONTENT_VALID_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - Color color = (Boolean) evt.getNewValue()?Color.WHITE:Color.RED; - numberWidget.setBackground(color); - } - }); - } - return numberWidget; - } - private JLabel getLblNewLabel() { - if (lblNewLabel == null) { - lblNewLabel = new JLabel("Currency Widget:"); - } - return lblNewLabel; - } - private CurrencyWidget getCurrencyWidget() { - if (currencyWidget == null) { - currencyWidget = new CurrencyWidget(); - currencyWidget.setColumns(10); - currencyWidget.addPropertyChangeListener(NumberWidget.VALUE_PROPERTY, new DefaultPropertyChangeListener("Amount")); - currencyWidget.addPropertyChangeListener(NumberWidget.CONTENT_VALID_PROPERTY, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - Color color = (Boolean) evt.getNewValue()?Color.WHITE:Color.RED; - currencyWidget.setBackground(color); - } - }); - } - return currencyWidget; - } - private PageSelector getPageSelector() { - if (pageSelector == null) { - pageSelector = new PageSelector(); - pageSelector.setPageCount(5); - pageSelector.addPropertyChangeListener(PageSelector.PAGE_SELECTED_PROPERTY_NAME, new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - AJLibDemo.setMessage("Page set to "+evt.getNewValue()); - } - }); - } - return pageSelector; - } - private JLabel getLblNewLabel1() { - if (lblNewLabel1 == null) { - lblNewLabel1 = new JLabel("Page selector:"); - } - return lblNewLabel1; - } -} +package com.fathzer.soft.ajlib.swing.demo; + +import javax.swing.JPanel; +import javax.swing.JLabel; + +import com.fathzer.soft.ajlib.swing.widget.CurrencyWidget; +import com.fathzer.soft.ajlib.swing.widget.NumberWidget; +import com.fathzer.soft.ajlib.swing.widget.TextWidget; + +import java.awt.Color; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.MessageFormat; +import com.fathzer.soft.ajlib.swing.widget.PageSelector; + + +public class WidgetsDemoPanel extends JPanel { + private static final class DefaultPropertyChangeListener implements PropertyChangeListener { + private String what; + private DefaultPropertyChangeListener(String what) { + this.what = what; + } + @Override + public void propertyChange(PropertyChangeEvent evt) { + String message; + if (evt.getOldValue()==null) { + message = MessageFormat.format("{0} is set to {1}", what, evt.getNewValue()); + } else if (evt.getNewValue()==null) { + message = MessageFormat.format("{0} is wrong", what); + } else { + message = MessageFormat.format("{0} changed from {1} to {2}", what, evt.getOldValue(), evt.getNewValue()); + } + AJLibDemo.setMessage(message); + } + } + private static final long serialVersionUID = 1L; + private JLabel lblTextwidget; + private TextWidget textWidget; + private JLabel lblNumberWidget; + private NumberWidget numberWidget; + private JLabel lblNewLabel; + private CurrencyWidget currencyWidget; + private PageSelector pageSelector; + private JLabel lblNewLabel1; + + /** + * Create the panel. + */ + public WidgetsDemoPanel() { + + initialize(); + } + private void initialize() { + GridBagLayout gridBagLayout = new GridBagLayout(); + setLayout(gridBagLayout); + GridBagConstraints gbcLblTextwidget = new GridBagConstraints(); + gbcLblTextwidget.fill = GridBagConstraints.BOTH; + gbcLblTextwidget.insets = new Insets(0, 0, 5, 5); + gbcLblTextwidget.gridx = 0; + gbcLblTextwidget.gridy = 0; + add(getLblTextwidget(), gbcLblTextwidget); + GridBagConstraints gbcTextWidget = new GridBagConstraints(); + gbcTextWidget.fill = GridBagConstraints.HORIZONTAL; + gbcTextWidget.insets = new Insets(0, 0, 5, 0); + gbcTextWidget.gridx = 1; + gbcTextWidget.gridy = 0; + add(getTextWidget(), gbcTextWidget); + GridBagConstraints gbcLblNumberWidget = new GridBagConstraints(); + gbcLblNumberWidget.anchor = GridBagConstraints.WEST; + gbcLblNumberWidget.insets = new Insets(0, 0, 5, 5); + gbcLblNumberWidget.gridx = 0; + gbcLblNumberWidget.gridy = 1; + add(getLblNumberWidget(), gbcLblNumberWidget); + GridBagConstraints gbcNumberWidget = new GridBagConstraints(); + gbcNumberWidget.insets = new Insets(0, 0, 5, 0); + gbcNumberWidget.weightx = 1.0; + gbcNumberWidget.fill = GridBagConstraints.HORIZONTAL; + gbcNumberWidget.gridx = 1; + gbcNumberWidget.gridy = 1; + add(getNumberWidget(), gbcNumberWidget); + GridBagConstraints gbcLblNewLabel = new GridBagConstraints(); + gbcLblNewLabel.anchor = GridBagConstraints.WEST; + gbcLblNewLabel.insets = new Insets(0, 0, 5, 5); + gbcLblNewLabel.gridx = 0; + gbcLblNewLabel.gridy = 2; + add(getLblNewLabel(), gbcLblNewLabel); + GridBagConstraints gbcCurrencyWidget = new GridBagConstraints(); + gbcCurrencyWidget.insets = new Insets(0, 0, 5, 0); + gbcCurrencyWidget.fill = GridBagConstraints.HORIZONTAL; + gbcCurrencyWidget.gridx = 1; + gbcCurrencyWidget.gridy = 2; + add(getCurrencyWidget(), gbcCurrencyWidget); + GridBagConstraints gbcLblNewLabel1 = new GridBagConstraints(); + gbcLblNewLabel1.anchor = GridBagConstraints.WEST; + gbcLblNewLabel1.insets = new Insets(0, 0, 0, 5); + gbcLblNewLabel1.gridx = 0; + gbcLblNewLabel1.gridy = 3; + add(getLblNewLabel1(), gbcLblNewLabel1); + GridBagConstraints gbcPageSelector = new GridBagConstraints(); + gbcPageSelector.anchor = GridBagConstraints.WEST; + gbcPageSelector.gridx = 1; + gbcPageSelector.gridy = 3; + add(getPageSelector(), gbcPageSelector); + } + + private JLabel getLblTextwidget() { + if (lblTextwidget == null) { + lblTextwidget = new JLabel("TextWidget:"); + } + return lblTextwidget; + } + private TextWidget getTextWidget() { + if (textWidget == null) { + textWidget = new TextWidget(10); + textWidget.setPredefined(new String[]{"dummy", "this string", "another one", "next one", "last one", "One more"}, 2); + textWidget.addPropertyChangeListener(TextWidget.TEXT_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + AJLibDemo.setMessage("Text changed from "+evt.getOldValue()+" to "+evt.getNewValue()); + } + }); + } + return textWidget; + } + private JLabel getLblNumberWidget() { + if (lblNumberWidget == null) { + lblNumberWidget = new JLabel("Number widget:"); + } + return lblNumberWidget; + } + private NumberWidget getNumberWidget() { + if (numberWidget == null) { + numberWidget = new NumberWidget(); + numberWidget.setToolTipText("This widget allows you to enter a double."); + numberWidget.setColumns(10); + numberWidget.addPropertyChangeListener(NumberWidget.VALUE_PROPERTY, new DefaultPropertyChangeListener("Number")); + numberWidget.addPropertyChangeListener(NumberWidget.CONTENT_VALID_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + Color color = (Boolean) evt.getNewValue()?Color.WHITE:Color.RED; + numberWidget.setBackground(color); + } + }); + } + return numberWidget; + } + private JLabel getLblNewLabel() { + if (lblNewLabel == null) { + lblNewLabel = new JLabel("Currency Widget:"); + } + return lblNewLabel; + } + private CurrencyWidget getCurrencyWidget() { + if (currencyWidget == null) { + currencyWidget = new CurrencyWidget(); + currencyWidget.setColumns(10); + currencyWidget.addPropertyChangeListener(NumberWidget.VALUE_PROPERTY, new DefaultPropertyChangeListener("Amount")); + currencyWidget.addPropertyChangeListener(NumberWidget.CONTENT_VALID_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + Color color = (Boolean) evt.getNewValue()?Color.WHITE:Color.RED; + currencyWidget.setBackground(color); + } + }); + } + return currencyWidget; + } + private PageSelector getPageSelector() { + if (pageSelector == null) { + pageSelector = new PageSelector(); + pageSelector.setPageCount(5); + pageSelector.addPropertyChangeListener(PageSelector.PAGE_SELECTED_PROPERTY_NAME, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + AJLibDemo.setMessage("Page set to "+evt.getNewValue()); + } + }); + } + return pageSelector; + } + private JLabel getLblNewLabel1() { + if (lblNewLabel1 == null) { + lblNewLabel1 = new JLabel("Page selector:"); + } + return lblNewLabel1; + } +} diff --git a/src/main/java/com/fathzer/soft/ajlib/swing/demo/WorkerDemoPanel.java b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/WorkerDemoPanel.java similarity index 96% rename from src/main/java/com/fathzer/soft/ajlib/swing/demo/WorkerDemoPanel.java rename to demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/WorkerDemoPanel.java index 1c191d1..ee25fdf 100644 --- a/src/main/java/com/fathzer/soft/ajlib/swing/demo/WorkerDemoPanel.java +++ b/demo/src/main/java/com/fathzer/soft/ajlib/swing/demo/WorkerDemoPanel.java @@ -1,115 +1,115 @@ -package com.fathzer.soft.ajlib.swing.demo; - -import java.awt.Dialog.ModalityType; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JPanel; - -import com.fathzer.soft.ajlib.swing.Utils; -import com.fathzer.soft.ajlib.swing.worker.WorkInProgressFrame; -import com.fathzer.soft.ajlib.swing.worker.Worker; - -import java.awt.GridBagConstraints; - -@SuppressWarnings("serial") -public class WorkerDemoPanel extends JPanel { - private JButton btnStartANew1; - - /** - * Create the panel. - */ - public WorkerDemoPanel() { - initialize(); - } - - private void initialize() { - setLayout(new GridBagLayout()); - JButton btnStartANew = getBtnStartANew(); - GridBagConstraints gbcBtnStartANew = new GridBagConstraints(); - gbcBtnStartANew.gridy = 0; - gbcBtnStartANew.gridx = 0; - add(btnStartANew, gbcBtnStartANew); - GridBagConstraints gbcBtnStartANew1 = new GridBagConstraints(); - gbcBtnStartANew1.gridy = 1; - gbcBtnStartANew1.gridx = 0; - add(getBtnStartANew1(), gbcBtnStartANew1); - } - - private static class WorkerSample extends Worker { - private static int globalTaskNumber; - private int taskNumber; - - WorkerSample() { - this.taskNumber = ++globalTaskNumber; - } - - @Override - protected Void doProcessing() throws Exception { - setPhase("A task may define phases", -1); - for (int i=0;i<40;i++) { - Thread.sleep(50); - if (isCancelled()) { - return null; - } - } - setPhase("Some may not have a fixed length", -1); - for (int i=0;i<30;i++) { - Thread.sleep(50); - if (isCancelled()) { - return null; - } - } - int nb = 30; - setPhase("Other may have a defined length", nb); - for (int i=0;i { + private static int globalTaskNumber; + private int taskNumber; + + WorkerSample() { + this.taskNumber = ++globalTaskNumber; + } + + @Override + protected Void doProcessing() throws Exception { + setPhase("A task may define phases", -1); + for (int i=0;i<40;i++) { + Thread.sleep(50); + if (isCancelled()) { + return null; + } + } + setPhase("Some may not have a fixed length", -1); + for (int i=0;i<30;i++) { + Thread.sleep(50); + if (isCancelled()) { + return null; + } + } + int nb = 30; + setPhase("Other may have a defined length", nb); + for (int i=0;iparent-pom 1.0.8 - ajlib - 0.3.16 + ajlib-parent-pom + 1.0.0 + pom - jar - - A-JLib - A-Jlib is a simple java library with Swing widgets, utilities - and other stuff + ajlib-parent + The parent pom used by ajlib. https://github.com/fathzer/ajlib https://github.com/fathzer/ajlib - scm:git:git@github.com:fathzer/ajlib + https://github.com/fathzer/ajlib UTF-8 - 1.23 java17 1.7 + 1.23 - + release @@ -42,23 +40,14 @@ sonar - fathzer + fathzer https://sonarcloud.io - + + - - com.fathzer - jlocal - 1.0.0 - - - org.codehaus.mojo - animal-sniffer-annotations - ${animal-sniffer-version} - junit junit @@ -67,56 +56,17 @@ + + ajlib + demo + + org.codehaus.mojo animal-sniffer-maven-plugin - - maven-javadoc-plugin - 2.10.3 - - com.fathzer.soft.ajlib.swing.demo - - - - maven-jar-plugin - 2.4 - - - - true - com.fathzer.soft.ajlib.swing.demo.AJLibDemo - - - - **/package.html - - - - - - maven-antrun-plugin - 1.7 - - - install - - - - - - - - - - run - - - -
\ No newline at end of file diff --git a/target/ajlib-parent-pom-1.0.0.pom b/target/ajlib-parent-pom-1.0.0.pom new file mode 100644 index 0000000..13fcf71 --- /dev/null +++ b/target/ajlib-parent-pom-1.0.0.pom @@ -0,0 +1,72 @@ + + 4.0.0 + + com.fathzer + parent-pom + 1.0.8 + + ajlib-parent-pom + 1.0.0 + pom + + ajlib-parent + The parent pom used by ajlib. + https://github.com/fathzer/ajlib + + + https://github.com/fathzer/ajlib + https://github.com/fathzer/ajlib + + + + UTF-8 + java17 + 1.7 + 1.23 + + + + + release + + true + + + 1.7 + + + + sonar + + fathzer + https://sonarcloud.io + + + + + + + + junit + junit + 4.13.2 + test + + + + + ajlib + demo + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + + \ No newline at end of file diff --git a/target/ajlib-parent-pom-1.0.0.pom.asc b/target/ajlib-parent-pom-1.0.0.pom.asc new file mode 100644 index 0000000..bbc0b52 --- /dev/null +++ b/target/ajlib-parent-pom-1.0.0.pom.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: BCPG v1.46 + +iQEcBAABAgAGBQJmTNi2AAoJEIrAN47HUwY9VS0IAKMYay1YAifVI2u1P1DRsvOg +g967ruIyOV2yR+mRUV678z7zVCuvI2XQ7MzfEZZ01R1wDzV5lMlqUp7JUIdpbuDz +ZQetNTiqq/BpTedtvBYTJmUOuQ8Zii+ldthcztdMT9OvvtVuUzsXA94gV1C+Enc/ +I/xmXBoB1yiNK7CNL6kTGABXeuNp8dW5HwC0lKKY8k12P7XleUjfxuNRufBwTIJ0 +Yu9u7auhxGg/33d2SkqkmkfJQhUefDrhv4gUwX1jgWXsDHfGRQKN16y/wHbTcWwY +PmYk3PfgIADk9dxP1e1jrE35Ar2u97NRAwlhE3fpanTRRME5nqXhU+etIfxnjXo= +=Yqr+ +-----END PGP SIGNATURE-----