View Javadoc
1   package fr.ifremer.quadrige2.ui.swing.common;
2   
3   /*-
4    * #%L
5    * Quadrige2 Core :: Quadrige2 UI Common
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2017 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  
27  import jaxx.runtime.JAXXBinding;
28  import jaxx.runtime.JAXXObject;
29  import jaxx.runtime.JAXXUtil;
30  import jaxx.runtime.SwingUtil;
31  import org.apache.commons.collections4.CollectionUtils;
32  import org.apache.commons.io.IOUtils;
33  import org.apache.commons.lang3.StringEscapeUtils;
34  import org.apache.commons.lang3.StringUtils;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.jdesktop.beans.AbstractBean;
38  import org.nuiton.jaxx.application.ApplicationBusinessException;
39  
40  import javax.swing.*;
41  import javax.swing.text.JTextComponent;
42  import java.awt.*;
43  import java.awt.image.BufferedImage;
44  import java.beans.PropertyChangeListener;
45  import java.io.BufferedReader;
46  import java.io.IOException;
47  import java.io.InputStream;
48  import java.io.StringReader;
49  import java.net.MalformedURLException;
50  import java.net.URL;
51  import java.net.URLConnection;
52  import java.text.DecimalFormat;
53  import java.text.DecimalFormatSymbols;
54  import java.util.Calendar;
55  import java.util.Collection;
56  import java.util.Date;
57  import java.util.List;
58  
59  import static org.nuiton.i18n.I18n.t;
60  
61  /**
62   * Created: 14/06/12
63   */
64  public class ApplicationUIUtil extends org.nuiton.jaxx.application.swing.util.ApplicationUIUtil {
65  
66      private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols();
67  
68      private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat();
69  
70      private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
71  
72      static {
73          SYMBOLS.setDecimalSeparator('.');
74          SYMBOLS.setGroupingSeparator(' ');
75  
76          DECIMAL_FORMAT.setDecimalFormatSymbols(SYMBOLS);
77          DECIMAL_FORMAT.setGroupingUsed(false);
78  
79          Calendar calendar = Calendar.getInstance();
80          calendar.set(2000, Calendar.JANUARY, 1, 0, 0);
81  
82          DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime();
83      }
84  
85      /**
86       * Logger.
87       */
88      private static final Log LOG = LogFactory.getLog(ApplicationUIUtil.class);
89  
90      /**
91       * Private constructor (never instantiate util class)
92       */
93      protected ApplicationUIUtil() {
94      }
95  
96      /**
97       * <p>setParentUI.</p>
98       *
99       * @param ui       a {@link jaxx.runtime.JAXXObject} object.
100      * @param parentUI a {@link fr.ifremer.quadrige2.ui.swing.common.ApplicationUI} object.
101      */
102     public static void setParentUI(JAXXObject ui, ApplicationUI<?, ?> parentUI) {
103         JAXXUtil.initContext(ui, parentUI);
104         setApplicationContext(ui, parentUI.getHandler() != null ? parentUI.getHandler().getContext() : ApplicationUIContext.getInstance());
105     }
106 
107     /**
108      * <p>getParentUI.</p>
109      *
110      * @param ui a {@link fr.ifremer.quadrige2.ui.swing.common.ApplicationUI} object.
111      * @return a {@link fr.ifremer.quadrige2.ui.swing.common.ApplicationUI} object.
112      */
113     public static ApplicationUI<?, ?> getParentUI(ApplicationUI<?, ?> ui) {
114         return ui.getContextValue(ApplicationUI.class, JAXXUtil.PARENT);
115     }
116 
117     /**
118      * <p>tryToConnectToUpdateUrl.</p>
119      *
120      * @param urlAsString         a {@link java.lang.String} object.
121      * @param badUrlFormatI18nKey a {@link java.lang.String} object.
122      * @param notReachI18nKey     a {@link java.lang.String} object.
123      * @param notFoundI18nKey     a {@link java.lang.String} object.
124      */
125     public static void tryToConnectToUpdateUrl(String urlAsString,
126                                                String badUrlFormatI18nKey,
127                                                String notReachI18nKey,
128                                                String notFoundI18nKey) {
129         URL url;
130         // get url
131         try {
132             url = new URL(urlAsString);
133         } catch (MalformedURLException e) {
134             if (LOG.isDebugEnabled()) {
135                 LOG.debug("Bad url syntax at " + urlAsString, e);
136             }
137             throw new ApplicationBusinessException(t(badUrlFormatI18nKey, urlAsString));
138         }
139 
140         URLConnection urlConnection;
141         // try to connect (fail if network or remote host does not exists)
142         try {
143             urlConnection = url.openConnection();
144             urlConnection.setConnectTimeout(10000);
145             urlConnection.connect();
146         } catch (Exception e) {
147             if (LOG.isDebugEnabled()) {
148                 LOG.debug("Could not connect to " + urlAsString, e);
149             }
150             throw new ApplicationBusinessException(t(notReachI18nKey, urlAsString));
151         }
152 
153         // try to open the resource (fail if resources does not exist)
154         try {
155             urlConnection.setReadTimeout(1000);
156             InputStream inputStream = null;
157             try {
158                 inputStream = urlConnection.getInputStream();
159             } finally {
160                 IOUtils.closeQuietly(inputStream);
161             }
162         } catch (Exception e) {
163             if (LOG.isDebugEnabled()) {
164                 LOG.debug("Could not found file at to " + urlAsString, e);
165             }
166             throw new ApplicationBusinessException(t(notFoundI18nKey, urlAsString));
167         }
168     }
169 
170     /**
171      * <p>getDecimalFormatSymbols.</p>
172      *
173      * @return a {@link java.text.DecimalFormatSymbols} object.
174      */
175     public static DecimalFormatSymbols getDecimalFormatSymbols() {
176         return SYMBOLS;
177     }
178 
179     /**
180      * <p>getDecimalFormat.</p>
181      *
182      * @param minDecimal a int.
183      * @param maxDecimal a int.
184      * @return a {@link java.text.DecimalFormat} object.
185      */
186     public static DecimalFormat getDecimalFormat(int minDecimal, int maxDecimal) {
187         DECIMAL_FORMAT.setMinimumFractionDigits(minDecimal);
188         DECIMAL_FORMAT.setMaximumFractionDigits(maxDecimal);
189         return DECIMAL_FORMAT;
190     }
191 
192     private static final String HTML_BEGIN = "<html>";
193     private static final String HTML_END = "</html>";
194     private static final String HTML_PATTERN = HTML_BEGIN + "%s" + HTML_END;
195     private static final String HTML_BR = "<br>";
196     private static final String HTML_BR2 = "<br/>";
197     private static final String HTML_LIST_PATTERN = "<ul>%s</ul>";
198     private static final String HTML_LIST_ITEM_PATTERN = "<li>%s</li>";
199     private static final String HTML_ITALIC_PATTERN = HTML_BEGIN + "<i>%s</i>" + HTML_END;
200 
201     /**
202      * <p>isHtmlString.</p>
203      *
204      * @param text a {@link java.lang.String} object.
205      * @return a boolean.
206      */
207     public static boolean isHtmlString(String text) {
208         if (StringUtils.isBlank(text)) {
209             return false;
210         }
211         String s = StringUtils.deleteWhitespace(text);
212         return s.startsWith(HTML_BEGIN) && s.endsWith(HTML_END);
213     }
214 
215     /**
216      * <p>getHtmlString.</p>
217      *
218      * @param text a {@link java.lang.String} object.
219      * @return a {@link java.lang.String} object.
220      */
221     public static String getHtmlString(String text) {
222 
223         if (StringUtils.isBlank(text)) {
224             return HTML_BEGIN + HTML_END;
225         } else if (isHtmlString(text)) {
226             return text;
227         }
228 
229         return String.format(HTML_PATTERN, formatHtmlText(text));
230     }
231 
232     public static String getHtmlItalicText(String text) {
233 
234         if (StringUtils.isBlank(text)) {
235             return HTML_BEGIN + HTML_END;
236         } else if (isHtmlString(text)) {
237             return getHtmlItalicText(removeHtmlTags(text));
238         }
239 
240         return String.format(HTML_ITALIC_PATTERN, text);
241     }
242 
243     private static String formatHtmlText(String text) {
244 
245         BufferedReader st = new BufferedReader(new StringReader(text));
246         StringBuilder buf = new StringBuilder();
247 
248         try {
249             String str;
250             while ((str = st.readLine()) != null) {
251 
252                 if (str.equalsIgnoreCase(HTML_BR2)) {
253                     str = HTML_BR;
254                     buf.append(str);
255                 } else {
256                     buf.append(escapeHtmlString(str));
257                 }
258 
259                 if (!str.equalsIgnoreCase(HTML_BR)) {
260                     buf.append(HTML_BR);
261                 }
262 
263             }
264         } catch (IOException e) {
265             // ignore it
266         }
267 
268         return buf.toString();
269     }
270 
271     private static String escapeHtmlString(String str) {
272         boolean containsHTML = str.matches(".*<[^>]+>.*");
273         return containsHTML ? str : StringEscapeUtils.escapeHtml4(str);
274     }
275 
276     /**
277      * <p>getHtmlString.</p>
278      *
279      * @param strings a {@link java.util.List} object.
280      * @return a {@link java.lang.String} object.
281      */
282     public static String getHtmlString(Collection<String> strings) {
283         if (CollectionUtils.isEmpty(strings)) {
284             return getHtmlString("");
285         }
286         return String.format(HTML_PATTERN, formatHtmlList(strings));
287     }
288 
289     /**
290      * <p>formatHtmlList.</p>
291      *
292      * @param strings a {@link java.util.List} object.
293      * @return a {@link java.lang.String} object.
294      */
295     public static String formatHtmlList(Collection<String> strings) {
296         String result = "";
297         for (String string : strings) {
298             result += String.format(HTML_LIST_ITEM_PATTERN, string);
299         }
300         return String.format(HTML_LIST_PATTERN, result);
301     }
302 
303     /**
304      * <p>getHtmlString.</p>
305      *
306      * @param text    a {@link java.lang.String} object.
307      * @param strings a {@link java.util.List} object.
308      * @return a {@link java.lang.String} object.
309      */
310     public static String getHtmlString(String text, Collection<String> strings) {
311         if (CollectionUtils.isEmpty(strings)) {
312             return getHtmlString(text);
313         }
314         String result = "";
315         for (String string : strings) {
316             if (StringUtils.isNotBlank(string)) {
317                 result += String.format(HTML_LIST_ITEM_PATTERN, formatHtmlText(string));
318             }
319         }
320         result = (text != null ? formatHtmlText(text) : "") + String.format(HTML_LIST_PATTERN, result);
321         return String.format(HTML_PATTERN, result);
322     }
323 
324     /**
325      * Remove HTML tag, that could have been added by getHtmlString() method
326      *
327      * @param htmlString a {@link java.lang.String} object.
328      * @return a {@link java.lang.String} object.
329      */
330     public static String removeHtmlTags(String htmlString) {
331         String result = htmlString;
332 
333         if (result.startsWith(HTML_BEGIN)) {
334             result = result.substring(HTML_BEGIN.length());
335         }
336         if (result.endsWith(HTML_END)) {
337             result = result.substring(0, result.length() - HTML_BEGIN.length() - 1);
338         }
339 
340         // remove br tags
341         result = result.replaceAll(HTML_BR, "").replaceAll(HTML_BR2, "");
342 
343         // some formatting
344         result = result.replaceAll("<strong>", "").replaceAll("<.strong>", "");
345         result = result.replaceAll("<li>", "").replaceAll("<.li>", "");
346         result = result.replaceAll("<ul>", "").replaceAll("<.ul>", "");
347 
348         return StringEscapeUtils.unescapeHtml4(result);
349     }
350 
351     /**
352      * <p>setComponentLocationOnScreen.</p>
353      *
354      * @param component a {@link java.awt.Component} object.
355      * @param point     a {@link java.awt.Point} object.
356      */
357     public static void setComponentLocationOnScreen(Component component, Point point) {
358 
359         if (component == null || point == null) {
360             return;
361         }
362 
363         Rectangle screenBounds = new Rectangle();
364         GraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
365         for (GraphicsDevice device : devices) {
366             screenBounds = screenBounds.union(device.getDefaultConfiguration().getBounds());
367         }
368 
369         if (point.x < screenBounds.x) {
370             point.x = 0;
371         } else if (point.x + component.getWidth() > screenBounds.width) {
372             point.x = screenBounds.width - component.getWidth();
373         }
374 
375         if (point.y < screenBounds.y) {
376             point.y = 0;
377         } else if (point.y + component.getHeight() > screenBounds.height) {
378             point.y = screenBounds.height - component.getHeight();
379         }
380 
381         component.setLocation(point);
382     }
383 
384     /**
385      * Like SwingXUtilities.setComponentTreeBackground(Component c, Color color), but filter out some components
386      *
387      * @param component a {@link java.awt.Component} object.
388      * @param color     a {@link java.awt.Color} object.
389      */
390     public static void setComponentTreeBackground(Component component, Color color) {
391 
392         if (component instanceof JTable
393                 || component instanceof JTextComponent
394                 || component instanceof JComboBox
395                 || component instanceof JButton
396                 ) {
397             return;
398         }
399 
400         if (component instanceof JList && component.isEnabled()) {
401             return;
402         }
403 
404         component.setBackground(color);
405 
406         if (component instanceof Container) {
407             Container container = (Container) component;
408             if (container.getComponents() != null) {
409                 for (Component child : container.getComponents()) {
410                     setComponentTreeBackground(child, color);
411                 }
412             }
413         }
414     }
415 
416     /**
417      * <p>setChildrenLabelForeground.</p>
418      *
419      * @param container a {@link java.awt.Container} object.
420      * @param color     a {@link java.awt.Color} object.
421      */
422     public static void setChildrenLabelForeground(Container container, Color color) {
423 
424         if (container.getComponents() != null) {
425             for (Component child : container.getComponents()) {
426 
427                 if (child instanceof JLabel) {
428                     child.setForeground(color);
429                 } else if (child instanceof Container) {
430                     setChildrenLabelForeground((Container) child, color);
431                 }
432             }
433         }
434     }
435 
436     /**
437      * <p>destroy.</p>
438      *
439      * @param ui a {@link fr.ifremer.quadrige2.ui.swing.common.ApplicationUI} object.
440      */
441     public static void destroy(ApplicationUI ui) {
442 
443         removeBindings(ui);
444 
445         if (ui.getModel() instanceof AbstractBean && !(ui.getModel() instanceof ApplicationUIContext)) {
446             removeListeners((AbstractBean) ui.getModel());
447         }
448         if (ui instanceof Component) {
449             removeListeners((Component) ui);
450         }
451         if (ui instanceof Container) {
452             destroyChildren((Container) ui);
453         }
454     }
455 
456     private static void destroyChildren(Container container) {
457 
458         for (Component child : container.getComponents()) {
459             if (child instanceof Container) {
460                 destroyChildren((Container) child);
461             }
462             if (child instanceof ApplicationUI) {
463                 ApplicationUI childUI = (ApplicationUI) child;
464                 childUI.getHandler().onCloseUI();
465                 destroy(childUI);
466             }
467         }
468     }
469 
470     /**
471      * <p>removeListeners.</p>
472      *
473      * @param bean a {@link org.jdesktop.beans.AbstractBean} object.
474      */
475     public static void removeListeners(AbstractBean bean) {
476         PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
477         for (PropertyChangeListener l : listeners) {
478             bean.removePropertyChangeListener(l);
479         }
480     }
481 
482     /**
483      * <p>removeListeners.</p>
484      *
485      * @param component a {@link java.awt.Component} object.
486      */
487     public static void removeListeners(Component component) {
488         PropertyChangeListener[] listeners = component.getPropertyChangeListeners();
489         for (PropertyChangeListener l : listeners) {
490             component.removePropertyChangeListener(l);
491         }
492     }
493 
494     /**
495      * <p>removeBindings.</p>
496      *
497      * @param jaxxObject a {@link jaxx.runtime.JAXXObject} object.
498      */
499     public static void removeBindings(JAXXObject jaxxObject) {
500         JAXXBinding[] bindings = jaxxObject.getDataBindings();
501         for (JAXXBinding binding : bindings) {
502             SwingUtil.removeDataBinding(jaxxObject, binding.getId());
503         }
504     }
505 
506     /**
507      * <p>getDefault2DigitYearStart.</p>
508      *
509      * @return a {@link java.util.Date} object.
510      */
511     public static Date getDefault2DigitYearStart() {
512         return DEFAULT_TWO_DIGIT_YEAR_START;
513     }
514 
515     public static Cursor getCustomCursor(String actionIconName) {
516 
517         Toolkit toolkit = Toolkit.getDefaultToolkit();
518 
519         ImageIcon imageIcon = SwingUtil.createActionIcon(actionIconName);
520 
521         Dimension bestDimension = toolkit.getBestCursorSize(imageIcon.getIconWidth(), imageIcon.getIconHeight());
522         if (bestDimension.getWidth() == 0) {
523             return Cursor.getDefaultCursor();
524         }
525 
526         BufferedImage cursorImage = new BufferedImage(bestDimension.width, bestDimension.height, BufferedImage.TYPE_INT_ARGB);
527         Graphics2D graphics2D = cursorImage.createGraphics();
528         graphics2D.drawImage(imageIcon.getImage(),
529                 (bestDimension.width - imageIcon.getIconWidth()) / 2,
530                 (bestDimension.height - imageIcon.getIconHeight()) / 2,
531                 imageIcon.getIconWidth(),
532                 imageIcon.getIconHeight(),
533                 null);
534 
535         return toolkit.createCustomCursor(
536                 cursorImage,
537                 new Point(bestDimension.width / 2, bestDimension.height / 2),
538                 actionIconName);
539     }
540 
541 }