Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/fm/gui/fmContentPanel.java


1   /****************************************************************************
2    * Copyright (c) 2003 Andrew Duka | aduka@users.sourceforge.net
3    * All right reserved.
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *
10   ****************************************************************************/
11  package com.fm.gui;
12  
13  import com.fm.transform.htmlTransformer;
14  import com.fm.transform.fmTransformException;
15  import com.fm.transform.Transformer;
16  import com.fm.rss.rssChannel;
17  import com.fm.rss.rssItem;
18  import com.fm.rss.rssDateHandler;
19  import com.fm.rss.comparator.*;
20  import com.fm.rss.filter.RssItemFilter;
21  import com.fm.rss.filter.rssFilterFactory;
22  import com.fm.update.fmEvent;
23  
24  
25  import javax.swing.*;
26  import javax.swing.Timer;
27  import javax.swing.tree.TreePath;
28  import javax.swing.table.*;
29  import javax.swing.event.ListSelectionListener;
30  import javax.swing.event.ListSelectionEvent;
31  import java.awt.*;
32  import java.awt.event.*;
33  import java.util.*;
34  
35  /**
36   * Contains general channel information and items representation
37   */
38  public class fmContentPanel extends JPanel implements ActionListener
39  {
40      /** Top panel with channel title, creation date, show mode */
41      private JPanel           infoPane;
42      /** Navigation and settings panel */
43      private JPanel           navPane;
44  
45      /** txt/html content pane for the non splitted view */
46      private JEditorPane      htmlContentPane;
47      /** Content panel for splitted view mode */
48      private fmSplitViewPane  splitViewPane;
49  
50      /** Default transformer */
51      private Transformer    transformer;
52  
53      /** Current channel (last channel set with setChannel) */
54      private rssChannel     currentChannel;
55      /** Channel currently being displayed */
56      private rssChannel     activeChannel;
57  
58      /** Lock flag for setChannel method */
59      private boolean setChannelLocked;
60  
61      /**
62      fmHyperlinkRouter
63  
64      /** Browse history vector */
65      private Vector browseHistory;
66      private static int HISTORY_MAX_SIZE = 10;
67      private int    historyPointer;
68  
69      // Browse buttons
70      private JButton nextButton;
71      private JButton prevButton;
72  
73      // Category tree (required for the GUI components synching)
74      private fmCategoryTree categoryTree;
75  
76  
77      // TITLES
78      private JLabel         channelTitle;
79  
80      public static String DEFAULT_GREETING = "<h3>Hello!</h3>For more information please visit " +
81              "<a href='http://feedman.sourceforge.net'>feedMan web site</a>" +
82              "  or open <a href='http://channel/1'>help channel</a>.";
83  
84  
85      Properties runtimeProperties;
86  
87      /**
88       * Default constructor
89       */
90      public fmContentPanel(Properties props,
91                            fmHyperlinkRouter linkRouter,
92                            fmCategoryTree tree)
93      {
94          super();
95  
96          this.setLayout(new BorderLayout());
97  
98          // current runtitme properties
99          runtimeProperties = props;
100 
101         // category tree
102         categoryTree = tree;
103 
104         // building up default transformer
105         final int showMode;
106 
107         if (runtimeProperties.containsKey(fmGuiConstants.DEFAULT_SHOW_FILTER))
108         {
109             showMode = Integer.parseInt(runtimeProperties.getProperty(fmGuiConstants.DEFAULT_SHOW_FILTER));
110         }
111         else
112         {
113             showMode = Integer.parseInt(fmGuiConstants.ALL_SHOW_FILTER);
114             runtimeProperties.put(fmGuiConstants.DEFAULT_SHOW_FILTER, fmGuiConstants.ALL_SHOW_FILTER);
115         }
116 
117         setChannelLocked = false;
118 
119         // init browse history
120         browseHistory = new Vector();
121         historyPointer = -1;
122 
123         // default titles
124         channelTitle = new JLabel("");
125         channelTitle.setFont(fmUITheme.CHANNEL_TITLE_FONT);
126 
127         // INFO PANEL (channel title, date created, etc)
128         infoPane = new JPanel(new BorderLayout());
129         Dimension infoDim = new Dimension(300,40);
130 
131         infoPane.setPreferredSize(infoDim);
132         infoPane.setMinimumSize(infoDim);
133         infoPane.setBackground(fmUITheme.LIGHT_BACKGROUND_COLOR);
134         infoPane.add(channelTitle, BorderLayout.CENTER);
135 
136         // CONTENT VIEW PANELS
137         htmlContentPane = null;
138         splitViewPane = null;
139 
140 
141         // Option panel (will be added to infoPanel)
142         JPanel showOptPanel = new JPanel();
143         showOptPanel.setBackground(fmUITheme.LIGHT_BACKGROUND_COLOR);
144         showOptPanel.add(Box.createHorizontalStrut(15));
145         showOptPanel.add(new JLabel(fmUITheme.getString("label.show")));
146 
147 
148 
149 
150         ///////////////////////////////////////////////////////////////////////
151         // SHOW FILTERS
152         final HashMap showFilters = new HashMap();
153         RssItemFilter f;
154         {
155             // SORTING FILTER
156             f = rssFilterFactory.createSortingFilter(rssFilterFactory.DATE_COMPARATOR);
157             f.setReverseMode(true);
158             showFilters.put(fmGuiConstants.ALL_SHOW_FILTER,f);
159 
160             // SHOW UNREAD FILTER
161             f = rssFilterFactory.createReadFilter();
162             f.setReverseMode(true);
163             showFilters.put(fmGuiConstants.UNREAD_SHOW_FILTER,f);
164 
165             // SHOW LAST 5 FILTER
166             f = rssFilterFactory.createReverseItemCountFilter(5, rssFilterFactory.DATE_COMPARATOR);
167             f.setReverseMode(true);
168             showFilters.put(fmGuiConstants.LAST5_SHOW_FILTER,f);
169 
170             // SHOW LAST 10 FILTER
171             f = rssFilterFactory.createReverseItemCountFilter(10, rssFilterFactory.DATE_COMPARATOR);
172             f.setReverseMode(true);
173             showFilters.put(fmGuiConstants.LAST10_SHOW_FILTER,f);
174         }
175 
176         // DEFAULT TRANSFORMER
177         f = (RssItemFilter) showFilters.get((new Integer(showMode)).toString());
178         transformer = new htmlTransformer(f);
179         transformer.setParamValue(htmlTransformer.CONTENT_FONT,
180                                   fmUITheme.CONTENT_FONT);
181 
182 
183         // SHOW PROPERTIES
184         final JComboBox showCombo = new JComboBox(new Object[]{fmUITheme.getString("label.show.all"),
185                                                                fmUITheme.getString("label.show.new"),
186                                                                fmUITheme.getString("label.show.last5"),
187                                                                fmUITheme.getString("label.show.last10")});
188 
189         ///////////////////////////////////////////////////////////////////////
190         // CHANGE SHOW MODE LISTENER
191         showCombo.addItemListener(new ItemListener() {
192             public void itemStateChanged(ItemEvent e) {
193                 String sItem = (String) showCombo.getSelectedItem();
194                 RssItemFilter f;
195                 if (sItem.equals(fmUITheme.getString("label.show.all"))) {
196                     f = (RssItemFilter) showFilters.get(fmGuiConstants.ALL_SHOW_FILTER);
197                     runtimeProperties.put(fmGuiConstants.DEFAULT_SHOW_FILTER, fmGuiConstants.ALL_SHOW_FILTER);
198                 }
199                 else if (sItem.equals(fmUITheme.getString("label.show.new"))) {
200                     f = (RssItemFilter) showFilters.get(fmGuiConstants.UNREAD_SHOW_FILTER);
201                     runtimeProperties.put(fmGuiConstants.DEFAULT_SHOW_FILTER, fmGuiConstants.UNREAD_SHOW_FILTER);
202                 }
203                 else if (sItem.equals(fmUITheme.getString("label.show.last5"))) {
204                     f = (RssItemFilter) showFilters.get(fmGuiConstants.LAST5_SHOW_FILTER);
205                     runtimeProperties.put(fmGuiConstants.DEFAULT_SHOW_FILTER, fmGuiConstants.LAST5_SHOW_FILTER);
206                 }
207                 else {
208                     f = (RssItemFilter) showFilters.get(fmGuiConstants.LAST10_SHOW_FILTER);
209                     runtimeProperties.put(fmGuiConstants.DEFAULT_SHOW_FILTER, fmGuiConstants.LAST10_SHOW_FILTER);
210                 }
211 
212 
213                 if (transformer != null)
214                     transformer.setItemFilter(f);
215 
216                 if (splitViewPane != null)
217                     splitViewPane.setItemFilter(f);
218 
219                 if (htmlContentPane != null) {
220                     try {
221                         htmlContentPane.setText((String)transformer.transform(activeChannel));
222                     }
223                     catch (fmTransformException te) {
224                         htmlContentPane.setText(fmUITheme.getLocalizedStrings().getString("msg.cant_transform"));
225                     }
226                 }
227             }
228         });
229         showCombo.setSelectedIndex(showMode);
230 
231 
232 
233         // L&F
234         showCombo.setMaximumSize(showCombo.getPreferredSize());
235         showCombo.setAlignmentX(RIGHT_ALIGNMENT);
236 
237         showOptPanel.add(showCombo);
238         infoPane.add(showOptPanel, BorderLayout.EAST);
239         infoPane.setVisible(false);
240         infoPane.setBorder(fmUITheme.FM_THIN_BORDER);
241 
242         add(infoPane, BorderLayout.NORTH);
243 
244         ///////////////////////////////////////////////////////////////////////
245         // CARD PANE: contains two panes with different content views
246         final JPanel cardPane = new JPanel(new CardLayout());
247         splitViewPane = new fmSplitViewPane(transformer, linkRouter);
248         splitViewPane.setBorder(BorderFactory.createEmptyBorder());
249 
250 
251         ///////////////////////////////////////////////////////////////////////
252         // TXT/HTML VEW PANEL
253         htmlContentPane = new JEditorPane("text/html",
254                                            fmContentPanel.DEFAULT_GREETING);
255 
256         htmlContentPane.setEditable(false);
257         htmlContentPane.setAutoscrolls(false);
258         htmlContentPane.addHyperlinkListener(linkRouter);
259 
260         // Setting default view mode if not set before
261         if (runtimeProperties.containsKey(fmGuiConstants.PREFFERED_VIEW_MODE) == false)
262             runtimeProperties.put(fmGuiConstants.PREFFERED_VIEW_MODE,
263                                   fmGuiConstants.SOLID_VIEW_MODE);
264 
265         boolean isSplittedPreffered = runtimeProperties.
266                                       getProperty(fmGuiConstants.PREFFERED_VIEW_MODE).
267                                          equals(fmGuiConstants.SPLITTED_VIEW_MODE);
268 
269         // Order of the view panes depends on preffered view mode
270         if (isSplittedPreffered) {
271             cardPane.add(splitViewPane, "splitted");
272             cardPane.add(new JScrollPane(htmlContentPane),"solid");
273         }
274         else {
275             cardPane.add(new JScrollPane(htmlContentPane),"solid");
276             cardPane.add(splitViewPane, "splitted");
277         }
278 
279         // adding view panes
280         add(cardPane, BorderLayout.CENTER);
281 
282         ///////////////////////////////////////////////////////////////////////
283         // NAVIGATION PANEL
284         navPane = new JPanel();
285         navPane.setLayout(new BoxLayout(navPane,BoxLayout.X_AXIS));
286         navPane.setBackground(fmUITheme.LIGHT_BACKGROUND_COLOR);
287 
288         Dimension navDim = new Dimension(200,35);
289         navPane.setPreferredSize(navDim);
290         navPane.setMinimumSize(navDim);
291 
292         // first gap
293         navPane.add(Box.createHorizontalStrut(15));
294 
295 
296         // HISTORY BUTTONS
297 
298         // LISTENER
299         ActionListener historyButtonListener = new ActionListener() {
300             public void actionPerformed (ActionEvent e) {
301                 if ((JButton)e.getSource() == nextButton)
302                     showNextChannel();
303                 else if ((JButton)e.getSource() == prevButton)
304                     showPreviousChannel();
305             }
306         };
307 
308         prevButton = fmUITheme.getButtonInstance(fmUITheme.NAVIGATION_PREV_ICON,
309                                                      "button.nav_prev.tooltip",
310                                                      fmUITheme.LIGHT_BACKGROUND_COLOR,
311                                                      Component.LEFT_ALIGNMENT);
312         prevButton.addActionListener(historyButtonListener);
313         prevButton.setEnabled(false);
314 
315         navPane.add(prevButton);
316         navPane.add(Box.createHorizontalStrut(5));
317 
318         // GO FORWARD HISTORY BUTTON ACTION
319         nextButton = fmUITheme.getButtonInstance(fmUITheme.NAVIGATION_NEXT_ICON,
320                                                  "button.nav_next.tooltip",
321                                                  fmUITheme.LIGHT_BACKGROUND_COLOR,
322                                                  Component.LEFT_ALIGNMENT);
323         nextButton.addActionListener(historyButtonListener);
324         nextButton.setEnabled(false);
325         navPane.add(nextButton);
326 
327 
328         // horizonatl glue
329         navPane.add(Box.createHorizontalGlue());
330 
331 
332         // CHANGE VIEW BUTTON
333         JButton button;
334         if (isSplittedPreffered) {
335             button = fmUITheme.getButtonInstance(fmUITheme.SPLITTED_VIEW_ICON,
336                                             "button.splitted_view.tooltip",
337                                             fmUITheme.LIGHT_BACKGROUND_COLOR,
338                                             Component.CENTER_ALIGNMENT);
339         }
340         else {
341             button = fmUITheme.getButtonInstance(fmUITheme.SOLID_VIEW_ICON,
342                                             "button.solid_view.tooltip",
343                                             fmUITheme.LIGHT_BACKGROUND_COLOR,
344                                             Component.CENTER_ALIGNMENT);
345         }
346 
347         final JButton prevItemButton = fmUITheme.getButtonInstance(fmUITheme.NAVIGATION_UP_ICON,
348                                                                     "button.nav_up.tooltip",
349                                                                     fmUITheme.LIGHT_BACKGROUND_COLOR,
350                                                                     Component.RIGHT_ALIGNMENT);
351 
352         final JButton nextItemButton = fmUITheme.getButtonInstance(fmUITheme.NAVIGATION_DOWN_ICON,
353                                                                     "button.nav_down.tooltip",
354                                                                     fmUITheme.LIGHT_BACKGROUND_COLOR,
355                                                                     Component.RIGHT_ALIGNMENT);
356 
357         ///////////////////////////////////////////////////////////////////////
358         //ADDING VIEW MODE BUTTON
359         JLabel tLabel = new JLabel(fmUITheme.getString("label.view_mode"));
360         tLabel.setAlignmentX(CENTER_ALIGNMENT);
361         navPane.add(tLabel);
362         navPane.add(button);
363         navPane.add(Box.createHorizontalGlue());
364 
365         // VIEW MODE ACTION LISTENER
366         button.addActionListener(new ActionListener() {
367             public void actionPerformed(ActionEvent e)
368             {
369                 ((CardLayout)cardPane.getLayout()).next(cardPane);
370                 if (splitViewPane.isVisible()) {
371                     ((JButton)e.getSource()).setIcon(fmUITheme.SPLITTED_VIEW_ICON);
372                     runtimeProperties.put(fmGuiConstants.PREFFERED_VIEW_MODE,
373                                           fmGuiConstants.SPLITTED_VIEW_MODE);
374                     nextItemButton.setEnabled(true);
375                     prevItemButton.setEnabled(true);
376                 }
377                 else  {
378                     ((JButton)e.getSource()).setIcon(fmUITheme.SOLID_VIEW_ICON);
379                     runtimeProperties.put(fmGuiConstants.PREFFERED_VIEW_MODE,
380                                           fmGuiConstants.SOLID_VIEW_MODE);
381                     nextItemButton.setEnabled(false);
382                     prevItemButton.setEnabled(false);
383                 }
384             }
385         });
386 
387 
388         ///////////////////////////////////////////////////////////////////////
389         // UP AND DOWN BUTTONS
390         prevItemButton.addActionListener(new ActionListener() {
391             public void actionPerformed(ActionEvent e) {
392                 splitViewPane.showPreviousItem();
393             }
394         });
395         prevItemButton.setEnabled(isSplittedPreffered);
396         navPane.add(prevItemButton);
397         navPane.add(Box.createHorizontalStrut(5));
398 
399 
400         nextItemButton.addActionListener(new ActionListener() {
401             public void actionPerformed(ActionEvent e) {
402                 splitViewPane.showNextItem();
403             }
404         });
405         nextItemButton.setEnabled(isSplittedPreffered);
406         navPane.add(nextItemButton);
407         navPane.add(Box.createHorizontalStrut(15));
408 
409         navPane.setVisible(false);
410         add(navPane, BorderLayout.SOUTH);
411     }
412 
413 
414     /**
415      * Set channel to display
416      *
417      * <p>This method will update browse history and set specified channel
418      * as current channel for further operations only if setChannelLocked flag
419      * is false</p>
420      *
421      * @param channel rssChannel entity
422      */
423     public void setChannel(rssChannel channel)
424     {
425         if (setChannelLocked)
426             return;
427 
428         // SAVING TO HISTORY
429         if (currentChannel != null)
430             addChannelToHistory(currentChannel);
431 
432         if (browseHistory.size() > 0)
433             prevButton.setEnabled(true);
434 
435         currentChannel = channel;
436 
437         showChannel(channel);
438 
439         navPane.setVisible(true);
440         infoPane.setVisible(true);
441     }
442 
443     /**
444      * Show specified channel using content view panels
445      *
446      * @param channel rssChannel instance (if null current activeChannel will be used)
447      */
448     private void showChannel(rssChannel channel)
449     {
450         try
451         {
452             if (channel != null)
453                 activeChannel = channel;
454 
455             splitViewPane.setChannel(activeChannel);
456             htmlContentPane.setText((String)transformer.transform(activeChannel));
457             channelTitle.setText(" " + activeChannel.getTitle());
458             runtimeProperties.put(fmGuiConstants.LAST_OPEN_CHANNEL_ID, (new Integer(channel.getID())).toString());
459         }
460         catch(fmTransformException te)
461         {
462             te.printStackTrace();
463             htmlContentPane.setText(fmUITheme.getLocalizedStrings().getString("msg.cant_transform"));
464         }
465     }
466 
467     /**
468      * Puts specified object into history Vector.
469      * If history size exceeds HISTORY_MAX_SIZE value, the head entry
470      * will be removed and the current value will be inserted into
471      * the tail (FIFO)
472      *
473      * @param ch channel to put into history
474      */
475     public void addChannelToHistory(rssChannel ch)
476     {
477          if (browseHistory.size() >= HISTORY_MAX_SIZE)
478              browseHistory.removeElementAt(0);
479 
480          browseHistory.addElement(ch);
481          historyPointer = browseHistory.size();
482     }
483 
484 
485     /**
486      * Show previous channel in the history
487      *
488      */
489     public void showPreviousChannel()
490     {
491         if (browseHistory.size() == 0)
492             return;
493         else
494         {
495             if (historyPointer > 0)
496                 --historyPointer;
497 
498             if (historyPointer == 0)
499                 prevButton.setEnabled(false);
500 
501             rssChannel ch = (rssChannel) browseHistory.get(historyPointer);
502 
503             if (ch == null)
504                 return;
505 
506             fmTreeNode tNode = categoryTree.findChannelNode(categoryTree.getRootNode(),
507                                                             ch.getID());
508             if (tNode != null)
509             {
510                 setChannelLocked = true;
511                 categoryTree.setSelectionPath(new TreePath(tNode.getPath()));
512                 // Unlocknig setChannel and history
513                 setChannelLocked = false;
514             }
515 
516             showChannel(ch);
517             nextButton.setEnabled(true);
518         }
519     }
520 
521     /**
522      * Show next channel in the history
523      *
524      */
525     public void showNextChannel()
526     {
527         int brSize = browseHistory.size();
528 
529         if (historyPointer < brSize)
530             ++historyPointer;
531 
532         rssChannel ch;
533 
534         if (historyPointer  ==  brSize) {
535             ch = currentChannel;
536             nextButton.setEnabled(false);
537         }
538         else {
539             ch = (rssChannel) browseHistory.get(historyPointer);
540         }
541 
542         if (ch == null) // emtpy history or error
543                 return;
544 
545         fmTreeNode tNode = categoryTree.findChannelNode(categoryTree.getRootNode(),
546                 ch.getID());
547         if (tNode != null)
548         {
549             // locking history because setSelectionPath will cause setChannel and will
550             // change browse history in this way.
551             setChannelLocked = true;
552             categoryTree.setSelectionPath(new TreePath(tNode.getPath()));
553             // Unlocknig setChannel and history
554             setChannelLocked = false;
555         }
556         showChannel(ch);
557         prevButton.setEnabled(true);
558     }
559 
560     /** Show previous channel's item.
561      *
562      * Does nothing if split view pane isn't visible
563      */
564     public void showPreviousItem()
565     {
566         if (splitViewPane.isVisible())
567             splitViewPane.showPreviousItem();
568     }
569 
570     /** Show next channel's item.
571      *
572      * Does nothing if split view pane isn't visible
573      */
574     public void showNextItem()
575     {
576         if (splitViewPane.isVisible())
577             splitViewPane.showNextItem();
578     }
579 
580 
581 
582 
583     /**
584      * Renderer for table header
585      */
586     public class fmHeaderTableHeaderRenderer extends DefaultTableCellRenderer
587     {
588         public fmHeaderTableHeaderRenderer() { super(); }
589 
590         public Component getTableCellRendererComponent(JTable table,
591                                                        Object value,
592                                                        boolean isSelected,
593                                                        boolean hasFocus,
594                                                        int row,
595                                                        int column)
596         {
597             RssItemFilter filter = ((fmSplitViewTableModel) table.getModel()).getFilter();
598             RssItemComparator c = filter.getItemComparator();
599             JButton icon = fmUITheme.getButtonInstance((filter.isInReverseMode()) ? fmUITheme.SORT_UP_ICON : fmUITheme.SORT_DOWN_ICON,
600                                                        null,
601                                                        fmUITheme.LIGHT_BACKGROUND_COLOR,
602                                                        Component.RIGHT_ALIGNMENT);
603 
604             // L&F
605 
606 
607              // DEFAULT VALUE
608             setValue(value);
609             setAlignmentX(LEFT_ALIGNMENT);
610             JPanel a = new JPanel(new BorderLayout());
611             a.add(this, BorderLayout.WEST);
612             a.setBorder(fmUITheme.FM_THIN_BORDER);
613             a.setBackground(fmUITheme.LIGHT_BACKGROUND_COLOR);
614             a.setForeground(fmUITheme.SELECTED_TEXT_FOREGROUND);
615 
616             switch (column) {
617                 case fmGuiConstants.TITLE_COLUMN_POSITION: // TITLE COLUMN
618                     if (c instanceof rssTitleComparator)
619                         a.add(icon,BorderLayout.EAST);
620                     break;
621 
622                 case fmGuiConstants.DATE_COLUMN_POSITION: // DATE COLUMN
623                     if (c instanceof rssDateComparator)
624                         a.add(icon,BorderLayout.EAST);
625                     break;
626 
627                 case fmGuiConstants.AUTHOR_COLUMN_POSITION: // AUTHOR COLUMN
628                     if (c instanceof rssAuthorComparator)
629                         a.add(icon,BorderLayout.EAST);
630                     break;
631 
632                 case fmGuiConstants.SUBJECT_COLUMN_POSITION: // SUBJECT COLUMN
633                     if (c instanceof rssSubjectComparator)
634                         a.add(icon,BorderLayout.EAST);
635                     break;
636             }
637 
638             return a;
639         }
640     }
641 
642     /**
643      * Renderer for table cells
644      */
645     public class fmHeaderTableCellRenderer extends DefaultTableCellRenderer
646     {
647         public fmHeaderTableCellRenderer() { super(); }
648 
649         public Component getTableCellRendererComponent(JTable table,
650                                                        Object value,
651                                                        boolean isSelected,
652                                                        boolean hasFocus,
653                                                        int row,
654                                                        int column)
655         {
656 
657             rssItem item = (rssItem) ((fmSplitViewTableModel) table.getModel()).getObjectAt(row);
658             // VALUE
659             setValue(value);
660             // FONT
661             if (item.isFresh) {
662                 setFont(table.getFont().deriveFont(Font.BOLD));
663             }
664             else {
665                 setFont(table.getFont());
666             }
667 
668             int borderS = Math.round(getFont().getSize() / 4);
669             setBorder(BorderFactory.createEmptyBorder(borderS, borderS, borderS, borderS));
670 
671 
672             // BACKGROUND
673             if (isSelected) {
674                 setBackground(fmUITheme.SELECTION_BACKGROUND_COLOR);
675                 setForeground(fmUITheme.SELECTED_TEXT_FOREGROUND);
676             }
677             else {
678                 setBackground(table.getBackground());
679             }
680 
681             return this;
682         }
683     }
684 
685 
686     /** Extends default JTable
687      *
688      */
689     private class fmHeaderTable extends JTable
690     {
691 
692         fmHeaderTableCellRenderer cellRenderer;
693 
694         /**
695          * Constructor
696          */
697         public fmHeaderTable()
698         {
699             super();
700             cellRenderer = new fmHeaderTableCellRenderer();
701         }
702 
703         /**
704          * Constructor
705          */
706         public fmHeaderTable(fmSplitViewTableModel model)
707         {
708             super(model);
709             cellRenderer = new fmHeaderTableCellRenderer();
710         }
711 
712         /**
713          * Rreturns renderer for the specified cell
714          *
715          * @param row
716          * @param column
717          * @return
718          */
719         public TableCellRenderer getCellRenderer(int row, int column)
720         {
721             if (dataModel instanceof fmSplitViewTableModel)
722             {
723                 // FRESH ITEM
724                 return cellRenderer;
725             }
726 
727             // return default renderer
728             return (TableCellRenderer)defaultRenderersByColumnClass.get(dataModel.getColumnClass(column));
729 
730         }
731     }
732 
733     /**
734      * Represents spiltted view
735      */
736     private class fmSplitViewPane extends JPanel
737     {
738         /** Displays channel posts's titles, dates, authors */
739         //private fmSplitViewTableModel headerTableModel;
740         private fmHeaderTable headerTable;
741         /** Displays post contents                          */
742         private JEditorPane contentPane;
743         /** Default transformer                             */
744         private Transformer transformer;
745 
746         public fmSplitViewPane(Transformer trf, fmHyperlinkRouter router)
747         {
748             super(new CardLayout());
749 
750             transformer = trf;
751 
752             JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
753             splitPane.setBorder(fmUITheme.FM_THIN_BORDER);
754             splitPane.setBackground(fmUITheme.LIGHT_BACKGROUND_COLOR);
755 
756             // CONTENT
757             contentPane = new JEditorPane("text/html", fmContentPanel.DEFAULT_GREETING);
758             contentPane.setEditable(false);
759             contentPane.setBorder(BorderFactory.createEmptyBorder());
760 
761             // Hyperlink router
762             contentPane.addHyperlinkListener(router);
763 
764             // HEADERS
765             fmSplitViewTableModel headerTableModel = new fmSplitViewTableModel(null,
766                                                                                transformer.getItemFilter());
767             headerTable = new fmHeaderTable(headerTableModel);
768             headerTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
769             headerTable.setAutoscrolls(true);
770             headerTable.getTableHeader().setBackground(fmUITheme.LIGHT_BACKGROUND_COLOR);
771 
772             ////////////////////////////////////////////////////////////
773             // FRESHNESS TIMER
774             final Timer freshnessTimer = new Timer(2000, new ActionListener(){
775                 public void actionPerformed(ActionEvent e) {
776                     int selected = headerTable.getSelectedRow();
777                     if (selected > -1) {
778                         rssItem tI = (rssItem) ((fmSplitViewTableModel) headerTable.getModel()).getObjectAt(selected);
779                         if (tI != null) {
780                             tI.isFresh = false;
781                             ((fmSplitViewTableModel) headerTable.getModel()).fireTableRowsUpdated(selected,selected);
782                             categoryTree.repaint();
783                         }
784                     }
785                     ((Timer)e.getSource()).stop();
786                 }
787             });
788             ///////////////////////////////////////////////////////////////////
789 
790             ///////////////////////////////////////////////////////////////////
791             // SELECTION LISTENER
792             headerTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
793                 public void valueChanged(ListSelectionEvent e) {
794                     int selected = headerTable.getSelectedRow();
795 
796                     // stopping previous timer run, which belongs to another item
797                     freshnessTimer.stop();
798 
799                     fmSplitViewTableModel tModel = ((fmSplitViewTableModel)headerTable.getModel());
800                     if (selected > -1) {
801                         showItem((rssItem) tModel.getObjectAt(selected));
802                     }
803                     else if (headerTable.getRowCount() > 0) { //setting to first item if channel has been changed
804                         headerTable.getSelectionModel().setSelectionInterval(0,0);
805                     }
806                     freshnessTimer.start();
807                 }
808             });
809 
810             // L&F
811             headerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
812             headerTable.setShowGrid(false);
813             headerTable.setSelectionBackground(fmUITheme.SELECTION_BACKGROUND_COLOR);
814             headerTable.setSelectionForeground(fmUITheme.BLACK_COLOR);
815 
816             ////////////////////////////////////////////////////////////////////////
817             // SCROLL PANE FOR HEADER TABLE
818             JScrollPane headerScroll = new JScrollPane(headerTable);
819             headerScroll.getViewport().setBackground(fmUITheme.WHITE_COLOR);
820 
821             ////////////////////////////////////////////////////////////////////////
822             // SETTING UP COLUMN WIDTHS
823             Dimension tDim = headerScroll.getPreferredSize();
824             int fCWidth = (int) Math.round(tDim.width / 2.2); //
825             int sCWidth = (int) Math.round(tDim.width / 3.3); // 30%
826             // todo remove hardcoded staff
827             headerTable.getColumnModel().getColumn(0).setPreferredWidth(fCWidth);
828             headerTable.getColumnModel().getColumn(1).setPreferredWidth(sCWidth);
829 
830 
831 
832             ////////////////////////////////////////////////////////////////////////
833             // MOUSE LISTENER FOR TABLE COLUMN HEADERS
834             MouseAdapter headerMouseListener = new MouseAdapter() {
835                 public void mouseClicked(MouseEvent e) {
836                     TableColumnModel columnModel = headerTable.getColumnModel();
837                     int column = headerTable.convertColumnIndexToModel(columnModel.getColumnIndexAtX(e.getX()));
838                     if (column != -1) {
839                         ((fmSplitViewTableModel) headerTable.getModel()).sortByColumn(column);
840                     }
841                 }
842             };
843 
844             headerTable.getTableHeader().addMouseListener(headerMouseListener);
845             headerTable.getTableHeader().setDefaultRenderer(new fmHeaderTableHeaderRenderer());
846 
847             splitPane.setTopComponent(headerScroll);
848             splitPane.setBottomComponent(new JScrollPane(contentPane));
849             splitPane.setDividerLocation(200); // Default divider location
850             add(splitPane, "");
851         }
852 
853         /**
854          * Sets channel for display
855          *
856          * @param ch
857          */
858         public void setChannel(rssChannel ch)
859         {
860             headerTable.getSelectionModel().clearSelection();
861             ((fmSplitViewTableModel)headerTable.getModel()).setChannel(ch);
862 
863             if (ch.getItemVector().size() > 0)  {
864                 headerTable.getSelectionModel().setSelectionInterval(0,0); //auto selecting first item
865             }
866             else {
867                 contentPane.setText("");
868             }
869             repaint();
870         }
871 
872         /**
873          * Set current item filter and update accordingly
874          *
875          * @param f
876          */
877         public void setItemFilter(RssItemFilter f) {
878             ((fmSplitViewTableModel) headerTable.getModel()).setFilter(f);
879         }
880 
881         /**
882          * Transform and display specified channel item
883          */
884         public void showItem(rssItem item) {
885             try {
886                 contentPane.setText((String) transformer.transform(item));
887             }
888             catch (fmTransformException fme) {
889                 fme.printStackTrace();
890                 contentPane.setText(fmUITheme.getLocalizedStrings().getString("msg.cant_transform"));
891             }
892         }
893 
894         /**
895          * Show next channel item.
896          * Note that this implementation does looped displaying:
897          * if current item is last the first item will be shown instead of nothing
898          */
899         public void showNextItem() {
900             int selected = headerTable.getSelectedRow();
901             if (selected < (headerTable.getRowCount() - 1))
902                 ++selected;
903             else
904                 selected = 0;
905 
906             headerTable.setRowSelectionInterval(selected,selected);
907             headerTable.scrollRectToVisible(headerTable.getCellRect(selected,0,true));
908             showItem((rssItem) ((fmSplitViewTableModel)headerTable.getModel()).
909                                 getObjectAt(selected));
910         }
911 
912         /**
913          * Show previous channel item.
914          * Note that this implementation does looped displaying:
915          * if current item is last the first item will be shown instead of nothing
916          */
917         public void showPreviousItem() {
918             int selected = headerTable.getSelectedRow();
919             if (selected > 0)
920                 --selected;
921             else
922                 selected = (headerTable.getRowCount() - 1);
923 
924             headerTable.setRowSelectionInterval(selected,selected);
925             headerTable.scrollRectToVisible(headerTable.getCellRect(selected,0,true));
926             showItem((rssItem) ((fmSplitViewTableModel)headerTable.getModel()).
927                                 getObjectAt(selected));
928         }
929 
930         /**
931          * Overrides parent's method
932          */
933         public void performLookAndFeelUpdate() {
934             headerTable.setFont(fmUITheme.HEADERS_FONT);
935             contentPane.setFont(fmUITheme.CONTENT_FONT);
936             repaint();
937         }
938     }
939 
940     /**
941      * Updates L&F settings using runtime properties and fmUITheme settings
942      */
943     public void performLookAndFeelUpdate() {
944         transformer.setParamValue(htmlTransformer.CONTENT_FONT,
945                                   fmUITheme.CONTENT_FONT);
946         splitViewPane.performLookAndFeelUpdate();
947         showChannel(activeChannel); // regenerates view panel contents
948     }
949 
950 
951     /**
952      * Action listener's methods
953      */
954     public void actionPerformed(ActionEvent e)
955     {
956         if (e.getActionCommand().equals(fmEvent.UPDATE_FINISHED))
957         {
958             rssChannel ch = (rssChannel)e.getSource();
959 
960             if (((fmEvent)e).getStatus() == fmEvent.SUCCESS_STATUS)
961             {
962                 if (ch.getID() == activeChannel.getID())
963                     showChannel(ch);
964 
965             }
966         }
967 
968     }
969 
970 }