1 /* 2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab 3 * Copyright (C) 2015-2016 - Scilab Enterprises - Simon Marchetto 4 * Copyright (C) 2016 - Scilab Enterprises - Clement David 5 * 6 * Copyright (C) 2012 - 2016 - Scilab Enterprises 7 * 8 * This file is hereby licensed under the terms of the GNU GPL v2.0, 9 * pursuant to article 5.3.4 of the CeCILL v.2.1. 10 * This file was originally licensed under the terms of the CeCILL v2.1, 11 * and continues to be available under such terms. 12 * For more information, see the COPYING file which you should have received 13 * along with this program. 14 * 15 */ 16 17 package org.scilab.modules.ui_data.newsfeed; 18 19 import org.scilab.modules.commons.ScilabConstants; 20 import org.scilab.modules.commons.gui.FindIconHelper; 21 import org.scilab.modules.gui.utils.WebBrowser; 22 23 import javax.swing.JPanel; 24 import javax.swing.JEditorPane; 25 import javax.swing.JScrollPane; 26 import javax.swing.text.html.HTMLEditorKit; 27 import javax.swing.text.Document; 28 import javax.swing.event.HyperlinkEvent; 29 import javax.swing.event.HyperlinkListener; 30 import javax.swing.SwingUtilities; 31 import javax.swing.UIManager; 32 33 import java.awt.BorderLayout; 34 import java.awt.Font; 35 import java.awt.Point; 36 import java.awt.event.ActionEvent; 37 import java.io.File; 38 import java.text.SimpleDateFormat; 39 40 import java.net.MalformedURLException; 41 import java.net.URL; 42 import java.util.logging.Level; 43 import java.util.logging.Logger; 44 import javax.swing.AbstractAction; 45 import javax.swing.Action; 46 import javax.swing.ImageIcon; 47 import javax.swing.JButton; 48 49 /** 50 * News feed widget Displays one news at a time. 51 * Controlled by NewsFeedController 52 * Displays news in HTML format (JEditorPane/HTMLEditorKit) 53 * Loads styles from CSS newsfeed.css (in directory ui_data/etc or SCIHOME) 54 */ 55 public class NewsFeedWidget extends JPanel implements NewsFeedEventListener, HyperlinkListener { 56 private static final String NEWS_DATE_HTML_ID = "news_date"; 57 private static final String NEWS_CONTENT_HTML_ID = "news_content"; 58 private static final String NEWS_LINK_HTML_ID = "news_link"; 59 private static final String NEWS_MEDIA_CONTENT_HTML_ID = "news_media_content"; 60 private static final String NEWS_DESCRIPTION_HTML_ID = "news_description"; 61 62 private final NewsFeedController newsFeedController; 63 private final JEditorPane editorPane; 64 private final JScrollPane scrollPane; 65 private final JPanel headerPane; 66 private final JButton headerButton; 67 NewsFeedWidget(NewsFeedController newsFeedController)68 public NewsFeedWidget(NewsFeedController newsFeedController) { 69 this.newsFeedController = newsFeedController; 70 71 editorPane = new JEditorPane(); 72 editorPane.setEditable(false); 73 editorPane.setContentType("text/html"); 74 75 // use current look & feel font 76 editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); 77 Font font = UIManager.getFont("Label.font"); 78 editorPane.setFont(font); 79 80 HTMLEditorKit htmlEditorKit = new HTMLEditorKit(); 81 editorPane.setEditorKit(htmlEditorKit); 82 Document doc = htmlEditorKit.createDefaultDocument(); 83 editorPane.setDocument(doc); 84 85 htmlEditorKit.getStyleSheet().importStyleSheet(getStyleSheetURL()); 86 87 editorPane.addHyperlinkListener(this); 88 89 headerButton = new JButton(); 90 headerPane = new JPanel(new BorderLayout()); 91 headerPane.add(new JButton(new AbstractAction(null, new ImageIcon(FindIconHelper.findIcon("go-previous"))) { 92 @Override 93 public void actionPerformed(ActionEvent e) { 94 newsFeedController.previousNews(); 95 } 96 }), BorderLayout.WEST); 97 headerPane.add(headerButton, BorderLayout.CENTER); 98 headerPane.add(new JButton(new AbstractAction(null, new ImageIcon(FindIconHelper.findIcon("go-next"))) { 99 @Override 100 public void actionPerformed(ActionEvent e) { 101 newsFeedController.nextNews(); 102 } 103 }), BorderLayout.EAST); 104 105 scrollPane = new JScrollPane(editorPane); 106 107 setLayout(new BorderLayout()); 108 add(headerPane, BorderLayout.NORTH); 109 add(scrollPane, BorderLayout.CENTER); 110 111 // at startup, display an error message 112 displayError(NewsFeedUIMessages.NEWS_FEED_UNAVAILABLE_ERROR); 113 } 114 newsFeedEventReceived(NewsFeedEvent evt)115 public void newsFeedEventReceived(NewsFeedEvent evt) { 116 switch (evt.getEventType()) { 117 case NewsFeedEvent.NEWS_CHANGED: { 118 displayNews(newsFeedController.getCurrentNews()); 119 break; 120 } 121 case NewsFeedEvent.NEWSFEED_ERROR: { 122 displayError(((NewsFeedErrorEvent) evt).getErrorMessage()); 123 break; 124 } 125 } 126 } 127 hyperlinkUpdate(HyperlinkEvent event)128 public void hyperlinkUpdate(HyperlinkEvent event) { 129 if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 130 URL url = event.getURL(); 131 if (url != null) { 132 WebBrowser.openUrl(url, event.getDescription()); 133 } 134 } 135 } 136 displayNews(News news)137 private void displayNews(News news) { 138 StringBuilder newsHtmlBuilder = new StringBuilder(); 139 140 // Add media content (image) if exist 141 NewsMediaContent mediaContent = news.getMediaContent(); 142 if (mediaContent != null) { 143 newsHtmlBuilder.append(getDivHtml(getImageHtml(mediaContent.getURL(), mediaContent.getWidth(), mediaContent.getHeight()), NEWS_MEDIA_CONTENT_HTML_ID)); 144 } 145 146 if (news.getContent() != null) { 147 // if given, use RSS item content 148 newsHtmlBuilder.append(getDivHtml(news.getContent(), NEWS_CONTENT_HTML_ID)); 149 } else { 150 // otherwise get content from description 151 newsHtmlBuilder.append(getDivHtml(news.getDescription(), NEWS_DESCRIPTION_HTML_ID)); 152 } 153 154 // Add news link if exist 155 if (news.getLink() != null) { 156 newsHtmlBuilder.append(getDivHtml(getLinkHtml(news.getLink(), news.getLink()), NEWS_LINK_HTML_ID)); 157 } 158 159 display(new AbstractAction(news.getTitle()) { 160 @Override 161 public void actionPerformed(ActionEvent e) { 162 try { 163 WebBrowser.openUrl(new URL(news.getLink()), news.getLink()); 164 } catch (MalformedURLException ex) { 165 Logger.getLogger(NewsFeedWidget.class.getName()).log(Level.SEVERE, null, ex); 166 } 167 } 168 }, getHTML(newsHtmlBuilder)); 169 } 170 getImageHtml(String url, String width, String height)171 private String getImageHtml(String url, String width, String height) { 172 // Cannot setup border with CSS, limitation of CSS support of HTMLEditorKit 173 return String.format("<img src='%s' border='0' width='%s', height='%s'/>", url, width, height); 174 } 175 getLinkHtml(String url, String description)176 private String getLinkHtml(String url, String description) { 177 return String.format("<a href='%s'>%s</a>", url, description); 178 } 179 getSpanHtml(String spanContent, String spanId)180 private String getSpanHtml(String spanContent, String spanId) { 181 return String.format("<span id='%s'>%s</span>", spanId, spanContent); 182 } 183 getDivHtml(String divContent, String divId)184 private String getDivHtml(String divContent, String divId) { 185 return String.format("<div id='%s'>%s</div>", divId, divContent); 186 } 187 getDivHtml(String divContent)188 private String getDivHtml(String divContent) { 189 return String.format("<div>%s</div>", divContent); 190 } 191 displayError(String errorMessage)192 private void displayError(String errorMessage) { 193 StringBuilder errMsgHtmlBuilder = new StringBuilder(errorMessage); 194 display(new AbstractAction("") { 195 @Override 196 public void actionPerformed(ActionEvent e) { 197 URL url = newsFeedController.getNewsFetcher().getURL(); 198 WebBrowser.openUrl(url, url.toString()); 199 } 200 }, getHTML(errMsgHtmlBuilder)); 201 } 202 getHTML(StringBuilder htmlBuilder)203 private String getHTML(StringBuilder htmlBuilder) { 204 htmlBuilder.insert(0, "<html><body>"); 205 htmlBuilder.append("</body></html>"); 206 return htmlBuilder.toString(); 207 } 208 display(final Action titleAction, final String htmlContent)209 private void display(final Action titleAction, final String htmlContent) { 210 try { 211 SwingUtilities.invokeLater(new Runnable() { 212 public void run() { 213 headerButton.setAction(titleAction); 214 headerButton.setToolTipText(titleAction.getValue(Action.NAME).toString()); 215 216 editorPane.setText(htmlContent); 217 // reset position to the upper left 218 editorPane.setCaretPosition(0); 219 } 220 }); 221 } catch (Exception e) { 222 } 223 } 224 getStyleSheetURL()225 private URL getStyleSheetURL() { 226 File settingsFile = new File(ScilabConstants.SCIHOME + "/newsfeed.css"); 227 if (!settingsFile.exists()) { 228 settingsFile = new File(ScilabConstants.SCI + "/modules/ui_data/etc/newsfeed.css"); 229 } 230 if (settingsFile.exists()) { 231 try { 232 return settingsFile.toURI().toURL(); 233 } catch (MalformedURLException e) { 234 return null; 235 } 236 } else { 237 return null; 238 } 239 } 240 } 241