--- /dev/null
+package jalview.gui;
+
+import jalview.bin.Cache;
+import jalview.jbgui.GBlogReader;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JToolBar;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.DateFormatter;
+
+import org.robsite.jswingreader.action.AboutAction;
+import org.robsite.jswingreader.action.MarkChannelAsRead;
+import org.robsite.jswingreader.action.MarkChannelAsUnread;
+import org.robsite.jswingreader.action.MarkItemAsRead;
+import org.robsite.jswingreader.action.MarkItemAsUnread;
+import org.robsite.jswingreader.action.RefreshChannelAction;
+import org.robsite.jswingreader.action.UpdatableAction;
+import org.robsite.jswingreader.model.Channel;
+import org.robsite.jswingreader.model.ChannelListModel;
+import org.robsite.jswingreader.model.Item;
+import org.robsite.jswingreader.model.SimpleRSSParser;
+import org.robsite.jswingreader.ui.BlogContentPane;
+import org.robsite.jswingreader.ui.ItemReadTimer;
+import org.robsite.jswingreader.ui.Main;
+import org.robsite.jswingreader.ui.util.ContextMenuMouseAdapter;
+import org.robsite.jswingreader.util.BrowserUtils;
+
+import quicktime.std.movies.media.TextDescription;
+
+/**
+ * Blog reading window, adapted from JSwingReader's
+ * org.robsite.jswingreader.ui.MainWindow class
+ */
+
+public class BlogReader extends GBlogReader
+{
+ private JButton buttonAbout = new JButton();
+
+ private JButton buttonRefresh = new JButton();
+
+ private JToolBar toolBar = new JToolBar();
+
+ private JLabel statusBar = new JLabel();
+
+ private JPanel panelMain = new JPanel();
+
+ private BorderLayout layoutMain = new BorderLayout();
+
+ private BorderLayout borderLayout1 = new BorderLayout();
+
+ private JPanel topPanel = new JPanel();
+
+ private JPanel bottomPanel = new JPanel();
+
+ private BorderLayout borderLayout2 = new BorderLayout();
+
+ private BorderLayout borderLayout3 = new BorderLayout();
+
+ private JSplitPane topBottomSplitPane = new JSplitPane();
+
+ private JList listItems = new JList(new DefaultListModel());
+
+ // SWITCH IN JALVIEW HTML VIEWER PANE HERE
+ private BlogContentPane textDescription = new BlogContentPane();
+
+ // ADD IN JALVIEW BANNER FOR PRETTINESS
+ private BorderLayout borderLayout4 = new BorderLayout();
+
+ private BorderLayout borderLayout5 = new BorderLayout();
+
+ private ChannelListModel _channelModel = null;
+
+ private JList listChannels = new JList();
+
+ private Action exitAction = new Action()
+ {
+
+ @Override
+ public void actionPerformed(ActionEvent arg0)
+ {
+ setVisible(false);
+ if (parent != null)
+ {
+ parent.showNews(false);
+ }
+
+ }
+
+ @Override
+ public void setEnabled(boolean arg0)
+ {
+
+ }
+
+ @Override
+ public void removePropertyChangeListener(PropertyChangeListener arg0)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void putValue(String arg0, Object arg1)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean isEnabled()
+ {
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ @Override
+ public Object getValue(String arg0)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void addPropertyChangeListener(PropertyChangeListener arg0)
+ {
+ // TODO Auto-generated method stub
+
+ }
+ };
+
+ private JLabel lblChannels = new JLabel();
+
+ private List _updatableActions = new ArrayList();
+
+ private ItemReadTimer _itemTimer = null;
+
+ private JPopupMenu _popupItems = null;
+
+ private JPopupMenu _popupChannels = null;
+
+ private String lastm = "";
+
+ private boolean newsnew = false;
+
+ private Desktop parent = null;
+
+ BlogReader()
+ {
+ this(null);
+ }
+
+ // should we ignore fake gui events
+ private boolean updating = false;
+
+ public BlogReader(Desktop desktop)
+ {
+ parent = desktop;
+ if (parent == null)
+ {
+ this.setSize(new Dimension(550, 350));
+ }
+ else
+ {
+ Rectangle bounds = parent
+ .getLastKnownDimensions("JALVIEW_RSS_WINDOW_");
+ if (bounds == null)
+ {
+ setBounds(parent.getX(), parent.getY(), 550, 350);
+ }
+ else
+ {
+ setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
+ }
+ }
+ _channelModel = new ChannelListModel();
+ // Construct our jalview news channel
+ Channel chan = new Channel();
+ chan.setURL(jalview.bin.Cache.getDefault(
+ "JALVIEW_NEWS_RSS",
+ jalview.bin.Cache.getDefault("www.jalview.org",
+ "http://www.jalview.org") + "/feeds/desktop/rss"));
+ loadLastM();
+ _channelModel.addChannel(chan);
+ updating = true;
+ try
+ {
+ jbInit();
+ postInit();
+ } catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ initItems(chan);
+ updating = false;
+ boolean setvisible = checkForNew(chan, true);
+ if (setvisible)
+ {
+ Cache.log.info("Will show jalview news automatically");
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if (parent != null)
+ {
+ parent.showNews(true);
+ }
+ else
+ {
+ setVisible(true);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * update hasnew flag and mark all new messages as unread.
+ */
+ private boolean checkForNew(Channel chan, boolean updateItems)
+ {
+ if (!updating || updateItems)
+ {
+ newsnew = false;
+ }
+ if (chan != null && chan.getItems() != null)
+ {
+ for (Item i : (List<Item>) chan.getItems())
+ {
+ boolean isread = (lastDate != null && i.getPublishDate() != null && !lastDate
+ .before(i.getPublishDate()));
+
+ if (i.getPublishDate() != null && (!updating || updateItems))
+ {
+ newsnew |= !isread;
+ }
+ if (updateItems)
+ {
+ i.setRead(isread);
+ }
+ }
+ }
+ return newsnew;
+ }
+
+ java.util.Date lastDate = null;
+
+ private void loadLastM()
+ {
+ lastDate = Cache.getDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED");
+ }
+
+ private void saveLastM(Item item)
+ {
+ if (item != null && item.getPublishDate() != null
+ && (lastDate == null || item.getPublishDate().after(lastDate)))
+ {
+ lastDate = item.getPublishDate();
+ if (lastDate != null)
+ {
+ jalview.bin.Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED",
+ lastDate);
+ jalview.bin.Cache.log.info("Saved last read date as "
+ + jalview.bin.Cache.date_format.format(lastDate));
+
+ }
+ if (_channelModel.getElementAt(0) != null)
+ {
+ checkForNew((Channel) _channelModel.getElementAt(0), false);
+ }
+ }
+ }
+
+ private void jbInit() throws Exception
+ {
+ this.setTitle("News from www.jalview.org");
+ this.getContentPane().setLayout(layoutMain);
+ panelMain.setLayout(borderLayout1);
+ topPanel.setLayout(borderLayout5);
+ bottomPanel.setLayout(borderLayout4);
+ topBottomSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
+ topBottomSplitPane.setDividerLocation(100);
+ topBottomSplitPane.setTopComponent(topPanel);
+ topBottomSplitPane.setBottomComponent(bottomPanel);
+ JScrollPane spTextDescription = new JScrollPane(textDescription);
+ textDescription.setText("");
+ statusBar.setText(" [Status] ");
+ buttonRefresh.addActionListener(new ActionListener()
+ {
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ refreshNews();
+ }
+ });
+ this.getContentPane().add(statusBar, BorderLayout.SOUTH);
+ toolBar.add(buttonRefresh);
+ toolBar.addSeparator();
+ JLabel about=new JLabel("brought to you by JSwingReader (jswingreader.sourceforge.net)");
+ toolBar.add(about);
+ toolBar.setFloatable(false);
+ this.getContentPane().add(toolBar, BorderLayout.NORTH);
+ panelMain.add(topBottomSplitPane, BorderLayout.CENTER);
+ this.getContentPane().add(panelMain, BorderLayout.CENTER);
+ JScrollPane spListItems = new JScrollPane(listItems);
+ listItems
+ .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ topPanel.add(spListItems, BorderLayout.CENTER);
+ bottomPanel.add(spTextDescription, BorderLayout.CENTER);
+ listChannels.setModel(_channelModel);
+
+ listItems.addMouseListener(new java.awt.event.MouseAdapter()
+ {
+ public void mouseClicked(MouseEvent e)
+ {
+ listItems_mouseClicked(e);
+ }
+ });
+ _popupItems = _buildItemsPopupMenu();
+ _popupChannels = _buildChannelsPopupMenu();
+ ContextMenuMouseAdapter popupAdapter = new ContextMenuMouseAdapter(
+ _popupItems);
+ ContextMenuMouseAdapter popupChannelsAdapter = new ContextMenuMouseAdapter(
+ _popupChannels);
+ listItems.addMouseListener(popupAdapter);
+ listItems.setCellRenderer(new ItemsRenderer());
+ lblChannels.setText("Channels");
+ }
+
+ private void postInit()
+ {
+ // clear the default hyperlink listener and replace with our own.
+ for (HyperlinkListener hll : textDescription.getHyperlinkListeners())
+ {
+ textDescription.removeHyperlinkListener(hll);
+ }
+ textDescription.addHyperlinkListener(new HyperlinkListener()
+ {
+ public void hyperlinkUpdate(HyperlinkEvent e)
+ {
+ if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
+ {
+ Desktop.showUrl(e.getURL().toExternalForm());
+ }
+ }
+ });
+
+ this.addWindowListener(new WindowAdapter()
+ {
+ public void windowClosing(WindowEvent e)
+ {
+ ActionEvent actionEvent = new ActionEvent(this,
+ ActionEvent.ACTION_FIRST, (String) exitAction
+ .getValue(Action.NAME));
+ exitAction.actionPerformed(actionEvent);
+ }
+
+ public void windowOpened(WindowEvent e)
+ {
+ }
+ });
+
+ listItems.addListSelectionListener(new ListSelectionListener()
+ {
+ public void valueChanged(ListSelectionEvent e)
+ {
+ if (e.getValueIsAdjusting() == false)
+ {
+ _itemsValueChanged(listItems);
+ }
+ }
+ });
+ listChannels.setSelectedIndex(1);
+ _updateAllActions();
+ _updateToolbarButtons();
+
+ _itemTimer = new ItemReadTimer(listChannels, listItems);
+ _itemsValueChanged(listItems);
+ }
+
+ public class LaunchJvBrowserOnItem extends AbstractAction implements
+ UpdatableAction
+ {
+ JList _listItems = null;
+
+ public LaunchJvBrowserOnItem(JList listItems)
+ {
+ super("Open in Browser");
+ this.putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
+ this.putValue(Action.LONG_DESCRIPTION, "Open in Browser");
+ _listItems = listItems;
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ Object o = _listItems.getSelectedValue();
+ if (o instanceof Item)
+ {
+ Item item = (Item) o;
+ item.setRead(true);
+ _listItems.repaint();
+
+ Desktop.showUrl(item.getLink());
+ }
+ }
+
+ public void update(Object o)
+ {
+ setEnabled(true);
+ if (_listItems == null || _listItems.getModel().getSize() == 0)
+ {
+ setEnabled(false);
+ }
+ else if (_listItems.getSelectedIndex() == -1)
+ {
+ setEnabled(false);
+ }
+ }
+
+ }
+
+ private JPopupMenu _buildItemsPopupMenu()
+ {
+ JPopupMenu popup = new JPopupMenu();
+ popup.add(new JMenuItem(new LaunchJvBrowserOnItem(listItems)));
+ popup.addSeparator();
+ popup.add(new JMenuItem(new MarkItemAsRead(listItems)));
+ popup.add(new JMenuItem(new MarkItemAsUnread(listItems)));
+ return popup;
+ }
+
+ private JPopupMenu _buildChannelsPopupMenu()
+ {
+ JPopupMenu popup = new JPopupMenu();
+ popup.add(new JMenuItem(new MarkChannelAsRead(listChannels, listItems)));
+ popup.add(new JMenuItem(
+ new MarkChannelAsUnread(listChannels, listItems)));
+ return popup;
+ }
+
+ private void initItems(Channel channel)
+ {
+ if (channel == null)
+ {
+ channel = new Channel();
+ }
+ if (!channel.isOpen() && channel.getURL() != null)
+ {
+ try
+ {
+ SimpleRSSParser.parse(channel);
+ } catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ DefaultListModel itemsModel = (DefaultListModel) listItems.getModel();
+ itemsModel.clear();
+ Iterator iter = (channel.getItems() != null) ? channel.getItems()
+ .iterator() : Collections.EMPTY_LIST.iterator();
+ while (iter.hasNext())
+ {
+ itemsModel.addElement(iter.next());
+ }
+ if (itemsModel.getSize() > 0)
+ {
+ listItems.setSelectedIndex(0);
+ _itemsValueChanged(listItems);
+ }
+ setStatusBarText(channel.getURL());
+ _updateAllActions();
+ }
+
+ private void _itemsValueChanged(JList itemList)
+ {
+ Item item = (Item) itemList.getSelectedValue();
+ if (item == null)
+ {
+ if (itemList.getModel().getSize() > 0)
+ {
+ item = (Item) itemList.getModel().getElementAt(0);
+ }
+ if (item == null)
+ {
+ item = new Item();
+ }
+ else
+ {
+ itemList.setSelectedIndex(0);
+ }
+ }
+
+ if (_itemTimer != null)
+ {
+ // prefer a shorter delay than 5s
+ _itemTimer.setDelay(300);
+ _itemTimer.start();
+ _itemTimer.setLastItem(item);
+ final Item lastitem = item;
+ _itemTimer.addActionListener(new ActionListener()
+ {
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ saveLastM(lastitem);
+ }
+ });
+ }
+
+ setStatusBarText(item.getLink());
+ textDescription.setBlogText(item);
+ _updateAllActions();
+ }
+
+ public void setStatusBarText(String text)
+ {
+ statusBar.setText(text);
+ }
+
+ private void _updateAllActions()
+ {
+ Iterator iter = _updatableActions.iterator();
+ while (iter.hasNext())
+ {
+ UpdatableAction action = (UpdatableAction) iter.next();
+ action.update(this);
+ }
+ }
+
+ private void _updateToolbarButtons()
+ {
+ Map general = (Map) Main.getPreferences().get("general");
+ if (general == null)
+ {
+ return;
+ }
+
+ Component[] components = toolBar.getComponents();
+ for (int i = 0; i < components.length; i++)
+ {
+ Component component = components[i];
+ if (component instanceof JButton)
+ {
+ JButton button = (JButton) component;
+ if (Boolean.toString(false).equals(general.get("useToolBarText")))
+ {
+ // Remove the text if preferences state no toolbar text
+ button.setText("");
+ }
+ if (Boolean.toString(true).equals(general.get("radioTextBelow")))
+ {
+ button.setVerticalTextPosition(AbstractButton.BOTTOM);
+ button.setHorizontalTextPosition(AbstractButton.CENTER);
+ }
+ else if (Boolean.toString(true).equals(
+ general.get("radioTextRight")))
+ {
+ button.setVerticalTextPosition(AbstractButton.CENTER);
+ button.setHorizontalTextPosition(AbstractButton.RIGHT);
+ }
+ }
+ }
+ }
+
+ private void listItems_mouseClicked(MouseEvent e)
+ {
+ if (e.getClickCount() == 2 && e.getModifiersEx() == MouseEvent.NOBUTTON)
+ {
+ Item item = (Item) listItems.getSelectedValue();
+ item.setRead(true);
+ saveLastM(item);
+ if (_itemTimer != null)
+ {
+ _itemTimer.stop();
+ }
+
+ Action action = new LaunchJvBrowserOnItem(listItems);
+ ActionEvent event = new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED, "LaunchBrowserOnItem");
+ action.actionPerformed(event);
+ }
+ }
+
+ /**
+ * force the news panel to refresh
+ */
+ public void refreshNews()
+ {
+ try {
+ initItems((Channel)_channelModel.getElementAt(0));
+
+ } catch (Exception x)
+ {}
+ }
+
+ public static void main(String args[])
+ {
+ // this tests the detection of new news based on the last read date stored
+ // in jalview properties
+ jalview.bin.Cache.loadProperties(null);
+ jalview.bin.Cache.initLogger();
+ // test will advance read date each time
+ Calendar today = Calendar.getInstance(), lastread = Calendar
+ .getInstance();
+ lastread.set(1983, 01, 01);
+ while (lastread.before(today))
+ {
+ Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED",
+ lastread.getTime());
+ BlogReader me = new BlogReader();
+ System.out.println("Set last date to "
+ + jalview.bin.Cache.date_format.format(lastread.getTime()));
+ if (me.isNewsNew())
+ {
+ Cache.log.info("There is news to read.");
+ }
+ else
+ {
+ Cache.log.info("There is no new news.");
+ }
+ me.setTitle("Testing : Last read is " + me.lastDate);
+ me.setVisible(true);
+ me.toFront();
+ while (me.isVisible())
+ {
+ try
+ {
+ Thread.sleep(100);
+ } catch (InterruptedException x)
+ {
+ }
+ ;
+ }
+ if (me.isNewsNew())
+ {
+ Cache.log.info("Still new news after reader displayed.");
+ }
+ if (lastread.getTime().before(me.lastDate))
+ {
+ Cache.log.info("The news was read.");
+ lastread.setTime(me.lastDate);
+ }
+ else
+ {
+ lastread.add(Calendar.MONTH, 1);
+ }
+ me.dispose();
+ }
+ }
+
+ boolean isNewsNew()
+ {
+ return newsnew;
+ }
+}
+
+class ChannelsRenderer extends DefaultListCellRenderer
+{
+ private final static Icon _icon = new ImageIcon(
+ Main.class.getResource("image/ComposeMail16.gif"));
+
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus)
+ {
+ JLabel component = (JLabel) super.getListCellRendererComponent(list,
+ value, index, isSelected, cellHasFocus);
+ component.setIcon(ChannelsRenderer._icon);
+ if (value instanceof Channel)
+ {
+ Channel channel = (Channel) value;
+ component.setText(channel.getTitle() + " ("
+ + channel.getUnreadItemCount() + ")");
+ component.setToolTipText(channel.getURL());
+ }
+ return component;
+ }
+}
+
+class ItemsRenderer extends DefaultListCellRenderer
+{
+ private final static Icon _icon = new ImageIcon(
+ Main.class.getResource("image/ComposeMail16.gif"));
+
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus)
+ {
+ JLabel component = (JLabel) super.getListCellRendererComponent(list,
+ value, index, isSelected, cellHasFocus);
+ component.setIcon(ItemsRenderer._icon);
+ if (value instanceof Item)
+ {
+ Item item = (Item) value;
+ if (item.getPublishDate() != null)
+ {
+ component.setText(DateFormat.getDateInstance().format(
+ item.getPublishDate())
+ + " " + item.getTitle());
+ }
+ component.setToolTipText(item.getLink());
+ if (!item.isRead())
+ {
+ component.setFont(component.getFont().deriveFont(Font.BOLD));
+ }
+ else
+ {
+ component.setFont(component.getFont().deriveFont(Font.PLAIN));
+ }
+ }
+ return component;
+ }
+}