3 import jalview.bin.Cache;
4 import java.awt.BorderLayout;
5 import java.awt.Component;
6 import java.awt.Dimension;
8 import java.awt.Rectangle;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
11 import java.awt.event.KeyEvent;
12 import java.awt.event.MouseEvent;
13 import java.awt.event.WindowAdapter;
14 import java.awt.event.WindowEvent;
15 import java.beans.PropertyChangeListener;
16 import java.text.DateFormat;
17 import java.text.SimpleDateFormat;
18 import java.util.ArrayList;
19 import java.util.Calendar;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.List;
25 import javax.swing.AbstractAction;
26 import javax.swing.AbstractButton;
27 import javax.swing.Action;
28 import javax.swing.DefaultListCellRenderer;
29 import javax.swing.DefaultListModel;
30 import javax.swing.Icon;
31 import javax.swing.ImageIcon;
32 import javax.swing.JButton;
33 import javax.swing.JFrame;
34 import javax.swing.JLabel;
35 import javax.swing.JList;
36 import javax.swing.JMenuItem;
37 import javax.swing.JPanel;
38 import javax.swing.JPopupMenu;
39 import javax.swing.JScrollPane;
40 import javax.swing.JSplitPane;
41 import javax.swing.JToolBar;
42 import javax.swing.ListSelectionModel;
43 import javax.swing.SwingUtilities;
44 import javax.swing.event.HyperlinkEvent;
45 import javax.swing.event.HyperlinkListener;
46 import javax.swing.event.ListSelectionEvent;
47 import javax.swing.event.ListSelectionListener;
49 import org.robsite.jswingreader.action.MarkChannelAsRead;
50 import org.robsite.jswingreader.action.MarkChannelAsUnread;
51 import org.robsite.jswingreader.action.MarkItemAsRead;
52 import org.robsite.jswingreader.action.MarkItemAsUnread;
53 import org.robsite.jswingreader.action.UpdatableAction;
54 import org.robsite.jswingreader.model.Channel;
55 import org.robsite.jswingreader.model.ChannelListModel;
56 import org.robsite.jswingreader.model.Item;
57 import org.robsite.jswingreader.model.SimpleRSSParser;
58 import org.robsite.jswingreader.ui.BlogContentPane;
59 import org.robsite.jswingreader.ui.ItemReadTimer;
60 import org.robsite.jswingreader.ui.Main;
61 import org.robsite.jswingreader.ui.util.ContextMenuMouseAdapter;
64 * Blog reading window, adapted from JSwingReader's
65 * org.robsite.jswingreader.ui.MainWindow class
68 public class BlogReader extends JPanel
70 private JButton buttonRefresh = new JButton();
72 private JToolBar toolBar = new JToolBar();
74 private JLabel statusBar = new JLabel();
76 private JPanel panelMain = new JPanel();
78 private BorderLayout layoutMain = new BorderLayout();
80 private BorderLayout borderLayout1 = new BorderLayout();
82 private JPanel topPanel = new JPanel();
84 private JPanel bottomPanel = new JPanel();
86 private JSplitPane topBottomSplitPane = new JSplitPane();
88 private JList listItems = new JList(new DefaultListModel());
90 // SWITCH IN JALVIEW HTML VIEWER PANE HERE
91 private BlogContentPane textDescription = new BlogContentPane();
93 // ADD IN JALVIEW BANNER FOR PRETTINESS
94 private BorderLayout borderLayout4 = new BorderLayout();
96 private BorderLayout borderLayout5 = new BorderLayout();
98 private ChannelListModel _channelModel = null;
100 private JList listChannels = new JList();
102 private Action exitAction = new Action()
106 public void actionPerformed(ActionEvent arg0)
115 parent.showNews(false);
121 public void setEnabled(boolean arg0)
127 public void removePropertyChangeListener(PropertyChangeListener arg0)
129 // TODO Auto-generated method stub
134 public void putValue(String arg0, Object arg1)
136 // TODO Auto-generated method stub
141 public boolean isEnabled()
143 // TODO Auto-generated method stub
148 public Object getValue(String arg0)
150 // TODO Auto-generated method stub
155 public void addPropertyChangeListener(PropertyChangeListener arg0)
157 // TODO Auto-generated method stub
162 private JFrame xf = null;
164 private JalviewDialog jd = null;
166 private JalviewDialog createDialog()
169 return jd = new JalviewDialog()
173 protected void raiseClosed()
177 Cache.log.info("News window closed.");
179 parent.showNews(false);
184 protected void okPressed()
186 // TODO Auto-generated method stub
191 protected void cancelPressed()
193 // TODO Auto-generated method stub
199 private JLabel lblChannels = new JLabel();
201 private List _updatableActions = new ArrayList();
203 private ItemReadTimer _itemTimer = null;
205 private JPopupMenu _popupItems = null;
207 private JPopupMenu _popupChannels = null;
209 private String lastm = "";
211 private boolean newsnew = false;
213 private Desktop parent = null;
220 // should we ignore fake gui events
221 private boolean updating = false;
223 public BlogReader(Desktop desktop)
225 Cache.log.info("Constructing news reader.");
228 _channelModel = new ChannelListModel();
229 // Construct our jalview news channel
230 Channel chan = new Channel();
231 chan.setURL(jalview.bin.Cache.getDefault(
233 jalview.bin.Cache.getDefault("www.jalview.org",
234 "http://www.jalview.org") + "/feeds/desktop/rss"));
236 _channelModel.addChannel(chan);
242 } catch (Exception e)
249 boolean setvisible = checkForNew(chan, true);
254 Cache.log.info("Will show jalview news automatically");
257 Cache.log.info("Completed construction of reader.");
262 * check if the news panel's container is visible
264 public boolean isVisible()
268 return xf != null && xf.isVisible();
270 return jd != null && jd.isVisible();
274 * display the container for the news panel
276 public void showNews()
278 final BlogReader me = this;
279 SwingUtilities.invokeLater(new Runnable()
284 Rectangle bounds = new Rectangle(5, 5, 550, 350);
288 xf.setContentPane(me);
289 xf.addWindowListener(new WindowAdapter()
291 public void windowClosing(WindowEvent e)
293 ActionEvent actionEvent = new ActionEvent(this,
294 ActionEvent.ACTION_FIRST, (String) exitAction
295 .getValue(Action.NAME));
296 exitAction.actionPerformed(actionEvent);
299 public void windowOpened(WindowEvent e)
303 me.setSize(new Dimension(550, 350));
309 bounds = new Rectangle(5, 5, 550, 350);
310 jd.initDialogFrame(me, true, false, "News from www.jalview.org",
311 bounds.width, bounds.height);
312 Cache.log.info("Displaying news.");
320 * update hasnew flag and mark all new messages as unread.
322 private boolean checkForNew(Channel chan, boolean updateItems)
325 if (!updating || updateItems)
329 java.util.Date earliest = null;
332 earliest = new SimpleDateFormat("YYYY-MM-DD").parse(chan
333 .getHTTPLastModified());
334 } catch (Exception x)
338 if (chan != null && chan.getItems() != null)
340 Cache.log.debug("Scanning news items: newsnew=" + newsnew
341 + " and lastDate is " + lastDate);
342 for (Item i : (List<Item>) chan.getItems())
344 boolean isread = lastDate == null ? false
345 : (i.getPublishDate() != null && !lastDate.before(i
348 if (!updating || updateItems)
356 if (i.getPublishDate() != null && !i.isRead())
358 if (earliest == null || earliest.after(i.getPublishDate()))
360 earliest = i.getPublishDate();
365 if (!updateItems && !updating && lastDate == null)
372 java.util.Date lastDate = null;
374 private void loadLastM()
376 lastDate = Cache.getDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED");
379 private void saveLastM(Item item)
383 if (item.getPublishDate() != null)
385 if (lastDate == null || item.getPublishDate().after(lastDate))
387 lastDate = item.getPublishDate();
391 if (_channelModel.getElementAt(0) != null)
393 checkForNew((Channel) _channelModel.getElementAt(0), false);
395 if (lastDate != null)
397 jalview.bin.Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED",
399 jalview.bin.Cache.log.info("Saved last read date as "
400 + jalview.bin.Cache.date_format.format(lastDate));
406 private void jbInit() throws Exception
408 setLayout(layoutMain);
409 panelMain.setLayout(borderLayout1);
410 topPanel.setLayout(borderLayout5);
411 bottomPanel.setLayout(borderLayout4);
412 topBottomSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
413 topBottomSplitPane.setDividerLocation(100);
414 topBottomSplitPane.setTopComponent(topPanel);
415 topBottomSplitPane.setBottomComponent(bottomPanel);
416 JScrollPane spTextDescription = new JScrollPane(textDescription);
417 textDescription.setText("");
418 statusBar.setText(" [Status] ");
419 buttonRefresh.addActionListener(new ActionListener()
423 public void actionPerformed(ActionEvent e)
428 add(statusBar, BorderLayout.SOUTH);
429 toolBar.add(buttonRefresh);
430 toolBar.addSeparator();
431 JLabel about = new JLabel(
432 "brought to you by JSwingReader (jswingreader.sourceforge.net)");
434 toolBar.setFloatable(false);
435 add(toolBar, BorderLayout.NORTH);
436 panelMain.add(topBottomSplitPane, BorderLayout.CENTER);
437 add(panelMain, BorderLayout.CENTER);
438 JScrollPane spListItems = new JScrollPane(listItems);
440 .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
441 topPanel.add(spListItems, BorderLayout.CENTER);
442 bottomPanel.add(spTextDescription, BorderLayout.CENTER);
443 listChannels.setModel(_channelModel);
445 listItems.addMouseListener(new java.awt.event.MouseAdapter()
447 public void mouseClicked(MouseEvent e)
449 listItems_mouseClicked(e);
452 _popupItems = _buildItemsPopupMenu();
453 _popupChannels = _buildChannelsPopupMenu();
454 ContextMenuMouseAdapter popupAdapter = new ContextMenuMouseAdapter(
456 ContextMenuMouseAdapter popupChannelsAdapter = new ContextMenuMouseAdapter(
458 listItems.addMouseListener(popupAdapter);
459 listItems.setCellRenderer(new ItemsRenderer());
460 lblChannels.setText("Channels");
463 private void postInit()
465 // clear the default hyperlink listener and replace with our own.
466 for (HyperlinkListener hll : textDescription.getHyperlinkListeners())
468 textDescription.removeHyperlinkListener(hll);
470 textDescription.addHyperlinkListener(new HyperlinkListener()
472 public void hyperlinkUpdate(HyperlinkEvent e)
474 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
476 Desktop.showUrl(e.getURL().toExternalForm());
481 listItems.addListSelectionListener(new ListSelectionListener()
483 public void valueChanged(ListSelectionEvent e)
485 if (e.getValueIsAdjusting() == false)
487 _itemsValueChanged(listItems);
491 listChannels.setSelectedIndex(1);
493 _updateToolbarButtons();
495 _itemTimer = new ItemReadTimer(listChannels, listItems);
496 _itemsValueChanged(listItems);
499 public class LaunchJvBrowserOnItem extends AbstractAction implements
502 JList _listItems = null;
504 public LaunchJvBrowserOnItem(JList listItems)
506 super("Open in Browser");
507 this.putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
508 this.putValue(Action.LONG_DESCRIPTION, "Open in Browser");
509 _listItems = listItems;
512 public void actionPerformed(ActionEvent e)
514 Object o = _listItems.getSelectedValue();
515 if (o instanceof Item)
517 Item item = (Item) o;
519 _listItems.repaint();
521 Desktop.showUrl(item.getLink());
525 public void update(Object o)
528 if (_listItems == null || _listItems.getModel().getSize() == 0)
532 else if (_listItems.getSelectedIndex() == -1)
540 private JPopupMenu _buildItemsPopupMenu()
542 JPopupMenu popup = new JPopupMenu();
543 popup.add(new JMenuItem(new LaunchJvBrowserOnItem(listItems)));
544 popup.addSeparator();
545 popup.add(new JMenuItem(new MarkItemAsRead(listItems)));
546 popup.add(new JMenuItem(new MarkItemAsUnread(listItems)));
550 private JPopupMenu _buildChannelsPopupMenu()
552 JPopupMenu popup = new JPopupMenu();
553 popup.add(new JMenuItem(new MarkChannelAsRead(listChannels, listItems)));
554 popup.add(new JMenuItem(
555 new MarkChannelAsUnread(listChannels, listItems)));
559 private void initItems(Channel channel)
563 channel = new Channel();
565 if (!channel.isOpen() && channel.getURL() != null)
569 SimpleRSSParser.parse(channel);
570 } catch (Exception ex)
572 ex.printStackTrace();
575 DefaultListModel itemsModel = (DefaultListModel) listItems.getModel();
577 Iterator iter = (channel.getItems() != null) ? channel.getItems()
578 .iterator() : Collections.EMPTY_LIST.iterator();
579 while (iter.hasNext())
581 itemsModel.addElement(iter.next());
583 if (itemsModel.getSize() > 0)
585 listItems.setSelectedIndex(0);
586 _itemsValueChanged(listItems);
588 setStatusBarText(channel.getURL());
592 private void _itemsValueChanged(JList itemList)
594 Item item = (Item) itemList.getSelectedValue();
597 if (itemList.getModel().getSize() > 0)
599 item = (Item) itemList.getModel().getElementAt(0);
607 itemList.setSelectedIndex(0);
611 if (_itemTimer != null)
613 // prefer a shorter delay than 5s
614 _itemTimer.setDelay(300);
616 _itemTimer.setLastItem(item);
617 final Item lastitem = item;
618 _itemTimer.addActionListener(new ActionListener()
622 public void actionPerformed(ActionEvent e)
629 setStatusBarText(item.getLink());
630 textDescription.setBlogText(item);
634 public void setStatusBarText(String text)
636 statusBar.setText(text);
639 private void _updateAllActions()
641 Iterator iter = _updatableActions.iterator();
642 while (iter.hasNext())
644 UpdatableAction action = (UpdatableAction) iter.next();
649 private void _updateToolbarButtons()
651 Map general = (Map) Main.getPreferences().get("general");
657 Component[] components = toolBar.getComponents();
658 for (int i = 0; i < components.length; i++)
660 Component component = components[i];
661 if (component instanceof JButton)
663 JButton button = (JButton) component;
664 if (Boolean.toString(false).equals(general.get("useToolBarText")))
666 // Remove the text if preferences state no toolbar text
669 if (Boolean.toString(true).equals(general.get("radioTextBelow")))
671 button.setVerticalTextPosition(AbstractButton.BOTTOM);
672 button.setHorizontalTextPosition(AbstractButton.CENTER);
674 else if (Boolean.toString(true).equals(
675 general.get("radioTextRight")))
677 button.setVerticalTextPosition(AbstractButton.CENTER);
678 button.setHorizontalTextPosition(AbstractButton.RIGHT);
684 private void listItems_mouseClicked(MouseEvent e)
686 if (e.getClickCount() == 2 && e.getModifiersEx() == MouseEvent.NOBUTTON)
688 Item item = (Item) listItems.getSelectedValue();
691 if (_itemTimer != null)
696 Action action = new LaunchJvBrowserOnItem(listItems);
697 ActionEvent event = new ActionEvent(this,
698 ActionEvent.ACTION_PERFORMED, "LaunchBrowserOnItem");
699 action.actionPerformed(event);
704 * force the news panel to refresh
706 public void refreshNews()
710 initItems((Channel) _channelModel.getElementAt(0));
712 } catch (Exception x)
717 public static void main(String args[])
719 // this tests the detection of new news based on the last read date stored
720 // in jalview properties
721 jalview.bin.Cache.loadProperties(null);
722 jalview.bin.Cache.initLogger();
723 // test will advance read date each time
724 Calendar today = Calendar.getInstance(), lastread = Calendar
726 lastread.set(1983, 01, 01);
727 while (lastread.before(today))
729 Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED",
731 BlogReader me = new BlogReader();
732 System.out.println("Set last date to "
733 + jalview.bin.Cache.date_format.format(lastread.getTime()));
736 Cache.log.info("There is news to read.");
740 Cache.log.info("There is no new news.");
741 me.xf.setTitle("Testing : Last read is " + me.lastDate);
745 Cache.log.info("Waiting for closure.");
751 } catch (InterruptedException x)
755 } while (me.isVisible());
759 Cache.log.info("Still new news after reader displayed.");
761 if (lastread.getTime().before(me.lastDate))
763 Cache.log.info("The news was read.");
764 lastread.setTime(me.lastDate);
768 lastread.add(Calendar.MONTH, 1);
780 class ChannelsRenderer extends DefaultListCellRenderer
782 private final static Icon _icon = new ImageIcon(
783 Main.class.getResource("image/ComposeMail16.gif"));
785 public Component getListCellRendererComponent(JList list, Object value,
786 int index, boolean isSelected, boolean cellHasFocus)
788 JLabel component = (JLabel) super.getListCellRendererComponent(list,
789 value, index, isSelected, cellHasFocus);
790 component.setIcon(ChannelsRenderer._icon);
791 if (value instanceof Channel)
793 Channel channel = (Channel) value;
794 component.setText(channel.getTitle() + " ("
795 + channel.getUnreadItemCount() + ")");
796 component.setToolTipText(channel.getURL());
802 class ItemsRenderer extends DefaultListCellRenderer
804 private final static Icon _icon = new ImageIcon(
805 Main.class.getResource("image/ComposeMail16.gif"));
807 public Component getListCellRendererComponent(JList list, Object value,
808 int index, boolean isSelected, boolean cellHasFocus)
810 JLabel component = (JLabel) super.getListCellRendererComponent(list,
811 value, index, isSelected, cellHasFocus);
812 component.setIcon(ItemsRenderer._icon);
813 if (value instanceof Item)
815 Item item = (Item) value;
816 if (item.getPublishDate() != null)
818 component.setText(DateFormat.getDateInstance().format(
819 item.getPublishDate())
820 + " " + item.getTitle());
822 component.setToolTipText(item.getLink());
825 component.setFont(component.getFont().deriveFont(Font.BOLD));
829 component.setFont(component.getFont().deriveFont(Font.PLAIN));