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