JAL-1448 always use UK locale for formatted date properties
[jalview.git] / src / jalview / gui / BlogReader.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.bin.Cache;
24 import jalview.util.MessageManager;
25
26 import java.awt.BorderLayout;
27 import java.awt.Component;
28 import java.awt.Dialog.ModalExclusionType;
29 import java.awt.Dimension;
30 import java.awt.Font;
31 import java.awt.Rectangle;
32 import java.awt.event.ActionEvent;
33 import java.awt.event.ActionListener;
34 import java.awt.event.KeyEvent;
35 import java.awt.event.MouseEvent;
36 import java.awt.event.WindowAdapter;
37 import java.awt.event.WindowEvent;
38 import java.beans.PropertyChangeListener;
39 import java.text.DateFormat;
40 import java.text.SimpleDateFormat;
41 import java.util.ArrayList;
42 import java.util.Calendar;
43 import java.util.Collections;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47
48 import javax.swing.AbstractAction;
49 import javax.swing.AbstractButton;
50 import javax.swing.Action;
51 import javax.swing.DefaultListCellRenderer;
52 import javax.swing.DefaultListModel;
53 import javax.swing.Icon;
54 import javax.swing.ImageIcon;
55 import javax.swing.JButton;
56 import javax.swing.JFrame;
57 import javax.swing.JLabel;
58 import javax.swing.JList;
59 import javax.swing.JMenuItem;
60 import javax.swing.JPanel;
61 import javax.swing.JPopupMenu;
62 import javax.swing.JScrollPane;
63 import javax.swing.JSplitPane;
64 import javax.swing.JToolBar;
65 import javax.swing.ListSelectionModel;
66 import javax.swing.SwingUtilities;
67 import javax.swing.event.HyperlinkEvent;
68 import javax.swing.event.HyperlinkListener;
69 import javax.swing.event.ListSelectionEvent;
70 import javax.swing.event.ListSelectionListener;
71
72 import org.robsite.jswingreader.action.MarkChannelAsRead;
73 import org.robsite.jswingreader.action.MarkChannelAsUnread;
74 import org.robsite.jswingreader.action.MarkItemAsRead;
75 import org.robsite.jswingreader.action.MarkItemAsUnread;
76 import org.robsite.jswingreader.action.UpdatableAction;
77 import org.robsite.jswingreader.model.Channel;
78 import org.robsite.jswingreader.model.ChannelListModel;
79 import org.robsite.jswingreader.model.Item;
80 import org.robsite.jswingreader.model.SimpleRSSParser;
81 import org.robsite.jswingreader.ui.BlogContentPane;
82 import org.robsite.jswingreader.ui.ItemReadTimer;
83 import org.robsite.jswingreader.ui.Main;
84 import org.robsite.jswingreader.ui.util.ContextMenuMouseAdapter;
85
86 /**
87  * Blog reading window, adapted from JSwingReader's
88  * org.robsite.jswingreader.ui.MainWindow class
89  */
90
91 public class BlogReader extends JPanel
92 {
93   private JButton buttonRefresh = new JButton();
94
95   private JToolBar toolBar = new JToolBar();
96
97   private JLabel statusBar = new JLabel();
98
99   private JPanel panelMain = new JPanel();
100
101   private BorderLayout layoutMain = new BorderLayout();
102
103   private BorderLayout borderLayout1 = new BorderLayout();
104
105   private JPanel topPanel = new JPanel();
106
107   private JPanel bottomPanel = new JPanel();
108
109   private JSplitPane topBottomSplitPane = new JSplitPane();
110
111   private JList listItems = new JList(new DefaultListModel());
112
113   // SWITCH IN JALVIEW HTML VIEWER PANE HERE
114   private BlogContentPane textDescription = new BlogContentPane();
115
116   // ADD IN JALVIEW BANNER FOR PRETTINESS
117   private BorderLayout borderLayout4 = new BorderLayout();
118
119   private BorderLayout borderLayout5 = new BorderLayout();
120
121   private ChannelListModel _channelModel = null;
122
123   private JList listChannels = new JList();
124
125   private Action exitAction = new Action()
126   {
127
128     @Override
129     public void actionPerformed(ActionEvent arg0)
130     {
131       if (xf != null)
132       {
133         xf.dispose();
134       }
135       xf = null;
136       jd = null;
137       if (parent != null)
138       {
139         parent.showNews(false);
140       }
141
142     }
143
144     @Override
145     public void setEnabled(boolean arg0)
146     {
147
148     }
149
150     @Override
151     public void removePropertyChangeListener(PropertyChangeListener arg0)
152     {
153       // TODO Auto-generated method stub
154
155     }
156
157     @Override
158     public void putValue(String arg0, Object arg1)
159     {
160       // TODO Auto-generated method stub
161
162     }
163
164     @Override
165     public boolean isEnabled()
166     {
167       // TODO Auto-generated method stub
168       return true;
169     }
170
171     @Override
172     public Object getValue(String arg0)
173     {
174       // TODO Auto-generated method stub
175       return null;
176     }
177
178     @Override
179     public void addPropertyChangeListener(PropertyChangeListener arg0)
180     {
181       // TODO Auto-generated method stub
182
183     }
184   };
185
186   private JFrame xf = null;
187
188   private JalviewDialog jd = null;
189
190   private JalviewDialog createDialog()
191   {
192
193     return jd = new JalviewDialog()
194     {
195
196       @Override
197       protected void raiseClosed()
198       {
199         if (parent != null)
200         {
201           Cache.log.debug("News window closed.");
202           jd = null;
203           parent.showNews(false);
204         }
205       }
206
207       @Override
208       protected void okPressed()
209       {
210         // TODO Auto-generated method stub
211
212       }
213
214       @Override
215       protected void cancelPressed()
216       {
217         // TODO Auto-generated method stub
218
219       }
220     };
221   };
222
223   private JLabel lblChannels = new JLabel();
224
225   private List _updatableActions = new ArrayList();
226
227   private ItemReadTimer _itemTimer = null;
228
229   private JPopupMenu _popupItems = null;
230
231   private JPopupMenu _popupChannels = null;
232
233   private String lastm = "";
234
235   private boolean newsnew = false;
236
237   private Desktop parent = null;
238
239   BlogReader()
240   {
241     this(null);
242   }
243
244   // should we ignore fake gui events
245   private boolean updating = false;
246
247   public BlogReader(Desktop desktop)
248   {
249     Cache.log.debug("Constructing news reader.");
250
251     parent = desktop;
252     _channelModel = new ChannelListModel();
253     // Construct our jalview news channel
254     Channel chan = new Channel();
255     chan.setURL(jalview.bin.Cache.getDefault(
256             "JALVIEW_NEWS_RSS",
257             jalview.bin.Cache.getDefault("www.jalview.org",
258                     "http://www.jalview.org") + "/feeds/desktop/rss"));
259     loadLastM();
260     _channelModel.addChannel(chan);
261     updating = true;
262     try
263     {
264       jbInit();
265       postInit();
266     } catch (Exception e)
267     {
268       e.printStackTrace();
269     }
270
271     initItems(chan);
272     updating = false;
273     boolean setvisible = checkForNew(chan, true);
274
275     if (setvisible)
276     {
277
278       Cache.log.debug("Will show jalview news automatically");
279       showNews();
280     }
281     Cache.log.debug("Completed construction of reader.");
282
283   }
284
285   /**
286    * check if the news panel's container is visible
287    */
288   @Override
289   public boolean isVisible()
290   {
291     if (parent == null)
292     {
293       return xf != null && xf.isVisible();
294     }
295     return jd != null && jd.isVisible();
296   }
297
298   /**
299    * display the container for the news panel
300    */
301   public void showNews()
302   {
303     final BlogReader me = this;
304     SwingUtilities.invokeLater(new Runnable()
305     {
306       @Override
307       public void run()
308       {
309         Rectangle bounds = new Rectangle(5, 5, 550, 350);
310         if (parent == null)
311         {
312           xf = new JFrame();
313           xf.setContentPane(me);
314           xf.addWindowListener(new WindowAdapter()
315           {
316             @Override
317             public void windowClosing(WindowEvent e)
318             {
319               ActionEvent actionEvent = new ActionEvent(this,
320                       ActionEvent.ACTION_FIRST, (String) exitAction
321                               .getValue(Action.NAME));
322               exitAction.actionPerformed(actionEvent);
323             }
324
325             @Override
326             public void windowOpened(WindowEvent e)
327             {
328             }
329           });
330           me.setSize(new Dimension(550, 350));
331           xf.setVisible(true);
332         }
333         else
334         {
335           createDialog();
336           bounds = new Rectangle(5, 5, 550, 350);
337           jd.initDialogFrame(me, false, false,
338                   MessageManager.getString("label.news_from_jalview"),
339                   bounds.width, bounds.height);
340           jd.frame.setModalExclusionType(ModalExclusionType.NO_EXCLUDE);
341           Cache.log.debug("Displaying news.");
342           jd.waitForInput();
343         }
344       }
345     });
346   }
347
348   /**
349    * update hasnew flag and mark all new messages as unread.
350    */
351   private boolean checkForNew(Channel chan, boolean updateItems)
352   {
353
354     if (!updating || updateItems)
355     {
356       newsnew = false;
357     }
358     java.util.Date earliest = null;
359     try
360     {
361       earliest = new SimpleDateFormat("YYYY-MM-DD").parse(chan
362               .getHTTPLastModified());
363     } catch (Exception x)
364     {
365     }
366     ;
367     if (chan != null && chan.getItems() != null)
368     {
369       Cache.log.debug("Scanning news items: newsnew=" + newsnew
370               + " and lastDate is " + lastDate);
371       for (Item i : (List<Item>) chan.getItems())
372       {
373         boolean isread = lastDate == null ? false
374                 : (i.getPublishDate() != null && !lastDate.before(i
375                         .getPublishDate()));
376
377         if (!updating || updateItems)
378         {
379           newsnew |= !isread;
380         }
381         if (updateItems)
382         {
383           i.setRead(isread);
384         }
385         if (i.getPublishDate() != null && !i.isRead())
386         {
387           if (earliest == null || earliest.after(i.getPublishDate()))
388           {
389             earliest = i.getPublishDate();
390           }
391         }
392       }
393     }
394     if (!updateItems && !updating && lastDate == null)
395     {
396       lastDate = earliest;
397     }
398     return newsnew;
399   }
400
401   java.util.Date lastDate = null;
402
403   private void loadLastM()
404   {
405     lastDate = Cache.getDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED");
406   }
407
408   private void saveLastM(Item item)
409   {
410     if (item != null)
411     {
412       if (item.getPublishDate() != null)
413       {
414         if (lastDate == null || item.getPublishDate().after(lastDate))
415         {
416           lastDate = item.getPublishDate();
417         }
418       }
419
420       if (_channelModel.getElementAt(0) != null)
421       {
422         checkForNew((Channel) _channelModel.getElementAt(0), false);
423       }
424       if (lastDate != null)
425       {
426         String formatted = Cache.setDateProperty(
427                 "JALVIEW_NEWS_RSS_LASTMODIFIED", lastDate);
428         Cache.log.debug("Saved last read date as " + formatted);
429       }
430     }
431   }
432
433   private void jbInit() throws Exception
434   {
435     setLayout(layoutMain);
436     panelMain.setLayout(borderLayout1);
437     topPanel.setLayout(borderLayout5);
438     bottomPanel.setLayout(borderLayout4);
439     topBottomSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
440     topBottomSplitPane.setDividerLocation(100);
441     topBottomSplitPane.setTopComponent(topPanel);
442     topBottomSplitPane.setBottomComponent(bottomPanel);
443     JScrollPane spTextDescription = new JScrollPane(textDescription);
444     textDescription.setText("");
445     statusBar.setText(new StringBuffer("[")
446             .append(MessageManager.getString("label.status")).append("]")
447             .toString());
448     buttonRefresh.addActionListener(new ActionListener()
449     {
450
451       @Override
452       public void actionPerformed(ActionEvent e)
453       {
454         refreshNews();
455       }
456     });
457     add(statusBar, BorderLayout.SOUTH);
458     toolBar.add(buttonRefresh);
459     toolBar.addSeparator();
460     JLabel about = new JLabel(
461             "brought to you by JSwingReader (jswingreader.sourceforge.net)");
462     toolBar.add(about);
463     toolBar.setFloatable(false);
464     add(toolBar, BorderLayout.NORTH);
465     panelMain.add(topBottomSplitPane, BorderLayout.CENTER);
466     add(panelMain, BorderLayout.CENTER);
467     JScrollPane spListItems = new JScrollPane(listItems);
468     listItems
469             .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
470     topPanel.add(spListItems, BorderLayout.CENTER);
471     bottomPanel.add(spTextDescription, BorderLayout.CENTER);
472     listChannels.setModel(_channelModel);
473
474     listItems.addMouseListener(new java.awt.event.MouseAdapter()
475     {
476       @Override
477       public void mouseClicked(MouseEvent e)
478       {
479         listItems_mouseClicked(e);
480       }
481     });
482     _popupItems = _buildItemsPopupMenu();
483     _popupChannels = _buildChannelsPopupMenu();
484     ContextMenuMouseAdapter popupAdapter = new ContextMenuMouseAdapter(
485             _popupItems);
486     ContextMenuMouseAdapter popupChannelsAdapter = new ContextMenuMouseAdapter(
487             _popupChannels);
488     listItems.addMouseListener(popupAdapter);
489     listItems.setCellRenderer(new ItemsRenderer());
490     lblChannels.setText(MessageManager.getString("label.channels"));
491   }
492
493   private void postInit()
494   {
495     // clear the default hyperlink listener and replace with our own.
496     for (HyperlinkListener hll : textDescription.getHyperlinkListeners())
497     {
498       textDescription.removeHyperlinkListener(hll);
499     }
500     textDescription.addHyperlinkListener(new HyperlinkListener()
501     {
502       @Override
503       public void hyperlinkUpdate(HyperlinkEvent e)
504       {
505         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
506         {
507           Desktop.showUrl(e.getURL().toExternalForm());
508         }
509       }
510     });
511
512     listItems.addListSelectionListener(new ListSelectionListener()
513     {
514       @Override
515       public void valueChanged(ListSelectionEvent e)
516       {
517         if (e.getValueIsAdjusting() == false)
518         {
519           _itemsValueChanged(listItems);
520         }
521       }
522     });
523     listChannels.setSelectedIndex(1);
524     _updateAllActions();
525     _updateToolbarButtons();
526
527     _itemTimer = new ItemReadTimer(listChannels, listItems);
528     _itemsValueChanged(listItems);
529   }
530
531   public class LaunchJvBrowserOnItem extends AbstractAction implements
532           UpdatableAction
533   {
534     JList _listItems = null;
535
536     public LaunchJvBrowserOnItem(JList listItems)
537     {
538       super("Open in Browser");
539       this.putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
540       this.putValue(Action.LONG_DESCRIPTION, "Open in Browser");
541       _listItems = listItems;
542     }
543
544     @Override
545     public void actionPerformed(ActionEvent e)
546     {
547       Object o = _listItems.getSelectedValue();
548       if (o instanceof Item)
549       {
550         Item item = (Item) o;
551         item.setRead(true);
552         _listItems.repaint();
553
554         Desktop.showUrl(item.getLink());
555       }
556     }
557
558     @Override
559     public void update(Object o)
560     {
561       setEnabled(true);
562       if (_listItems == null || _listItems.getModel().getSize() == 0)
563       {
564         setEnabled(false);
565       }
566       else if (_listItems.getSelectedIndex() == -1)
567       {
568         setEnabled(false);
569       }
570     }
571
572   }
573
574   private JPopupMenu _buildItemsPopupMenu()
575   {
576     JPopupMenu popup = new JPopupMenu();
577     popup.add(new JMenuItem(new LaunchJvBrowserOnItem(listItems)));
578     popup.addSeparator();
579     popup.add(new JMenuItem(new MarkItemAsRead(listItems)));
580     popup.add(new JMenuItem(new MarkItemAsUnread(listItems)));
581     return popup;
582   }
583
584   private JPopupMenu _buildChannelsPopupMenu()
585   {
586     JPopupMenu popup = new JPopupMenu();
587     popup.add(new JMenuItem(new MarkChannelAsRead(listChannels, listItems)));
588     popup.add(new JMenuItem(
589             new MarkChannelAsUnread(listChannels, listItems)));
590     return popup;
591   }
592
593   private void initItems(Channel channel)
594   {
595     if (channel == null)
596     {
597       channel = new Channel();
598     }
599     if (!channel.isOpen() && channel.getURL() != null)
600     {
601       try
602       {
603         SimpleRSSParser.parse(channel);
604       } catch (Exception ex)
605       {
606         ex.printStackTrace();
607       }
608     }
609     DefaultListModel itemsModel = (DefaultListModel) listItems.getModel();
610     itemsModel.clear();
611     Iterator iter = (channel.getItems() != null) ? channel.getItems()
612             .iterator() : Collections.EMPTY_LIST.iterator();
613     while (iter.hasNext())
614     {
615       itemsModel.addElement(iter.next());
616     }
617     if (itemsModel.getSize() > 0)
618     {
619       listItems.setSelectedIndex(0);
620       _itemsValueChanged(listItems);
621     }
622     setStatusBarText(channel.getURL());
623     _updateAllActions();
624   }
625
626   private void _itemsValueChanged(JList itemList)
627   {
628     Item item = (Item) itemList.getSelectedValue();
629     if (item == null)
630     {
631       if (itemList.getModel().getSize() > 0)
632       {
633         item = (Item) itemList.getModel().getElementAt(0);
634       }
635       if (item == null)
636       {
637         item = new Item();
638       }
639       else
640       {
641         itemList.setSelectedIndex(0);
642       }
643     }
644
645     if (_itemTimer != null)
646     {
647       // prefer a shorter delay than 5s
648       _itemTimer.setDelay(300);
649       _itemTimer.start();
650       _itemTimer.setLastItem(item);
651       final Item lastitem = item;
652       _itemTimer.addActionListener(new ActionListener()
653       {
654
655         @Override
656         public void actionPerformed(ActionEvent e)
657         {
658           saveLastM(lastitem);
659         }
660       });
661     }
662
663     setStatusBarText(item.getLink());
664     textDescription.setBlogText(item);
665     _updateAllActions();
666   }
667
668   public void setStatusBarText(String text)
669   {
670     statusBar.setText(text);
671   }
672
673   private void _updateAllActions()
674   {
675     Iterator iter = _updatableActions.iterator();
676     while (iter.hasNext())
677     {
678       UpdatableAction action = (UpdatableAction) iter.next();
679       action.update(this);
680     }
681   }
682
683   private void _updateToolbarButtons()
684   {
685     Map general = (Map) Main.getPreferences().get("general");
686     if (general == null)
687     {
688       return;
689     }
690
691     Component[] components = toolBar.getComponents();
692     for (int i = 0; i < components.length; i++)
693     {
694       Component component = components[i];
695       if (component instanceof JButton)
696       {
697         JButton button = (JButton) component;
698         if (Boolean.toString(false).equals(general.get("useToolBarText")))
699         {
700           // Remove the text if preferences state no toolbar text
701           button.setText("");
702         }
703         if (Boolean.toString(true).equals(general.get("radioTextBelow")))
704         {
705           button.setVerticalTextPosition(AbstractButton.BOTTOM);
706           button.setHorizontalTextPosition(AbstractButton.CENTER);
707         }
708         else if (Boolean.toString(true).equals(
709                 general.get("radioTextRight")))
710         {
711           button.setVerticalTextPosition(AbstractButton.CENTER);
712           button.setHorizontalTextPosition(AbstractButton.RIGHT);
713         }
714       }
715     }
716   }
717
718   private void listItems_mouseClicked(MouseEvent e)
719   {
720     if (e.getClickCount() == 2 && e.getModifiersEx() == MouseEvent.NOBUTTON)
721     {
722       Item item = (Item) listItems.getSelectedValue();
723       item.setRead(true);
724       saveLastM(item);
725       if (_itemTimer != null)
726       {
727         _itemTimer.stop();
728       }
729
730       Action action = new LaunchJvBrowserOnItem(listItems);
731       ActionEvent event = new ActionEvent(this,
732               ActionEvent.ACTION_PERFORMED, "LaunchBrowserOnItem");
733       action.actionPerformed(event);
734     }
735   }
736
737   /**
738    * force the news panel to refresh
739    */
740   public void refreshNews()
741   {
742     try
743     {
744       initItems((Channel) _channelModel.getElementAt(0));
745
746     } catch (Exception x)
747     {
748     }
749   }
750
751   public static void main(String args[])
752   {
753     // this tests the detection of new news based on the last read date stored
754     // in jalview properties
755     jalview.bin.Cache.loadProperties(null);
756     jalview.bin.Cache.initLogger();
757     // test will advance read date each time
758     Calendar today = Calendar.getInstance(), lastread = Calendar
759             .getInstance();
760     lastread.set(1983, 01, 01);
761     while (lastread.before(today))
762     {
763       String formattedDate = Cache.setDateProperty(
764               "JALVIEW_NEWS_RSS_LASTMODIFIED", lastread.getTime());
765       BlogReader me = new BlogReader();
766       System.out.println("Set last date to " + formattedDate);
767       if (me.isNewsNew())
768       {
769         Cache.log.debug("There is news to read.");
770       }
771       else
772       {
773         Cache.log.debug("There is no new news.");
774         me.xf.setTitle("Testing : Last read is " + me.lastDate);
775         me.showNews();
776         me.xf.toFront();
777       }
778       Cache.log.debug("Waiting for closure.");
779       do
780       {
781         try
782         {
783           Thread.sleep(300);
784         } catch (InterruptedException x)
785         {
786         }
787         ;
788       } while (me.isVisible());
789
790       if (me.isNewsNew())
791       {
792         Cache.log.debug("Still new news after reader displayed.");
793       }
794       if (lastread.getTime().before(me.lastDate))
795       {
796         Cache.log.debug("The news was read.");
797         lastread.setTime(me.lastDate);
798       }
799       else
800       {
801         lastread.add(Calendar.MONTH, 1);
802       }
803
804     }
805   }
806
807   boolean isNewsNew()
808   {
809     return newsnew;
810   }
811 }
812
813 class ChannelsRenderer extends DefaultListCellRenderer
814 {
815   private final static Icon _icon = new ImageIcon(
816           Main.class.getResource("image/ComposeMail16.gif"));
817
818   @Override
819   public Component getListCellRendererComponent(JList list, Object value,
820           int index, boolean isSelected, boolean cellHasFocus)
821   {
822     JLabel component = (JLabel) super.getListCellRendererComponent(list,
823             value, index, isSelected, cellHasFocus);
824     component.setIcon(ChannelsRenderer._icon);
825     if (value instanceof Channel)
826     {
827       Channel channel = (Channel) value;
828       component
829               .setText(MessageManager.formatMessage(
830                       "label.channel_title_item_count",
831                       new String[] {
832                           channel.getTitle(),
833                           Integer.valueOf(channel.getUnreadItemCount())
834                                   .toString() }));
835       component.setToolTipText(channel.getURL());
836     }
837     return component;
838   }
839 }
840
841 class ItemsRenderer extends DefaultListCellRenderer
842 {
843   private final static Icon _icon = new ImageIcon(
844           Main.class.getResource("image/ComposeMail16.gif"));
845
846   @Override
847   public Component getListCellRendererComponent(JList list, Object value,
848           int index, boolean isSelected, boolean cellHasFocus)
849   {
850     JLabel component = (JLabel) super.getListCellRendererComponent(list,
851             value, index, isSelected, cellHasFocus);
852     component.setIcon(ItemsRenderer._icon);
853     if (value instanceof Item)
854     {
855       Item item = (Item) value;
856       if (item.getPublishDate() != null)
857       {
858         component.setText(MessageManager.formatMessage(
859                 "label.blog_item_published_on_date",
860                 new String[] {
861                     DateFormat
862                             .getDateInstance(DateFormat.LONG,
863                                     MessageManager.getLocale())
864                             .format(item.getPublishDate()).toString(),
865                     item.getTitle() }));
866       }
867       component.setToolTipText(item.getLink());
868       if (!item.isRead())
869       {
870         component.setFont(component.getFont().deriveFont(Font.BOLD));
871       }
872       else
873       {
874         component.setFont(component.getFont().deriveFont(Font.PLAIN));
875       }
876     }
877     return component;
878   }
879 }