View Javadoc

1   /*
2    * Copyright (c) 2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.aequo.file;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Color;
17  import java.awt.Component;
18  import java.awt.Dimension;
19  import java.awt.Rectangle;
20  import java.awt.event.ActionEvent;
21  import java.awt.event.ActionListener;
22  import java.awt.event.ComponentEvent;
23  import java.awt.event.ComponentListener;
24  import java.awt.event.FocusEvent;
25  import java.awt.event.FocusListener;
26  import java.awt.event.MouseEvent;
27  import java.awt.event.MouseListener;
28  import java.io.File;
29  import java.io.IOException;
30  import java.text.Format;
31  import java.text.SimpleDateFormat;
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import javax.swing.Action;
36  import javax.swing.BorderFactory;
37  import javax.swing.Icon;
38  import javax.swing.JComponent;
39  import javax.swing.JLabel;
40  import javax.swing.JMenu;
41  import javax.swing.JMenuBar;
42  import javax.swing.JMenuItem;
43  import javax.swing.JPanel;
44  import javax.swing.JPopupMenu;
45  import javax.swing.JSeparator;
46  import javax.swing.JSplitPane;
47  import javax.swing.JTable;
48  import javax.swing.JToolBar;
49  import javax.swing.ListSelectionModel;
50  import javax.swing.UIManager;
51  import javax.swing.border.BevelBorder;
52  import javax.swing.event.ListSelectionEvent;
53  import javax.swing.event.ListSelectionListener;
54  import javax.swing.table.DefaultTableCellRenderer;
55  import javax.swing.table.TableColumn;
56  import javax.swing.table.TableColumnModel;
57  
58  import org.abstracthorizon.aequo.CompareEntry;
59  import org.abstracthorizon.aequo.GlobalContext;
60  import org.abstracthorizon.aequo.action.AboutAction;
61  import org.abstracthorizon.aequo.action.CloseWindowAction;
62  import org.abstracthorizon.aequo.file.action.FileBaseAction;
63  import org.abstracthorizon.aequo.file.action.NewSessionAction;
64  import org.abstracthorizon.aequo.file.action.RefreshSubtreeAction;
65  import org.abstracthorizon.aequo.gui.AbstractComparePanel;
66  import org.abstracthorizon.aequo.gui.DropDownBoxComponent;
67  import org.abstracthorizon.aequo.gui.RowFocusBorder;
68  import org.abstracthorizon.aequo.text.TextCompareEntry;
69  import org.abstracthorizon.aequo.text.action.ShowComparisonAction;
70  import org.abstracthorizon.aequo.util.Preferences;
71  import org.abstracthorizon.aequo.util.StringUtils;
72  import org.abstracthorizon.aequo.util.filters.Filter;
73  import org.abstracthorizon.aequo.util.prefs.RecentFileEntry;
74  import org.abstracthorizon.aequo.util.prefs.RecentFilters;
75  
76  /**
77   * Panel that displays two tables over file model.
78   *
79   * @author Daniel Sendula
80   */
81  public class TableFileComparePanel extends AbstractComparePanel<File, FileCompareEntry> {
82  
83      /** Divider pane */
84      protected JSplitPane splitPane;
85  
86      /** Left bottom bar */
87      protected JPanel leftBottomBar;
88      
89      /** Right bottom bar */
90      protected JPanel rightBottomBar;
91  
92      /** Left status label */
93      protected JLabel leftStatus;
94      
95      /** Right status label */
96      protected JLabel rightStatus;
97  
98      /** General status label */
99      protected JLabel bottomStatus;
100 
101     /** File icon */
102     protected Icon fileIcon;
103     
104     /** Directory icon */
105     protected Icon directoryIcon;
106 
107     /** Model */
108     protected FilesModel compareModel;
109     
110     /** Date format to be displayed on the screen */
111     public static final Format dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
112     
113     /**
114      * Constructor
115      * @param context global context
116      * @param compareModel model
117      */
118     public TableFileComparePanel(GlobalContext context, FilesModel compareModel) {
119         super(context, compareModel);
120         this.compareModel = compareModel;
121         this.context = context;
122 
123         directoryIcon    = UIManager.getIcon("FileView.directoryIcon");
124         fileIcon         = UIManager.getIcon("FileView.fileIcon");
125     }
126 
127     /**
128      * Initialises the panel
129      */
130     public void initialise() {
131         splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
132         
133         super.initialise();
134 
135         leftBottomBar = new JPanel(new BorderLayout());
136         rightBottomBar = new JPanel(new BorderLayout());
137 
138         leftStatus = new JLabel(" ");
139         rightStatus = new JLabel(" ");
140 
141         leftStatus.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
142         rightStatus.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
143 
144         leftStatus.setPreferredSize(new Dimension(50, leftStatus.getPreferredSize().height));
145         rightStatus.setPreferredSize(new Dimension(50, rightStatus.getPreferredSize().height));
146 
147         leftBottomBar.add(leftStatus, BorderLayout.CENTER);
148         rightBottomBar.add(rightStatus, BorderLayout.CENTER);
149 
150         bottomLeftPanel.add(leftBottomBar, BorderLayout.CENTER);
151         bottomRightPanel.add(rightBottomBar, BorderLayout.CENTER);
152 
153         bottomStatus = new JLabel(" ");
154         bottomStatus.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
155         add(bottomStatus, BorderLayout.SOUTH);
156 
157         splitPane.setLeftComponent(leftPanel);
158         splitPane.setRightComponent(rightPanel);
159         splitPane.setResizeWeight(0.5d);
160         add(splitPane, BorderLayout.CENTER);
161         splitPane.setDividerLocation(0.5d);
162         
163         compareModel.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
164 
165             public void valueChanged(ListSelectionEvent e) {
166                 if (!e.getValueIsAdjusting()) {
167                     ListSelectionModel selModel = compareModel.getSelectionModel();
168                     if (selModel.isSelectionEmpty()) {
169                         // TODO calcualte all!!!
170                         leftStatus.setText("");
171                         rightStatus.setText("");
172                     } else {
173                         int leftDir = 0;
174                         int rightDir = 0;
175                         int leftFiles = 0;
176                         int rightFiles = 0;
177                         long leftSize = 0;
178                         long rightSize = 0;
179                         
180                         int min = selModel.getMinSelectionIndex();
181                         int max = selModel.getMaxSelectionIndex();
182                         for (int i = min; i <= max; i++) {
183                             if (selModel.isSelectedIndex(i)) {
184                                 FileCompareEntry entry = compareModel.get(i);
185                                 if (entry.hasChildren()) {
186                                     if (entry.getData(0).exists()) {
187                                         leftDir++;
188                                         leftSize = leftSize + entry.getSize(0);
189                                     }
190                                     if (entry.getData(1).exists()) {
191                                         rightDir++;
192                                         rightSize = rightSize + entry.getSize(1);
193                                     }
194                                 } else {
195                                     if (entry.getData(0).exists()) {
196                                         leftFiles++;
197                                         leftSize = leftSize + entry.getSize(0);
198                                     }
199                                     if (entry.getData(1).exists()) {
200                                         rightFiles++;
201                                         rightSize = rightSize + entry.getSize(1);
202                                     }
203                                 }
204                             }
205                         }
206                         // TODO Do calculate it correctly!
207                         leftStatus.setText(leftDir + " dirs " + leftFiles + " files in total of " + StringUtils.normaliseSize(leftSize));
208                         rightStatus.setText(rightDir + " dirs " + rightFiles + " files in total of " + StringUtils.normaliseSize(rightSize));
209                         
210                     }
211                 }
212             }
213         });
214         
215     }
216 
217     /**
218      * Creates the actions for this component
219      */
220     protected void createActions() {
221         createCachedAction("ShowComparisonAction");
222         createCachedAction("RefreshSubtreeAction");
223         createCachedAction("BinaryComparisonAction");
224         createCachedAction("NewSessionAction");
225 
226         createCachedColumnActions("CopyToAction");
227         createCachedColumnActions("MoveToAction");
228         createCachedColumnActions("TouchAction");
229 
230         createCachedColumnActions("RemoveFromViewAction");
231         createCachedColumnActions("DeleteAction");
232         
233         createCachedAction("CollapseAction");
234         createCachedAction("ExpandAction");
235         createCachedAction("ExpandAllAction");
236 
237         createCachedAction("SelectAllAction");
238         createCachedAction("SelectFilesAction");
239     }
240     
241     /**
242      * Sets up compare colours
243      */
244     protected void setUpColors() {
245         foreGrounds.put(CompareEntry.EMPTY, Color.BLACK);
246         backGrounds.put(CompareEntry.EMPTY, new Color(232, 232, 232));
247 
248         foreGrounds.put(CompareEntry.EQUAL, Color.BLACK);
249         backGrounds.put(CompareEntry.EQUAL, Color.WHITE);
250 
251         foreGrounds.put(CompareEntry.LESS, Color.BLACK);
252         backGrounds.put(CompareEntry.LESS, new Color(255, 208, 208));
253 
254         foreGrounds.put(CompareEntry.GREATER, Color.BLACK);
255         backGrounds.put(CompareEntry.GREATER, new Color(208, 208, 255));
256 
257         foreGrounds.put(CompareEntry.DIFFERENT, Color.BLACK);
258         backGrounds.put(CompareEntry.DIFFERENT, new Color(255, 128, 128));
259 
260         foreGrounds.put(TextCompareEntry.SIMILAR, Color.BLACK);
261         backGrounds.put(TextCompareEntry.SIMILAR, new Color(255, 224, 224));
262 
263         foreGrounds.put(TextCompareEntry.CHANGED, Color.BLACK);
264         backGrounds.put(TextCompareEntry.CHANGED, new Color(255, 255, 180));
265     }
266 
267     /**
268      * Creates left component
269      * @return left component
270      */
271     protected JComponent createLeftComponent() {
272         JTable table = new JTable((FilesModel)compareModel);
273 
274         setUpTable(table);
275         FileCellRenderer renderer = new FileCellRenderer(0);
276         table.setDefaultRenderer(FileCompareEntry.class, renderer);
277 
278         return table;
279     }
280 
281     /**
282      * Creates right component
283      * @return right component
284      */
285     protected JComponent createRightComponent() {
286         JTable table = new JTable((FilesModel)compareModel);
287 
288         setUpTable(table);
289         FileCellRenderer renderer = new FileCellRenderer(1);
290         table.setDefaultRenderer(FileCompareEntry.class, renderer);
291 
292         return table;
293     }
294 
295     /**
296      * Sets up the table
297      * @param table table
298      */
299     protected void setUpTable(JTable table) {
300         table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
301         table.setSelectionModel(compareModel.getSelectionModel());
302         table.addFocusListener(tableRowFocusListener);
303         table.setShowGrid(false);
304         table.setShowVerticalLines(false);
305         table.setShowHorizontalLines(false);
306         table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
307 
308         TableColumnModel columnModel = table.getColumnModel();
309         columnModel.setColumnSelectionAllowed(false);
310 
311         TableColumn col0 = columnModel.getColumn(0);
312         col0.setPreferredWidth(-1);
313         col0.setModelIndex(0);
314 
315         TableColumn col1 = columnModel.getColumn(1);
316         int col1Width = table.getFontMetrics(table.getFont()).stringWidth("0000000") + 2;
317         col1.setPreferredWidth(col1Width);
318         col1.setModelIndex(1);
319 
320         TableColumn col2 = columnModel.getColumn(2);
321         int col2Width = table.getFontMetrics(table.getFont()).stringWidth("00/00/0000 00:00:00") + 2;
322         col2.setPreferredWidth(col2Width);
323         col2.setModelIndex(2);
324         
325         table.addComponentListener(new ComponentListener() {
326 
327             boolean first = true;
328             
329             public void componentHidden(ComponentEvent e) { }
330 
331             public void componentMoved(ComponentEvent e) { }
332 
333             public void componentResized(ComponentEvent e) {
334                 if (first) {
335                     JTable table = (JTable)e.getComponent();
336     
337                     TableColumnModel columnModel = table.getColumnModel();
338                     columnModel.setColumnSelectionAllowed(false);
339     
340                     int width = table.getWidth() - table.getInsets().left - table.getInsets().right;
341                     
342                     TableColumn col1 = columnModel.getColumn(1);
343                     int col1Width = table.getFontMetrics(table.getFont()).stringWidth("00000MB") + 2;
344                     col1.setPreferredWidth(col1Width);
345                     col1.setModelIndex(1);
346     
347                     TableColumn col2 = columnModel.getColumn(2);
348                     int col2Width = table.getFontMetrics(table.getFont()).stringWidth("00/00/0000 00:00:00") + 2;
349                     col2.setPreferredWidth(col2Width);
350                     col2.setModelIndex(2);
351 
352                     TableColumn col0 = columnModel.getColumn(0);
353                     col0.setPreferredWidth(width - col1Width - col2Width - 10);
354                     col0.setModelIndex(0);
355     
356                     first = false;
357                 }
358             }
359 
360             public void componentShown(ComponentEvent e) { }
361             
362         });
363     }
364 
365     /**
366      * Updates lists (tables) with popup menus, etc...
367      */
368     protected void createLists() {
369         super.createLists();
370 
371         DoubleClickMouseInputListener doubleClickListener = new DoubleClickMouseInputListener();
372         leftList.addMouseListener(doubleClickListener);
373         rightList.addMouseListener(doubleClickListener);
374         
375 //        leftPopupMenu = leftList.getComponentPopupMenu();
376 //        leftList.setComponentPopupMenu(null);
377 //        
378 //        rightPopupMenu = rightList.getComponentPopupMenu();
379 //        rightList.setComponentPopupMenu(null);
380     }
381 
382     /**
383      * Creates popup menu
384      * @param column column
385      * @return popup menu
386      */
387     protected JPopupMenu createActionMenu(int column) {
388         JPopupMenu menu = super.createActionMenu(column);
389 
390         menu.add(new DefaultActionMenuItem());
391 //        menu.add(actionsCache.get("ShowComparisonAction"));
392         menu.add(new JSeparator());
393         menu.add(actionsCache.get("RefreshSubtreeAction"));
394         menu.add(actionsCache.get("BinaryComparisonAction"));
395         menu.add(actionsCache.get("NewSessionAction"));
396         menu.add(new JSeparator());
397 
398         menu.add(actionsCache.get("CopyToAction"));
399         menu.add(actionsCache.get("MoveToAction"));
400         menu.add(actionsCache.get("TouchAction"));
401         menu.add(new JSeparator());
402 
403 //        menu.add(new CutAction(compareModel, column));
404 //        menu.add(new CopyAction(compareModel, column));
405 //        menu.add(new PasteOverAction(compareModel, column));
406 //        menu.add(new JSeparator());
407 
408         menu.add(actionsCache.get("RemoveFromViewAction"));
409         menu.add(new JSeparator());
410         menu.add(actionsCache.get("DeleteAction"));
411         
412         updateCachedActionsColumn(column);
413         
414         return menu;
415     }
416 
417     /**
418      * Adds elements to menu bar
419      * @param menuBar menu bar
420      */
421     public void createMenu(JMenuBar menuBar) {
422         super.createMenu(menuBar);
423         
424         JMenu fileMenu = obtainMenu("File", menuBar);
425         CloseWindowAction closeAction = new CloseWindowAction();
426         closeAction.setWindow(getEnclosingFrame());
427         JMenuItem closeWindowAction = new JMenuItem(closeAction);
428         fileMenu.add(closeWindowAction, 3);
429 
430         JMenu editMenu = obtainMenu("Edit", menuBar);
431         editMenu.add(actionsCache.get("CopyToAction"));
432         editMenu.add(actionsCache.get("MoveToAction"));
433         editMenu.add(actionsCache.get("TouchAction"));
434         editMenu.addSeparator();
435         editMenu.add(actionsCache.get("SelectAllAction"));
436         editMenu.add(actionsCache.get("SelectFilesAction"));
437         editMenu.addSeparator();
438         editMenu.add(actionsCache.get("RemoveFromViewAction"));
439         editMenu.addSeparator();
440         editMenu.add(actionsCache.get("DeleteAction"));
441         editMenu.addChangeListener(new MenuChangeListener());
442         
443         JMenu viewMenu = obtainMenu("View", menuBar);
444         menuBar.add(viewMenu);
445         viewMenu.add(createAction("RefreshAllAction"));
446         RefreshSubtreeAction refreshSubtreeAction = (RefreshSubtreeAction)createAction("RefreshSubtreeAction");
447         refreshSubtreeAction.setName("Refresh Subtree");
448         viewMenu.add(refreshSubtreeAction);
449         viewMenu.addChangeListener(new MenuChangeListener());
450         
451         JMenu actionMenu = obtainMenu("Action", menuBar);
452         menuBar.add(actionMenu);
453 //        actionMenu.add(actionsCache.get("ShowComparisonAction"));
454         actionMenu.add(new DefaultActionMenuItem());
455         actionMenu.add(actionsCache.get("ExpandAllAction"));
456         actionMenu.addSeparator();
457         actionMenu.add(actionsCache.get("BinaryComparisonAction"));
458         NewSessionAction newSessionAction = (NewSessionAction)createAction("NewSessionAction");
459         newSessionAction.setName("New Session from Element");
460         actionMenu.add(newSessionAction);
461         actionMenu.addChangeListener(new MenuChangeListener());
462 
463 //        JMenu helpMenu = new JMenu("Help");
464 //        menuBar.add(helpMenu);
465 //        
466 //        helpMenu.add(createAction("AboutAction"));
467     }
468 
469     /**
470      * Updates panels adding top tool bar
471      */
472     protected void createPanels() {
473         super.createPanels();
474         
475         Preferences prefs = getGlobalContext().getPreferences();
476 
477         JToolBar topToolBar = new JToolBar();
478 
479         topToolBar.add(createAction("org.abstracthorizon.aequo.action.NewSessionAction"));
480         topToolBar.add(createAction("RefreshAllAction"));
481         List<Filter> filters = RecentFilters.allRecentFilters(prefs);
482         List<String> strings = new ArrayList<String>(filters.size());
483         for (Filter filter : filters) {
484             strings.add(filter.getFilters());
485         }
486         
487         DropDownBoxComponent filtersInput = new DropDownBoxComponent("Filters: ", compareModel.getFilter().getFilters(), strings);
488         filtersInput.addActionListener(new ActionListener() {
489             public void actionPerformed(ActionEvent e) {
490                 DropDownBoxComponent filtersInput = (DropDownBoxComponent)e.getSource();
491                 Filter newFilter = new Filter(filtersInput.getSelected());
492                 Filter oldFilter = compareModel.getFilter();
493                 if (!newFilter.equals(oldFilter)) {
494                     compareModel.setFilter(newFilter);
495                     if (oldFilter.hasBeenAdded(newFilter)) {
496                         compareModel.filterOut();
497                     } else {
498                         // TODO as for refresh
499                         compareModel.refresh();
500                     }
501                     
502                     
503                     Preferences prefs = getGlobalContext().getPreferences();
504                     RecentFileEntry fileEntry = 
505                         new RecentFileEntry(
506                                 compareModel.getData(0).getAbsolutePath(),
507                                 compareModel.getData(1).getAbsolutePath(),
508                                 newFilter.toString());
509                     
510                     if (RecentFilters.writeNew(prefs, newFilter) | RecentFileEntry.writeNew(prefs, fileEntry)) {
511                         try {
512                             prefs.save();
513                         } catch (IOException ex) {
514                             ex.printStackTrace();
515                         }
516                     }
517                     
518                 }                
519             }
520         });
521         topToolBar.addSeparator();
522         topToolBar.add(filtersInput);
523         topToolBar.addSeparator();
524         topToolBar.add(new JSeparator());
525         AboutAction aboutAction = (AboutAction)createAction("AboutAction");
526         aboutAction.setName("<=>");
527         topToolBar.add(aboutAction);
528         add(topToolBar, BorderLayout.PAGE_START);
529         
530         
531         JToolBar leftToolBar = new JToolBar();
532         leftPanel.add(leftToolBar, BorderLayout.NORTH);
533         leftToolBar.add(createAction("ReplaceSideAction", 0));
534         List<String> oldFiles = new ArrayList<String>();
535         
536         int max = RecentFileEntry.getNumberOfRecent(prefs);
537         for (int i = 0; i <= max; i++) {
538             RecentFileEntry entry = RecentFileEntry.read(prefs, i);
539             if (!oldFiles.contains(entry.left)) {
540                 oldFiles.add(entry.left);
541             }
542             if (!oldFiles.contains(entry.right)) {
543                 oldFiles.add(entry.right);
544             }
545         }
546         
547         DropDownBoxComponent leftFileInput = new DropDownBoxComponent(compareModel.getData(0).getAbsolutePath(), oldFiles);
548         leftFileInput.addActionListener(new FileDropdownListener(compareModel, 0));
549         leftToolBar.addSeparator();
550         leftToolBar.add(leftFileInput);
551         leftToolBar.addSeparator();
552 
553         JToolBar rightToolBar = new JToolBar();
554         rightPanel.add(rightToolBar, BorderLayout.NORTH);
555         rightToolBar.add(createAction("ReplaceSideAction", 1));
556 
557         DropDownBoxComponent rightFileInput = new DropDownBoxComponent(compareModel.getData(1).getAbsolutePath(), oldFiles);
558         rightFileInput.addActionListener(new FileDropdownListener(compareModel, 1));
559         rightToolBar.addSeparator();
560         rightToolBar.add(rightFileInput);
561         rightToolBar.addSeparator();
562 
563     }
564 
565     /**
566      * Updates action with file model
567      * @param a action
568      */
569     protected void updateAction(Action a) {
570         super.updateAction(a);
571         if (a instanceof FileBaseAction) {
572             FileBaseAction fileBaseAction = (FileBaseAction)a;
573             fileBaseAction.setFilesModel(compareModel);
574         }
575     }
576     
577     /**
578      * Updates menu items with the values of action elements
579      * @param menu menu to be updated
580      */
581     protected void updateMenu(Component[] components) {
582         for (Component c : components) {
583             if (c instanceof DefaultActionMenuItem) {
584                 DefaultActionMenuItem item = (DefaultActionMenuItem)c;
585                 item.update();
586             }
587         }
588         super.updateMenu(components);
589     }
590     
591     /**
592      * Table cell renderer that renders {@link FileCompareEntry} entries.
593      */
594     protected class FileCellRenderer extends DefaultTableCellRenderer {
595 
596         /** Border for cell that is focused */
597         private final RowFocusBorder focusBorder = new RowFocusBorder();
598 
599         /** Model's column this renderer is working for */
600         protected int modelColumn;
601 
602         /**
603          * Constructor
604          * @param modelColumn model's column
605          */
606         public FileCellRenderer(int modelColumn) {
607             this.modelColumn = modelColumn;
608             setBorder(focusBorder);
609         }
610 
611         public Component getTableCellRendererComponent(JTable table, Object value,
612                 boolean isSelected, boolean hasFocus, int row, int column) {
613 
614             hasFocus = table.hasFocus() && (table.getSelectionModel().getLeadSelectionIndex() == row);
615 
616             int columns = table.getColumnCount();
617 
618             setFont(table.getFont());
619             setEnabled(table.isEnabled());
620 
621             FileCompareEntry entry = (FileCompareEntry)value;
622 
623             File file = entry.getData(modelColumn);
624 
625             byte result = entry.getStatus(modelColumn);
626             boolean fileExists = file.exists();
627             long fileModifiedDate = file.lastModified();
628             long fileSize = entry.getSize(modelColumn);
629             String name = file.getName();
630             
631             if (!fileExists) {
632                 result = CompareEntry.EMPTY;
633             }
634             
635             if (column == 0) {
636                 focusBorder.setLeftMargin(entry.getLevel() * 10);
637 
638                 if (fileExists) {
639                     StringBuffer text = new StringBuffer();
640                     text.append(name);
641                     setText(text.toString());
642                 } else {
643                     setText(null);
644                 }
645 
646                 if (file.isDirectory()) {
647                     setIcon(directoryIcon);
648                 } else if (fileExists) {
649                     setIcon(fileIcon);
650                 } else {
651                     setIcon(null);
652                 }
653                 setHorizontalAlignment(JLabel.LEFT);
654             } else if (column == 1) {
655                 focusBorder.setLeftMargin(0);
656                 if (fileExists) {
657                     String normalised = StringUtils.normaliseSize(fileSize);
658                     setText(normalised);
659                 } else {
660                     setText("");
661                 }
662                 setIcon(null);
663                 setHorizontalAlignment(JLabel.RIGHT);
664                 if (result == CompareEntry.EQUAL) {
665                     long[] sizes = entry.getSizes();
666                     for (int i = 0; i < sizes.length; i++) {
667                         if (i != modelColumn) {
668                             if (sizes[i] != fileSize) {
669                                 result = CompareEntry.SIMILAR;
670                             }
671                         }
672                     }
673                 }
674             } else if (column == 2) {
675                 focusBorder.setLeftMargin(0);
676                 if (fileExists) {
677                     setText(dateFormat.format(fileModifiedDate));
678                 } else {
679                     setText("");
680                 }
681                 setIcon(null);
682                 setHorizontalAlignment(JLabel.LEFT);
683                 
684                 if (result == CompareEntry.EQUAL) {
685                     File[] files = entry.getData();
686                     for (int i = 0; i < files.length; i++) {
687                         if (i != modelColumn) {
688                             if (files[i].lastModified() != fileModifiedDate) {
689                                 result = CompareEntry.SIMILAR;
690                             }
691                         }
692                     }
693                 }
694             } else {
695                 setText("");
696                 setIcon(null);
697             }
698 
699             if (columns == 1) {
700                 focusBorder.setCellType(RowFocusBorder.ONLY_CELL_IN_ROW);
701             } else if (column == 0) {
702                 focusBorder.setCellType(RowFocusBorder.FIRST_CELL_IN_ROW);
703             } else if (column == columns - 1) {
704                 focusBorder.setCellType(RowFocusBorder.LAST_CELL_IN_ROW);
705             } else {
706                 focusBorder.setCellType(RowFocusBorder.MIDDLE_CELL_IN_ROW);
707             }
708             focusBorder.setFocused(hasFocus);
709 
710             Color foreGround = foreGrounds.get(result);
711             Color backGround = backGrounds.get(result);
712             if (foreGround == null) {
713                 throw new IllegalStateException("result = " + result);
714             }
715             if (backGround == null) {
716                 throw new IllegalStateException("result = " + result);
717             }
718 
719             if (isSelected) {
720                 backGround = backGround.darker();
721             }
722 
723             setForeground(foreGround);
724             setBackground(backGround);
725 
726             return this;
727         }
728     }
729 
730     /**
731      * Listener of double click which in turns invokes default event on selected entry
732      */
733     protected class DoubleClickMouseInputListener implements MouseListener {
734 
735         public DoubleClickMouseInputListener() {
736         }
737 
738         public void mouseClicked(MouseEvent e) {
739             if (e.getClickCount() == 2) {
740                 int index = compareModel.getSelectionModel().getLeadSelectionIndex();
741 
742                 if (index >= 0) {
743                     FileCompareEntry entry = (FileCompareEntry)compareModel.get(index);
744                     if (entry.children != null) {
745 
746                         FileCompareEntry nextEntry = null;
747 
748                         if (index < compareModel.getSize() - 1) {
749                             nextEntry = (FileCompareEntry)compareModel.get(index + 1);
750                         }
751 
752                         boolean expand = ((nextEntry == null) || (entry.getLevel() >= nextEntry.getLevel()));
753 
754                         if (expand) {
755                             compareModel.expand(entry);
756                         } else {
757                             compareModel.collapse(entry);
758                         }
759                     } else {
760                         boolean isDir = false;
761                         int i = 0;
762                         File[] files = entry.getData();
763                         while (!isDir && (i < files.length)) {
764                             isDir = files[i].isDirectory();
765                             i++;
766                         }
767                         if (!isDir) {
768                             ShowComparisonAction showComparisonAction = new ShowComparisonAction(entry);
769                             showComparisonAction.setGlobalContext(getGlobalContext());
770                             showComparisonAction.perform(null);
771                         }
772                     }
773                 }
774             }
775         }
776 
777         public void mouseEntered(MouseEvent e) {
778         }
779 
780         public void mouseExited(MouseEvent e) {
781         }
782 
783         public void mousePressed(MouseEvent e) {
784             ListSelectionModel selModel = compareModel.getSelectionModel();
785             if (e.isPopupTrigger()) {
786                 
787                 int index = ((JTable)e.getComponent()).rowAtPoint(e.getPoint());
788                 if (index >= 0) {
789                     if (e.isControlDown()) {
790                         if (!selModel.isSelectedIndex(index)) {
791                             selModel.addSelectionInterval(index, index);
792                         }
793                     } else {
794                         if (!selModel.isSelectedIndex(index)) {
795                             selModel.setSelectionInterval(index, index);
796                         }
797                     }
798                 }
799                 
800                 JPopupMenu popup = leftPopupMenu;
801                 if (e.getComponent() == rightList) {
802                     popup = rightPopupMenu;
803                     updateCachedActionsColumn(1);
804                     if (!rightList.hasFocus()) {
805                         rightList.grabFocus();
806                     }
807                 } else {
808                     updateCachedActionsColumn(0);
809                     if (!leftList.hasFocus()) {
810                         leftList.grabFocus();
811                     }
812                 }
813                 updateMenu(popup.getComponents());
814                 popup.show(e.getComponent(), e.getX(), e.getY());
815             }
816         }
817 
818         public void mouseReleased(MouseEvent e) {
819         }
820     }
821 
822     /** Single instance of table row focus listener */
823     protected static TableRowFocusListener tableRowFocusListener = new TableRowFocusListener();
824 
825     /**
826      * Focus listener that repaints all cells when a cell in a row gets or loses focus
827      */
828     protected static class TableRowFocusListener implements FocusListener {
829 
830         public void focusGained(FocusEvent e) {
831             repaintLeadCell((JTable)e.getComponent());
832         }
833 
834         public void focusLost(FocusEvent e) {
835             repaintLeadCell((JTable)e.getComponent());
836         }
837 
838         private void repaintLeadCell(JTable table) {
839 
840             int row = table.getSelectionModel().getLeadSelectionIndex();
841             if (row >= 0) {
842                 Rectangle firstCell = table.getCellRect(row, 0, false);
843                 Rectangle lastCell = table.getCellRect(row, 2, false);
844                 int height = firstCell.height;
845                 if (lastCell.height > height) {
846                     height = lastCell.height;
847                 }
848                 int width = lastCell.x - firstCell.x + lastCell.width;
849                 Rectangle dirtyRect = new Rectangle(firstCell.x, firstCell.y, width, height);
850                 table.repaint(dirtyRect);
851             }
852         }
853 
854     }
855 
856     /**
857      * Action listener class that updates appropdiate file model column
858      */
859     protected static class FileDropdownListener implements ActionListener {
860         
861         protected int column;
862         
863         protected FilesModel filesModel;
864         
865         public FileDropdownListener(FilesModel filesModel, int column) {
866             this.column = column;
867             this.filesModel = filesModel;
868         }
869         
870         public void actionPerformed(ActionEvent event) {
871             DropDownBoxComponent component = (DropDownBoxComponent)event.getSource();
872             File file = new File(component.getSelected());
873             if (file.exists()) {
874                 filesModel.setData(column, file);
875                 filesModel.refresh();
876             }
877         }
878     }
879 
880     /**
881      * Class that defines default action for Files model
882      */
883     protected class DefaultActionMenuItem extends JMenuItem {
884         
885         public DefaultActionMenuItem() {
886         }
887      
888         protected void update() {
889             int i = compareModel.getSelectionModel().getLeadSelectionIndex();
890             if (i >= 0) {
891                 FileCompareEntry entry = compareModel.get(i);
892                 if (entry.hasChildren()) {
893                     FileCompareEntry child = entry.getChildren()[0];
894                     if (child.getIndex() >= 0) {
895                         setAction(actionsCache.get("CollapseAction"));
896                     } else {
897                         setAction(actionsCache.get("ExpandAction"));
898                     }
899                     return;
900                 }
901             }
902             setAction(actionsCache.get("ShowComparisonAction"));
903             setEnabled(false);
904         }
905     }
906 }