JAL-3949 - refactor logging from jalview.bin.Cache to jalview.bin.Console
[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(
258             Cache.getDefault("JALVIEW_NEWS_RSS",
259                     Cache.getDefault("www.jalview.org",
260                             "https://www.jalview.org")
261                             + "/feeds/desktop/rss"));
262     loadLastM();
263     _channelModel.addChannel(chan);
264     updating = true;
265     try
266     {
267       jbInit();
268       postInit();
269     } catch (Exception e)
270     {
271       e.printStackTrace();
272     }
273
274     initItems(chan);
275     updating = false;
276     boolean setvisible = checkForNew(chan, true);
277
278     if (setvisible)
279     {
280
281       Console.debug("Will show jalview news automatically");
282       showNews();
283     }
284     Console.debug("Completed construction of reader.");
285
286   }
287
288   /**
289    * check if the news panel's container is visible
290    */
291   @Override
292   public boolean isVisible()
293   {
294     if (parent == null)
295     {
296       return xf != null && xf.isVisible();
297     }
298     return jd != null && jd.isVisible();
299   }
300
301   /**
302    * display the container for the news panel
303    */
304   public void showNews()
305   {
306     final BlogReader me = this;
307     SwingUtilities.invokeLater(new Runnable()
308     {
309       @Override
310       public void run()
311       {
312         Rectangle bounds = new Rectangle(5, 5, 550, 350);
313         if (parent == null)
314         {
315           xf = new JFrame();
316           xf.setContentPane(me);
317           xf.addWindowListener(new WindowAdapter()
318           {
319             @Override
320             public void windowClosing(WindowEvent e)
321             {
322               ActionEvent actionEvent = new ActionEvent(this,
323                       ActionEvent.ACTION_FIRST,
324                       (String) exitAction.getValue(Action.NAME));
325               exitAction.actionPerformed(actionEvent);
326             }
327
328             @Override
329             public void windowOpened(WindowEvent e)
330             {
331             }
332           });
333           me.setSize(new Dimension(550, 350));
334           xf.setVisible(true);
335         }
336         else
337         {
338           createDialog();
339           bounds = new Rectangle(5, 5, 550, 350);
340           jd.initDialogFrame(me, false, false,
341                   MessageManager.getString("label.news_from_jalview"),
342                   bounds.width, bounds.height);
343           jd.frame.setModalExclusionType(ModalExclusionType.NO_EXCLUDE);
344           Console.debug("Displaying news.");
345           jd.waitForInput();
346         }
347       }
348     });
349   }
350
351   /**
352    * update hasnew flag and mark all new messages as unread.
353    */
354   private boolean checkForNew(Channel chan, boolean updateItems)
355   {
356
357     if (!updating || updateItems)
358     {
359       newsnew = false;
360     }
361     java.util.Date earliest = null;
362     try
363     {
364       earliest = new SimpleDateFormat("YYYY-MM-DD")
365               .parse(chan.getHTTPLastModified());
366     } catch (Exception x)
367     {
368     }
369     if (chan != null && chan.getItems() != null)
370     {
371       Console.debug("Scanning news items: newsnew=" + newsnew
372               + " and lastDate is " + lastDate);
373       for (Item i : (List<Item>) chan.getItems())
374       {
375         Date published = i.getPublishDate();
376         boolean isread = lastDate == null ? false
377                 : (published != null && !lastDate.before(published));
378
379         if (!updating || updateItems)
380         {
381           newsnew |= !isread;
382         }
383         if (updateItems)
384         {
385           i.setRead(isread);
386         }
387         if (published != null && !i.isRead())
388         {
389           if (earliest == null || earliest.after(published))
390           {
391             earliest = published;
392           }
393         }
394       }
395     }
396     if (!updateItems && !updating && lastDate == null)
397     {
398       lastDate = earliest;
399     }
400     return newsnew;
401   }
402
403   java.util.Date lastDate = null;
404
405   private void loadLastM()
406   {
407     lastDate = Cache.getDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED");
408   }
409
410   private void saveLastM(Item item)
411   {
412     if (item != null)
413     {
414       if (item.getPublishDate() != null)
415       {
416         if (lastDate == null || item.getPublishDate().after(lastDate))
417         {
418           lastDate = item.getPublishDate();
419         }
420       }
421
422       if (_channelModel.getElementAt(0) != null)
423       {
424         checkForNew((Channel) _channelModel.getElementAt(0), false);
425       }
426       if (lastDate != null)
427       {
428         String formatted = Cache
429                 .setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED", lastDate);
430         Console.debug("Saved last read date as " + formatted);
431       }
432     }
433   }
434
435   private void jbInit() throws Exception
436   {
437     setLayout(layoutMain);
438     panelMain.setLayout(borderLayout1);
439     topPanel.setLayout(borderLayout5);
440     bottomPanel.setLayout(borderLayout4);
441     topBottomSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
442     topBottomSplitPane.setDividerLocation(100);
443     topBottomSplitPane.setTopComponent(topPanel);
444     topBottomSplitPane.setBottomComponent(bottomPanel);
445     JScrollPane spTextDescription = new JScrollPane(textDescription);
446     textDescription.setText("");
447     statusBar.setText(new StringBuffer("[")
448             .append(MessageManager.getString("label.status")).append("]")
449             .toString());
450     buttonRefresh.addActionListener(new ActionListener()
451     {
452
453       @Override
454       public void actionPerformed(ActionEvent e)
455       {
456         refreshNews();
457       }
458     });
459     add(statusBar, BorderLayout.SOUTH);
460     toolBar.add(buttonRefresh);
461     toolBar.addSeparator();
462     JLabel about = new JLabel(
463             "brought to you by JSwingReader (jswingreader.sourceforge.net)");
464     toolBar.add(about);
465     toolBar.setFloatable(false);
466     add(toolBar, BorderLayout.NORTH);
467     panelMain.add(topBottomSplitPane, BorderLayout.CENTER);
468     add(panelMain, BorderLayout.CENTER);
469     JScrollPane spListItems = new JScrollPane(listItems);
470     listItems
471             .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
472     topPanel.add(spListItems, BorderLayout.CENTER);
473     bottomPanel.add(spTextDescription, BorderLayout.CENTER);
474     listChannels.setModel(_channelModel);
475
476     listItems.addMouseListener(new java.awt.event.MouseAdapter()
477     {
478       @Override
479       public void mouseClicked(MouseEvent e)
480       {
481         listItems_mouseClicked(e);
482       }
483     });
484     _popupItems = _buildItemsPopupMenu();
485     _popupChannels = _buildChannelsPopupMenu();
486     ContextMenuMouseAdapter popupAdapter = new ContextMenuMouseAdapter(
487             _popupItems);
488     ContextMenuMouseAdapter popupChannelsAdapter = new ContextMenuMouseAdapter(
489             _popupChannels);
490     listItems.addMouseListener(popupAdapter);
491     listItems.setCellRenderer(new ItemsRenderer());
492     lblChannels.setText(MessageManager.getString("label.channels"));
493   }
494
495   private void postInit()
496   {
497     // clear the default hyperlink listener and replace with our own.
498     for (HyperlinkListener hll : textDescription.getHyperlinkListeners())
499     {
500       textDescription.removeHyperlinkListener(hll);
501     }
502     textDescription.addHyperlinkListener(new HyperlinkListener()
503     {
504       @Override
505       public void hyperlinkUpdate(HyperlinkEvent e)
506       {
507         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
508         {
509           Desktop.showUrl(e.getURL().toExternalForm());
510         }
511       }
512     });
513
514     listItems.addListSelectionListener(new ListSelectionListener()
515     {
516       @Override
517       public void valueChanged(ListSelectionEvent e)
518       {
519         if (e.getValueIsAdjusting() == false)
520         {
521           _itemsValueChanged(listItems);
522         }
523       }
524     });
525     listChannels.setSelectedIndex(1);
526     _updateAllActions();
527     _updateToolbarButtons();
528
529     _itemTimer = new ItemReadTimer(listChannels, listItems);
530     _itemsValueChanged(listItems);
531   }
532
533   public class LaunchJvBrowserOnItem extends AbstractAction
534           implements UpdatableAction
535   {
536     JList _listItems = null;
537
538     public LaunchJvBrowserOnItem(JList listItems)
539     {
540       super("Open in Browser");
541       this.putValue(MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_O));
542       this.putValue(Action.LONG_DESCRIPTION, "Open in Browser");
543       _listItems = listItems;
544     }
545
546     @Override
547     public void actionPerformed(ActionEvent e)
548     {
549       Object o = _listItems.getSelectedValue();
550       if (o instanceof Item)
551       {
552         Item item = (Item) o;
553         item.setRead(true);
554         _listItems.repaint();
555
556         Desktop.showUrl(item.getLink());
557       }
558     }
559
560     @Override
561     public void update(Object o)
562     {
563       setEnabled(true);
564       if (_listItems == null || _listItems.getModel().getSize() == 0)
565       {
566         setEnabled(false);
567       }
568       else if (_listItems.getSelectedIndex() == -1)
569       {
570         setEnabled(false);
571       }
572     }
573
574   }
575
576   private JPopupMenu _buildItemsPopupMenu()
577   {
578     JPopupMenu popup = new JPopupMenu();
579     popup.add(new JMenuItem(new LaunchJvBrowserOnItem(listItems)));
580     popup.addSeparator();
581     popup.add(new JMenuItem(new MarkItemAsRead(listItems)));
582     popup.add(new JMenuItem(new MarkItemAsUnread(listItems)));
583     return popup;
584   }
585
586   private JPopupMenu _buildChannelsPopupMenu()
587   {
588     JPopupMenu popup = new JPopupMenu();
589     popup.add(
590             new JMenuItem(new MarkChannelAsRead(listChannels, listItems)));
591     popup.add(new JMenuItem(
592             new MarkChannelAsUnread(listChannels, listItems)));
593     return popup;
594   }
595
596   private void initItems(Channel channel)
597   {
598     if (channel == null)
599     {
600       channel = new Channel();
601     }
602     if (!channel.isOpen() && channel.getURL() != null)
603     {
604       try
605       {
606         SimpleRSSParser.parse(channel);
607       } catch (Exception ex)
608       {
609         ex.printStackTrace();
610       }
611     }
612     DefaultListModel itemsModel = (DefaultListModel) listItems.getModel();
613     itemsModel.clear();
614     Iterator iter = (channel.getItems() != null)
615             ? channel.getItems().iterator()
616             : Collections.EMPTY_LIST.iterator();
617     while (iter.hasNext())
618     {
619       itemsModel.addElement(iter.next());
620     }
621     if (itemsModel.getSize() > 0)
622     {
623       listItems.setSelectedIndex(0);
624       _itemsValueChanged(listItems);
625     }
626     setStatusBarText(channel.getURL());
627     _updateAllActions();
628   }
629
630   private void _itemsValueChanged(JList itemList)
631   {
632     Item item = (Item) itemList.getSelectedValue();
633     if (item == null)
634     {
635       if (itemList.getModel().getSize() > 0)
636       {
637         item = (Item) itemList.getModel().getElementAt(0);
638       }
639       if (item == null)
640       {
641         item = new Item();
642       }
643       else
644       {
645         itemList.setSelectedIndex(0);
646       }
647     }
648
649     if (_itemTimer != null)
650     {
651       // prefer a shorter delay than 5s
652       _itemTimer.setDelay(300);
653       _itemTimer.start();
654       _itemTimer.setLastItem(item);
655       final Item lastitem = item;
656       _itemTimer.addActionListener(new ActionListener()
657       {
658
659         @Override
660         public void actionPerformed(ActionEvent e)
661         {
662           saveLastM(lastitem);
663         }
664       });
665     }
666
667     setStatusBarText(item.getLink());
668     textDescription.setBlogText(item);
669     _updateAllActions();
670   }
671
672   public void setStatusBarText(String text)
673   {
674     statusBar.setText(text);
675   }
676
677   private void _updateAllActions()
678   {
679     Iterator iter = _updatableActions.iterator();
680     while (iter.hasNext())
681     {
682       UpdatableAction action = (UpdatableAction) iter.next();
683       action.update(this);
684     }
685   }
686
687   private void _updateToolbarButtons()
688   {
689     Map general = (Map) Main.getPreferences().get("general");
690     if (general == null)
691     {
692       return;
693     }
694
695     Component[] components = toolBar.getComponents();
696     for (int i = 0; i < components.length; i++)
697     {
698       Component component = components[i];
699       if (component instanceof JButton)
700       {
701         JButton button = (JButton) component;
702         if (Boolean.toString(false).equals(general.get("useToolBarText")))
703         {
704           // Remove the text if preferences state no toolbar text
705           button.setText("");
706         }
707         if (Boolean.toString(true).equals(general.get("radioTextBelow")))
708         {
709           button.setVerticalTextPosition(AbstractButton.BOTTOM);
710           button.setHorizontalTextPosition(AbstractButton.CENTER);
711         }
712         else if (Boolean.toString(true)
713                 .equals(general.get("radioTextRight")))
714         {
715           button.setVerticalTextPosition(AbstractButton.CENTER);
716           button.setHorizontalTextPosition(AbstractButton.RIGHT);
717         }
718       }
719     }
720   }
721
722   private void listItems_mouseClicked(MouseEvent e)
723   {
724     if (e.getClickCount() == 2 && e.getModifiersEx() == MouseEvent.NOBUTTON)
725     {
726       Item item = (Item) listItems.getSelectedValue();
727       item.setRead(true);
728       saveLastM(item);
729       if (_itemTimer != null)
730       {
731         _itemTimer.stop();
732       }
733
734       Action action = new LaunchJvBrowserOnItem(listItems);
735       ActionEvent event = new ActionEvent(this,
736               ActionEvent.ACTION_PERFORMED, "LaunchBrowserOnItem");
737       action.actionPerformed(event);
738     }
739   }
740
741   /**
742    * force the news panel to refresh
743    */
744   public void refreshNews()
745   {
746     try
747     {
748       initItems((Channel) _channelModel.getElementAt(0));
749
750     } catch (Exception x)
751     {
752     }
753   }
754
755   /**
756    * @j2sIgnore
757    * @param args
758    */
759   public static void main(String args[])
760   {
761     // this tests the detection of new news based on the last read date stored
762     // in jalview properties
763     Cache.loadProperties(null);
764     Console.initLogger();
765     // test will advance read date each time
766     Calendar today = Calendar.getInstance(),
767             lastread = Calendar.getInstance();
768     lastread.set(1983, 01, 01);
769     while (lastread.before(today))
770     {
771       String formattedDate = Cache.setDateProperty(
772               "JALVIEW_NEWS_RSS_LASTMODIFIED", lastread.getTime());
773       BlogReader me = new BlogReader();
774       System.out.println("Set last date to " + formattedDate);
775       if (me.isNewsNew())
776       {
777         Console.debug("There is news to read.");
778       }
779       else
780       {
781         Console.debug("There is no new news.");
782         me.xf.setTitle("Testing : Last read is " + me.lastDate);
783         me.showNews();
784         me.xf.toFront();
785       }
786       Console.debug("Waiting for closure.");
787       do
788       {
789         try
790         {
791           Thread.sleep(300);
792         } catch (InterruptedException x)
793         {
794         }
795       } while (me.isVisible());
796
797       if (me.isNewsNew())
798       {
799         Console.debug("Still new news after reader displayed.");
800       }
801       if (lastread.getTime().before(me.lastDate))
802       {
803         Console.debug("The news was read.");
804         lastread.setTime(me.lastDate);
805       }
806       else
807       {
808         lastread.add(Calendar.MONTH, 1);
809       }
810
811     }
812   }
813
814   boolean isNewsNew()
815   {
816     return newsnew;
817   }
818 }
819
820 class ChannelsRenderer extends DefaultListCellRenderer
821 {
822   private final static Icon _icon = new ImageIcon(
823           Main.class.getResource("image/ComposeMail16.gif"));
824
825   @Override
826   public Component getListCellRendererComponent(JList list, Object value,
827           int index, boolean isSelected, boolean cellHasFocus)
828   {
829     JLabel component = (JLabel) super.getListCellRendererComponent(list,
830             value, index, isSelected, cellHasFocus);
831     component.setIcon(ChannelsRenderer._icon);
832     if (value instanceof Channel)
833     {
834       Channel channel = (Channel) value;
835       component.setText(MessageManager
836               .formatMessage("label.channel_title_item_count", new String[]
837               { channel.getTitle(), Integer
838                       .valueOf(channel.getUnreadItemCount()).toString() }));
839       component.setToolTipText(channel.getURL());
840     }
841     return component;
842   }
843 }
844
845 class ItemsRenderer extends DefaultListCellRenderer
846 {
847   private final static Icon _icon = new ImageIcon(
848           Main.class.getResource("image/ComposeMail16.gif"));
849
850   @Override
851   public Component getListCellRendererComponent(JList list, Object value,
852           int index, boolean isSelected, boolean cellHasFocus)
853   {
854     JLabel component = (JLabel) super.getListCellRendererComponent(list,
855             value, index, isSelected, cellHasFocus);
856     component.setIcon(ItemsRenderer._icon);
857     if (value instanceof Item)
858     {
859       Item item = (Item) value;
860       if (item.getPublishDate() != null)
861       {
862         component.setText(MessageManager.formatMessage(
863                 "label.blog_item_published_on_date", new String[]
864                 { DateFormat
865                         .getDateInstance(DateFormat.LONG,
866                                 MessageManager.getLocale())
867                         .format(item.getPublishDate()).toString(),
868                     item.getTitle() }));
869       }
870       component.setToolTipText(item.getLink());
871       if (!item.isRead())
872       {
873         component.setFont(component.getFont().deriveFont(Font.BOLD));
874       }
875       else
876       {
877         component.setFont(component.getFont().deriveFont(Font.PLAIN));
878       }
879     }
880     return component;
881   }
882 }