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